Search code examples
c++macrosvariadic-macros

Variadic Macro: cannot pass objects of non-trivially-copyable type through '...'


I am trying to write a macro for logging mechanism. I wrote a variadic macro but it does not work with std::string. The code looks like the following:

#include <stdio.h>
#include <string>


#define LOG_NOTE(m, ...) printf(m, ##__VA_ARGS__)

int main()
{
    std::string foo = "random string";
    int bar = 5;
    LOG_NOTE("%s %d %s", "Hello World", bar, foo);

    return 0;
}

If I would call the macro like following, I would not get any error.

LOG_NOTE("%s %d %s", "Hello World", bar, "random string");

Compiler Output:

In function 'int main()': 5:49: error: cannot pass objects of non-trivially-copyable type 'std::string {aka class std::basic_string}' through '...' 11:5: note: in expansion of macro 'LOG_NOTE'


Solution

  • I wrote a variadic macro

    Don't. Use a variadic template function.

    The actual problem you have is that you're trying to pass a C++ object (std::string) through a C API (printf). This is not possible.

    You'd need some mechanism for conversion, for example:

    #include <stdio.h>
    #include <string>
    
    template<class T>
    decltype(auto) convert_for_log_note(T const& x)
    {
        return x;
    }
    
    decltype(auto) convert_for_log_note(std::string const& x)
    {
        return x.c_str();
    }
    
    
    template<class...Args> 
    void LOG_NOTE(const char* format, Args&&...args)
    {
        printf(format, convert_for_log_note(args)...);
    }
    
    int main()
    {
        std::string foo = "random string";
        int bar = 5;
        LOG_NOTE("%s %d %s\n", "Hello World", bar, foo);
    
        return 0;
    }
    

    Example output:

    Hello World 5 random string
    

    http://coliru.stacked-crooked.com/a/beb3431114833860

    Update:

    For C++11 you'll need to spell out the return types by hand:

    #include <stdio.h>
    #include <string>
    
    template<class T>
    T const& convert_for_log_note(T const& x)
    {
        return x;
    }
    
    const char* convert_for_log_note(std::string const& x)
    {
        return x.c_str();
    }
    
    
    template<class...Args> 
    void LOG_NOTE(const char* format, Args&&...args)
    {
        printf(format, convert_for_log_note(args)...);
    }
    
    int main()
    {
        std::string foo = "random string";
        int bar = 5;
        LOG_NOTE("%s %d %s\n", "Hello World", bar, foo);
    
        return 0;
    }