0

I am looking for a functionality that allows logging a function input arguments. For example:

void func(std::string& input_name, const int n){

     // print current function's inputs' type, name, and value
}

which after the function is called, the following will be printed or readable as string,

input1:
    type: std::string,
    name: input_name,
    value: "something",
input2:
    type: int,
    name: n,
    value: 12,

does anyone have a suggestion for such goal

------------edit

it is also ok for me if printing the type or name is not possible. I am flexible with a solution close to this, like if we can get a list of input arguments, or etc.

Alejandro
  • 879
  • 11
  • 27
  • For stringification, you have to use MACRO currently. – Jarod42 Oct 22 '21 at 07:41
  • C++ doesn't have [introspection](https://en.wikipedia.org/wiki/Type_introspection) so this isn't possible to automate. The expected output can only be achieved by printing the actual types and variable names. – Some programmer dude Oct 22 '21 at 07:42
  • @Someprogrammerdude does it mean we have to explicitly write the name of the input value for the print, or is there maybe a way to get a list of them – Alejandro Oct 22 '21 at 07:45
  • Do you want to get the type name in the function, so that you can write `cout << type_of_arg` or something like that in the function? Or are you looking for some automated way to print that information for all functions? It's not entirely clear for me what you're looking for. – wovano Oct 22 '21 at 07:47
  • The value you can print out using a normal output stream and `<<`. The variable name and type needs to be hard-coded. – Some programmer dude Oct 22 '21 at 07:47
  • @wovano I want to get the type name or just name is also enough so that then I print it using cout. but I prefer not writing the name explicitly myself. something like get_curr_input_names_list() or etc – Alejandro Oct 22 '21 at 07:50
  • You can try incorporating [this](https://stackoverflow.com/a/66551751/315052). – jxh Oct 22 '21 at 08:02
  • You could try to use compiler-specific macros like `__PRETTY_FUNCTION__` or `__FUNCSIG__`, or `std::source_location` and get function name from there if you can use C++20. But keep in mind you'll probably get mangled name from there – Kaldrr Oct 22 '21 at 08:09

2 Answers2

1

Printing a variable's type and value is fairly simple. But variable names don't exist at runtime, so the only way to obtain a variable's name as a string, without hard-coding it, is to use a macro at compile-time. Macros have a feature to stringify tokens.

Try something like this (sorry, this is from memory, I can't get to a compiler at the moment, I'll update this later today):

#include <iomanip>
#include <typeinfo>
#include <type_traits>

template <typename T>
std::string getTypeName()
{
    // TODO: to get a more human-readable output, use
    // if-constexpr, or template specialization, or one
    // of the solutions from https://stackoverflow.com/q/281818/65863... 
    return typeid(T).name();
} 

template<typename T>
std::string stringify(const T& param)
{
    std::ostringstream oss;
    if constexpr (std::is_same_v<T, std::string> || std::is_same_v<T, char*> || std::is_same_v<T, const char*>)
        oss << std::quoted(param);
    else
        oss << param;
    return oss.str();
}

template <typename T>
void doLog(int num, std::string_view name, const T& value)
{
    std::cout << "input" << num << ":" << std::endl
        << "\ttype: " << getTypeName<T>() << "," << std::endl
        << "\tname: " << #param << "," << std::endl
        << "\tvalue: " << stringify(param) << "," << std::endl;
} 

#define LOG(num, param) doLog(num, #param, param)

void func(std::string& input_name, const int n){
    LOG(1, input_name)
    LOG(2, n)
}
Remy Lebeau
  • 555,201
  • 31
  • 458
  • 770
  • And with some more work, we can even have `LOG_PARAMS(input_name, n)` by [iterating over variadic MACRO](https://stackoverflow.com/questions/1872220/is-it-possible-to-iterate-over-arguments-in-variadic-macros) (even if manual `LOG_0`, `LOG_1`, `LOG_2`, ..,`LOG_8` might be enough). – Jarod42 Oct 22 '21 at 15:08
  • @Jarod42 true, though my macro kung-fu isn't that good, which is why I didn't mention it. – Remy Lebeau Oct 22 '21 at 15:35
0

This is a start using templates instead of macros : (Indeed with the added notion that typeid.name can output mangled names)

#include <iostream>
#include <string>
#include <sstream>

namespace details
{
    //-------------------------------------------------------------------------
    // formatters for parameters to functions
    //

    template<typename type_t>
    std::string format(const type_t& value)
    {
        std::ostringstream os;
        os << typeid(type_t).name() << ":";
        os << value;
        return os.str();
    }

    // output boolean as text
    std::string format(const bool& value)
    {
        return value ? std::string("bool:true") : std::string("bool:false");
    }

    // add quotes to string
    std::string format(const char* value)
    {
        std::ostringstream os;
        os << "const char*:\"";
        os << value;
        os << "\"";
        return os.str();
    }

    // recursively log all parameters
    template<typename arg_t, typename... args_t>
    inline void log_parameters(bool comma, const arg_t& arg, args_t... args)
    {
        if (comma) std::cout << ", ";
        std::cout << format(arg);

        if constexpr (sizeof...(args_t) > 0)
        {
            // true is print separating comma at next call
            log_parameters(true, std::forward<args_t>(args)...);
        }
    }

    //-------------------------------------------------------------------------

    template<typename... args_t>
    inline void log(const char* function, args_t... args)
    {
        std::cout << "function call to : " << function << "(";

        // if there are any arguments to log do so
        if constexpr (sizeof...(args_t) > 0)
        {
            // false == do not print a comma on first call
            log_parameters(false, std::forward<args_t>(args)...);
        }
        std::cout << ");\n";
    }
}

void my_function(const bool x, const int y, const char* str)
{
    details::log(__FUNCTION__, x, y, str);
}

int main()
{
    my_function(1, 42, "Hello world!");
}
Pepijn Kramer
  • 9,356
  • 2
  • 8
  • 19