Variadic 宏:不能通过“...”传递非平凡可复制类型的对象
Variadic Macro: cannot pass objects of non-trivially-copyable type through '...'
我正在尝试为日志记录机制编写一个宏。我写了一个可变参数宏,但它不适用于 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");
编译器输出:
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'
这里的问题不是可变参数宏,而是对 printf
的调用。看看 documentation:格式说明符 "%s"
对应于 char*
,而不是 std::string
。 printf
只能处理原始内置类型。您可以将调用更改为
LOG_NOTE("%s %d %s", "Hello World", bar, foo.c_str());
解决这个问题。
I wrote a variadic macro
不要。使用可变模板函数。
您遇到的实际问题是您试图通过 C API (printf
) 传递 C++ 对象 (std::string
)。这是不可能的。
您需要某种转换机制,例如:
#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
更新:
对于 C++11,您需要手动拼出 return 类型:
#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;
}
您不能将对象传递给 printf
,因此您当前必须使用
LOG_NOTE("%s %d %s", "Hello World", bar, foo.c_str());
如果你不需要格式化,只是写每个参数用 space 分隔,你可以简单地使用可变参数模板而不是 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);
}
我无法获得@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;
}
inline
关键字对于 Arduino C++ 编译器的上述解决方案是必需的,而其他 g++ 编译器不需要它(无论如何我已经尝试过)。如果没有此关键字,Arduino 代码会编译,但链接器会抱怨多个定义。
我正在尝试为日志记录机制编写一个宏。我写了一个可变参数宏,但它不适用于 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");
编译器输出:
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'
这里的问题不是可变参数宏,而是对 printf
的调用。看看 documentation:格式说明符 "%s"
对应于 char*
,而不是 std::string
。 printf
只能处理原始内置类型。您可以将调用更改为
LOG_NOTE("%s %d %s", "Hello World", bar, foo.c_str());
解决这个问题。
I wrote a variadic macro
不要。使用可变模板函数。
您遇到的实际问题是您试图通过 C API (printf
) 传递 C++ 对象 (std::string
)。这是不可能的。
您需要某种转换机制,例如:
#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
更新:
对于 C++11,您需要手动拼出 return 类型:
#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;
}
您不能将对象传递给 printf
,因此您当前必须使用
LOG_NOTE("%s %d %s", "Hello World", bar, foo.c_str());
如果你不需要格式化,只是写每个参数用 space 分隔,你可以简单地使用可变参数模板而不是 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);
}
我无法获得@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;
}
inline
关键字对于 Arduino C++ 编译器的上述解决方案是必需的,而其他 g++ 编译器不需要它(无论如何我已经尝试过)。如果没有此关键字,Arduino 代码会编译,但链接器会抱怨多个定义。