13

given a class

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

I want to print or stream out

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

the type of the member variables are not the same (mainly, int, bool, char*, etc, i.e., no need to overload << operator), and the member variable name could be named with anything, i.e., no rule to follow. Instead of typing explicitely one by one (very big tedious, and error-prone work), is there any generic way?

Thanks for any comments!

pepero
  • 7,095
  • 7
  • 41
  • 72
  • There is no generic way to do what you want in C++. You should ask yourself whether you are using the right tool for the job. Consider either another language, or a different data structure in C++, perhaps a vector or a list. – n. m. could be an AI Jul 08 '11 at 10:13
  • Besides, you really can't do it properly. Take `struct { std::string a; }` - do you really want it to print out `a._BUF[0] = 'H' a._BUF[1] = 'e' a._BUF[2] = 'l' , a._BUF[0] = 'l'` etcetera? Or would you rather want `a = "Hello, world\n"` ? C++ has one clear idea how you print `struct Foo`, and that's with `operator<<(std::ostream&, Foo);` – MSalters Jul 08 '11 at 11:39
  • Some frameworks [add reflection](http://doc.qt.nokia.com/latest/properties.html) but this is not a native language feature – spraff Jul 08 '11 at 10:12

6 Answers6

19

You can employ an evil macro:

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

Usage example (Edit now updated with example for struct members):

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

See live demo on CodePad

Why the funny macro?

Answering the unasked question. Consider what happens if you nest the macro invocation in a conditional, or a for loop. Marshall Cline explains the rest

sehe
  • 374,641
  • 47
  • 450
  • 633
  • Yep. Sometimes an evil macro is needed to get patch one of the many holes in the language. – David Hammen Jul 08 '11 at 11:20
  • Hi, sehe, thank you for your answer. But You still need to type a1, a2, a3, ... , that is not big improvement or generic approach than 'printf ("a1 ..."); printf ("a2 ..."); ...'. what if i have 10, 000 variables? – pepero Jul 08 '11 at 11:28
  • Ermmm... then you're... (a) screwed (b) doing something wrong. 10,000 variables in a struct: you should be using a container. Also, look at serialization frameworks (they still don't print member names, but I think that is a design issue with your classes) – sehe Jul 08 '11 at 11:34
  • Hi, sehe, seems no generic way exists. I up-vote your post for this macro, but accept unwind's answer for pointing out reflection. :-) – pepero Jul 08 '11 at 11:38
  • The link under "funny macro" appears to be broken. Why is it "evil"? – Keith M Nov 29 '18 at 18:48
11

The feature you're looking for is typically called reflection. It is not part of C++, since in compiled languages the information you're after (human-readable variable names) is generally not kept by the compiler. It is not needed to run the code, so there's no point in including it.

Debuggers can often inspect either out-of-band symbol information, or symbol data kept in binaries for this very purpose, to show such names but re-doing that for this purpose is probably more work than it's worth.

I would suggest looking for some of the many "tricks" (=solutions) to implement this yourself.

unwind
  • 391,730
  • 64
  • 469
  • 606
  • 1
    The venerable [`pstruct` command](http://linux.about.com/library/cmd/blcmdl1_pstruct.htm) parses output of `gcc -g -s` to get the information in usable form. It was intended for use with perl, but you could use it for this type of approach – sehe Jul 08 '11 at 10:25
6

The watch macro is one of the most useful tricks ever.

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

If you’re debugging your code, watch(variable); will print the name of the variable and its value. (It’s possible because it's built during preprocessing time.)

Agile_Eagle
  • 1,710
  • 3
  • 13
  • 32
1

It's not possible (see the other answers).

One workaround for this is to use automatic code generation. You write the field definitions in a file and then generate the .h and .cpp files from it. I used this for a code which had around 100 classes with lots of fields. It was able to generate the code for sending them to streams (mostly debugging) and for socket communication. It's very reliable (never had to test any of those functionalities), but since it's not pure C++ it might not be the solution for you.

Karoly Horvath
  • 94,607
  • 11
  • 117
  • 176
  • This is probably the best way to go if you (a) don't want to, or can't, use an external library and (b) have too many class/struct members to reasonably copy-paste macros manually (sehe's answer). (Or if the struct/class code gets changed often and you're concerned someone will eventually make a copy-paste error.) – Keith M Nov 29 '18 at 18:37
0

GDB can print structs. This script generates gdb script to set breakpoints and print values at specified by gdb_print locations:

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
}

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

Costa
  • 461
  • 7
  • 13
0

There is no way to enumerate members of the class in C++, since there is no reflection in C++. So you cannot have access to the variable name.

You could use pointers to members though...

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
Armen Tsirunyan
  • 130,161
  • 59
  • 324
  • 434