在 C++11 中字符串化变量名的替代方法
Alternatives to stringifying the variable name in C++11
在我的代码中,我重复了这个表达式:
T foo;
do_sth(foo, "foo");
我正在考虑将变量名称字符串化,如下所示:
#define VARNAME(Var) (#Var)
void do_sth_new(T foo) { do_sth(foo, VARNAME(foo)); };
T foo;
do_sth_new(foo);
这是好的做法吗? C++11 有更好的选择吗?
遗憾的是,没有。这个问题仍然没有解决方案(甚至在 C++17 中也没有)。一旦将静态反射添加到 C++ 中,可能会有一些东西。但是暂时你被宏困住了。
如您所示,它不起作用,因为 VARNAME(foo)
将始终是 "foo"
(因为这是参数的名称)。你必须把 do_sth_new
本身写成宏:
#define do_sth_new(_foo) \
do { do_sth(_foo, #_foo); } while (false)
只有这样才会:
T bar;
do_sth_new(bar);
生成 "bar"
.
不,除了使用预处理器之外别无选择,因为这是词法级别的操作。您需要对语言中的 AST 进行 LISP 级修改以获得更好的解决方案,这不太可能发生。
没有真正的方法可以避免宏进行字符串化。
您可以做的是以更面向 C++ 对象的方式装饰它,特别是如果您想要执行多个采用对象及其 var 名称的不同方法,并且如果这是一个调试功能,您可能想要在生产中禁用。
所以我建议你声明一个模板 class DebugContextWrap,这种类型的对象(或 const ref)可以作为单个参数传递给函数,而不是有 2 个参数。
一个缺点是,如果您的函数代码实际上想要访问实际值,那么您必须像对迭代器所做的那样通过 operator ->
或 data()
执行间接寻址。
然后您可以编写一个生成 DebugContextWrap 实例的宏 - 例如:
template class FooType
class DebugContextWrap
{
FooType& fooVal;
const char* debugName;
const char* debug__FILE__val;
const int debug__LINE__val;
public:
DebugContextWrap(FooType& fooVal,
const char* debugName, const char* debug__FILE__val, const int debug__LINE__val)
{ ... }
DebugContextWrap(FooType& fooVal) // implicit when caller doesn't use DEBUG_WRAP
{ ... }
FooType* operator ->()const
{ return &foo; }
FooType& operator *()const
{ return foo; }
FooType& Data()const
{ return foo; }
const char* DebugName()const
{ return debugName; }
};
#define DEBUG_WRAP(foo) \
DebugContextWrap<decltype foo>(foo, #foo, __FILE__, __LINE__)
void do_sth(const DebugContextWrap<FooType>& foo);
do_sth(DEBUG_WRAP(foovar));
在我的代码中,我重复了这个表达式:
T foo;
do_sth(foo, "foo");
我正在考虑将变量名称字符串化,如下所示:
#define VARNAME(Var) (#Var)
void do_sth_new(T foo) { do_sth(foo, VARNAME(foo)); };
T foo;
do_sth_new(foo);
这是好的做法吗? C++11 有更好的选择吗?
遗憾的是,没有。这个问题仍然没有解决方案(甚至在 C++17 中也没有)。一旦将静态反射添加到 C++ 中,可能会有一些东西。但是暂时你被宏困住了。
如您所示,它不起作用,因为 VARNAME(foo)
将始终是 "foo"
(因为这是参数的名称)。你必须把 do_sth_new
本身写成宏:
#define do_sth_new(_foo) \
do { do_sth(_foo, #_foo); } while (false)
只有这样才会:
T bar;
do_sth_new(bar);
生成 "bar"
.
不,除了使用预处理器之外别无选择,因为这是词法级别的操作。您需要对语言中的 AST 进行 LISP 级修改以获得更好的解决方案,这不太可能发生。
没有真正的方法可以避免宏进行字符串化。
您可以做的是以更面向 C++ 对象的方式装饰它,特别是如果您想要执行多个采用对象及其 var 名称的不同方法,并且如果这是一个调试功能,您可能想要在生产中禁用。
所以我建议你声明一个模板 class DebugContextWrap,这种类型的对象(或 const ref)可以作为单个参数传递给函数,而不是有 2 个参数。
一个缺点是,如果您的函数代码实际上想要访问实际值,那么您必须像对迭代器所做的那样通过 operator ->
或 data()
执行间接寻址。
然后您可以编写一个生成 DebugContextWrap 实例的宏 - 例如:
template class FooType
class DebugContextWrap
{
FooType& fooVal;
const char* debugName;
const char* debug__FILE__val;
const int debug__LINE__val;
public:
DebugContextWrap(FooType& fooVal,
const char* debugName, const char* debug__FILE__val, const int debug__LINE__val)
{ ... }
DebugContextWrap(FooType& fooVal) // implicit when caller doesn't use DEBUG_WRAP
{ ... }
FooType* operator ->()const
{ return &foo; }
FooType& operator *()const
{ return foo; }
FooType& Data()const
{ return foo; }
const char* DebugName()const
{ return debugName; }
};
#define DEBUG_WRAP(foo) \
DebugContextWrap<decltype foo>(foo, #foo, __FILE__, __LINE__)
void do_sth(const DebugContextWrap<FooType>& foo);
do_sth(DEBUG_WRAP(foovar));