Search code examples
c++parameter-passingc++17function-callintercept

Intercept the function call to add a argument before an if


I made a console.log function to mimic the javascript console.log function. The problem i have is to line break after all argumets because i'm unable to detect when is a single argument call or is the last argument being called due to function overload.

The solution i thougth off is to add a new argument on functions from the header file.

So when i write in main.cpp:

console.log("hello");
console.log("bye");

i want you to help me to add a new argument before any processing from the header file getting:

console.log("hello", "");
console.log("bye", "");

The full code is here. https://github.com/StringManolo/cppjs/blob/main/console/log.h#L17

Here is the minimal reproducible example as you requested: main.cpp

#include "./log.h"

int main() {

  /* Each console.log call should add a line break */
  console.log("Line1");
  console.log("Line2");
  console.log("Line3");

  /* Like Here */
  console.log("Line1", "");
  console.log("Line2", "");
  console.log("Line3", "");

  /* Then i can do: */
  console.log("Welcome.");
  console.log("7 * 8 = ", 7 * 8);
  console.log("Bye!");

  std::vector<std::string> example = {"a", "b", "c", "d", "e"};
  console.log(example);

  std::any numberOrString = 1337;
  console.log(numberOrString);

  numberOrString = (std::string) "String";
  console.log(numberOrString);

  /* stdout :
  $ ./test
  Line1Line2Line3Line1
  Line2
  Line3
  Welcome.7 * 8 = 56
  Bye!a, b, c, d, e
  1337
  String
  */

  /* Desired output:
  Line1
  Line2
  Line3
  Line1
  Line2
  Line3
  Welcome.
  7 * 8 = 56
  Bye!
  a, b, c, d, e
  1337
  String
  */

  return 0;
}

log.h

#pragma once

#include <iostream>
#include <utility>
#include <any>
#include <vector>
#include <string>

struct {
  std::any aux;

  template<typename T, typename...Args>
  static void log(T&& t, Args&&... args) {

    log(t);
    log(std::forward<Args>(args)...);
    if(sizeof...(args) > 1) {

    } else {
      std::cout << "\n";
    }

  }

  static void log(int arg) {
    std::cout << arg;
  }

  static void log(const char* arg) {
    std::cout << arg;
  }

  static void log(std::vector<std::string> arg) {
    for(int i = 0; i < arg.size(); ++i) {
      if (i + 1 == arg.size()) {
        std::cout << arg[i] << std::endl;
      } else {
        std::cout << arg[i] << ", ";
      }
    }
  }

  template<typename T>
  static void log(T&& t) {
    if (typeid(t).name() == typeid(aux).name()) {
      try {
        std::any_cast<std::string>(t);
        std::cout /* ANY */<< std::any_cast<std::string>(t) << std::endl;
      } catch (const std::bad_any_cast& a) {
        //std::cout << a.what() << '\n';
        try {
          std::any_cast<int>(t);
          std::cout /* ANY */<< std::any_cast<int>(t) << std::endl;
        } catch (const std::bad_any_cast& b) {
          std::cout << a.what() << "\n";
        }
      }

    } else {
      std::cout << "THE TYPE (" << typeid(t).name() << ") NOT OVERLOADED" << std::endl;
    }
  }
} console;

I need more than 1 argument in console.log() call to trigger the line break.

How can i intercept the call to push an argument before the if is evaluated?

Command to compile:

g++ -o test main.cpp -std=c++17

Comand to compile full code:

g++ -o program main.cpp -lcurl -std=c++17

Solution

  • You should probably use different name:

    template <typename ...Ts>
    static void log(Ts&&... ts)
    {
        (do_log(ts), ...);
        std::cout << "\n";
    }
    
    // private:
    template <typename T>
    static void do_log(const T& arg) {
        std::cout << arg;
    }
    
    template <typename T>
    static void do_log(const std::vector<T>& v)
    {
        std::cout << '{';
    
        const char* sep = "";
        for (const auto& e : v) {
            std::cout << sep;
            do_log(e);
            sep = ", ";
        }
        std::cout << '}';
    }
    
    template <typename T>
    static bool try_log_any(const std::any& arg) {
        if (auto* p = std::any_cast<T>(&arg)) {
            do_log(*p);
            return true;
        }
        return false;
    }
    
    template <typename... Ts>
    static bool try_log_any_from(const std::any& arg) {
        if ((try_log_any<Ts>(arg) || ...)) {
            return true;
        }
        std::cout << "unsupported any type" << std::endl;
        return false;
    }
    
    static void do_log(const std::any& arg) {
        try_log_any_from<std::string, int, unsigned, char, float, double /*..*/>(arg);
    }
    

    Demo