可变参数宏:不能通过'...'传递非平凡复制类型的对象

时间:2018-08-15 09:44:49

标签: c++ macros variadic-macros

我正在尝试为日志记录机制编写宏。我编写了一个可变参数宏,但不适用于std::string。该代码如下所示:

#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;
}

如果我按如下所示调用宏,则不会出现任何错误。

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

编译器输出:

  

在函数'int main()'中:5:49:错误:无法传递以下对象   非平凡类型'std :: string {aka class   std :: basic_string}'到'...'11:5:注意:扩展   宏“ LOG_NOTE”

4 个答案:

答案 0 :(得分:3)

这里的问题不是可变参数宏,而是对printf的调用。看一下documentation:格式说明符"%s"对应于char*,而不是std::stringprintf仅能处理原始的内置类型。您可以将调用更改为

LOG_NOTE("%s %d %s", "Hello World", bar, foo.c_str());

要解决此问题。

答案 1 :(得分:3)

您无法将对象传递给printf,因此您当前必须使用

LOG_NOTE("%s %d %s", "Hello World", bar, foo.c_str());

如果不需要格式化,只写每个用空格隔开的参数,则可以简单地使用可变参数模板而不是MACRO:

template <typename ... Ts>
void LOG_NOTE(const Ts&...args)
{
    const char* sep = "";
    (((std::cout << sep << args), sep = " "), ...); // C++17 folding expression
    // C++11/C++14 version are more verbose:
    // int dummy[] = {0, ((std::cout << sep << args), (sep = " "), 0)...};
    // static_cast<void>(dummy); // avoid warning for unused variable
}

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

Demo

答案 2 :(得分:2)

  

我写了一个可变参数宏

不要。使用可变参数模板功能。

您遇到的实际问题是,您试图通过C API(std::string)传递C ++对象(printf)。这是不可能的。

您需要某种转换机制,例如:

#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;
}

示例输出:

Hello World 5 random string

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

答案 3 :(得分:0)

我无法获得@richardhodges好的解决方案,无法在我尝试过的任何C ++ 11编译器中工作。但是,以下方法适用于gcc -std=c++11

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

template<class T>
T convert_for_log_note(T const& x)
{
    return x;
}

inline 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;
}

在上述解决方案中,对于Arduino C ++编译器,关键字inline是必需的,而其他g ++编译器则不需要(无论如何,我已经尝试过了)。如果没有此关键字,则将编译Arduino代码,但是链接器会抱怨多个定义。