在 C++ 中打印出变量名称的通用方法

generic way to print out variable name in c++

提问人:pepero 提问时间:7/8/2011 最后编辑:pepero 更新时间:11/8/2023 访问量:29408

问:

给定一个类

struct {
  int a1;
  bool a2;
  ...
  char* a500;
  ...
  char a10000;      
}

我想打印或流式传输

"a1 value is SOME_VALUE"  
"a2 value is SOME_VALUE"
"a500 value is SOME_VALUE"
...
"a10000 value is SOME_VALUE"

成员变量的类型不相同(主要是 int、bool、char* 等,即无需重载运算符<<),成员变量名称可以用任何名称命名,即无需遵循规则。而不是一个接一个地明确输入(非常繁琐且容易出错的工作),有什么通用的方法吗?

感谢您的任何评论!

C++ 变量 printf iostream

评论

0赞 n. m. could be an AI 7/8/2011
在 C++ 中没有通用的方法可以做你想做的事。你应该问问自己,你是否在为这项工作使用正确的工具。考虑另一种语言,或者C++中不同的数据结构,可能是向量或列表。
0赞 MSalters 7/8/2011
再说了,你真的做不好。拿 - 你真的想让它打印出来等等吗?还是你宁愿想要?C++ 有一个清晰的想法,即如何打印,那就是struct { std::string a; }a._BUF[0] = 'H' a._BUF[1] = 'e' a._BUF[2] = 'l' , a._BUF[0] = 'l'a = "Hello, world\n"struct Foooperator<<(std::ostream&, Foo);
0赞 spraff 7/8/2011
一些框架添加了反射,但这不是本机语言功能

答:

0赞 Armen Tsirunyan 7/8/2011 #1

无法在 C++ 中枚举类的成员,因为 C++ 中没有反射。因此,您无权访问变量名称。

不过,您可以使用指向成员的指针...

void PrintMemberValue(int MyClass::*p, MyClass const & obj)
{
    cout << "Member has value " << obj.*p;
}

MyClass obj;
int MyClass::*p = &MyClass::a1;
PrintMemberValue(p, obj);
p = &MyClass::a2;
PrintMemberValue(p, obj);
etc
11赞 unwind 7/8/2011 #2

您要查找的功能通常称为反射。它不是C++的一部分,因为在编译语言中,你所追求的信息(人类可读的变量名)通常不会由编译器保留。运行代码不需要它,因此没有必要包含它。

调试器通常可以为此目的检查带外符号信息或保存在二进制文件中的符号数据,以显示此类名称,但为此目的重新执行此操作可能比它的价值要多。

我建议寻找许多“技巧”(=解决方案)中的一些来自己实现这一点。

评论

1赞 sehe 7/8/2011
古老的 pstruct 命令解析输出,以可用形式获取信息。它本来是用来和perl一起用的,但你可以把它用于这种类型的方法gcc -g -s
21赞 sehe 7/8/2011 #3

您可以使用一个邪恶的宏:

#define DUMP(a) \
    do { std::cout << #a " is value " << (a) << std::endl; } while(false)

使用示例(编辑现在更新了结构成员的示例):

#include <iostream>

#define DUMPSTR_WNAME(os, name, a) \
    do { (os) << (name) << " is value " << (a) << std::endl; } while(false)

#define DUMPSTR(os, a) DUMPSTR_WNAME((os), #a, (a))
#define DUMP(a)        DUMPSTR_WNAME(std::cout, #a, (a))

struct S {
    int a1;
    float a2;
    std::string a3;

    std::ostream& dump(std::ostream& os)
    {
        DUMPSTR(os, a1);
        DUMPSTR(os, a2);
        DUMPSTR(os, a3);
        return os;
    }
};

int main()
{
    S s = { 3, 3.14, "  03.1415926" };

    s.dump(std::cout);

    DUMP(s.a1);
    DUMP(s.a2);
    DUMP(s.a3);

    return 0;
}

在 CodePad 上观看现场演示

a1 is value 3
a2 is value 3.14
a3 is value   03.1415926
s.a1 is value 3
s.a2 is value 3.14
s.a3 is value   03.1415926

为什么是有趣的宏?

回答未被问到的问题。考虑一下如果将宏调用嵌套在条件循环或 for 循环中会发生什么情况。马歇尔·克莱恩(Marshall Cline)解释了其余的

评论

0赞 David Hammen 7/8/2011
是的。有时需要一个邪恶的宏来修补语言中的众多漏洞之一。
0赞 pepero 7/8/2011
嗨,sehe,谢谢你的回答。但是你仍然需要输入a1,a2,a3,...,这比'printf(“a1 ...”)没有太大的改进或通用方法;printf (“a2 ...”);...'.如果我有 10, 000 个变量怎么办?
0赞 sehe 7/8/2011
呃......那你就......(a) 搞砸了 (b) 做错了什么。结构中有 10,000 个变量:您应该使用容器。另外,看看序列化框架(它们仍然不打印成员名称,但我认为这是你的类的设计问题)
0赞 pepero 7/8/2011
嗨,sehe,似乎不存在通用方法。我支持你的帖子,但接受 unwind 的回答,指出反思。:-)
0赞 Keith M 11/30/2018
“有趣的宏”下的链接似乎已损坏。为什么是“邪恶的”?
1赞 Karoly Horvath 7/8/2011 #4

这是不可能的(请参阅其他答案)。

一种解决方法是使用自动代码生成。在文件中写入字段定义,然后从中生成 .h 和 .cpp 文件。我将其用于一个代码,该代码具有大约 100 个类和许多字段。它能够生成代码,将它们发送到流(主要是调试)和套接字通信。它非常可靠(从未测试过任何这些功能),但由于它不是纯C++,因此它可能不是您的解决方案。

评论

0赞 Keith M 11/30/2018
如果您 (a) 不想或不能使用外部库,并且 (b) 有太多的类/结构成员而无法合理地手动复制粘贴宏(sehe 的答案),这可能是最好的方法。(或者,如果结构/类代码经常更改,并且您担心有人最终会犯复制粘贴错误。
6赞 Agile_Eagle 11/13/2018 #5

宏是有史以来最有用的技巧之一。watch

#define watch(x) cout << (#x) << " is " << (x) << endl

如果要调试代码,将打印变量的名称及其值。(这是可能的,因为它是在预处理期间构建的。watch(variable);

0赞 Costa 7/16/2019 #6

GDB 可以打印结构体。此脚本生成 gdb 脚本,用于在gdb_print位置指定位置设置断点和打印值:

gdb-print-prepare()
{

    # usage:
    # mark print points with empty standalone defines:
    # gdb_print(huge_struct);
    # gdb-print-prepare $src > app.gdb
    # gdb --batch --quiet --command=app.gdb $app
    cat  <<-EOF
    set auto-load safe-path /
    EOF
    grep --with-filename --line-number --recursive '^\s\+gdb_print(.*);' $1 | \
    while IFS=$'\t ;()' read line func var rest; do
        cat  <<-EOF
        break ${line%:}
        commands
        silent
        where 1
        echo \\n$var\\n
        print $var
        cont
        end
        EOF
    done
    cat  <<-EOF
    run
    bt
    echo ---\\n
    EOF
}

https://gitlab.com/makelinux/lib/blob/master/snippets/gdb-print-prepare.md