универсальный способ распечатать имя переменной в C ++ - PullRequest
8 голосов
/ 08 июля 2011

с учетом класса

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 * и т. Д., Т. Е. Нет необходимости перегружать оператор <<), а имя переменной-члена может быть названо любым, т. Правило следовать. Вместо того, чтобы вводить явно один за другим (очень большая утомительная и подверженная ошибкам работа), есть ли какой-нибудь общий способ? </p>

Спасибо за любые комментарии!

Ответы [ 6 ]

14 голосов
/ 08 июля 2011

Вы можете использовать злой макрос:

#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

Почему смешной макрос?

Ответ на не заданный вопрос.Подумайте, что произойдет, если вы вложите вызов макроса в условный цикл или цикл for. Маршалл Клайн объясняет все остальное

9 голосов
/ 08 июля 2011

Функция, которую вы ищете, обычно называется отражением . Он не является частью C ++, поскольку в скомпилированных языках информация, которую вы ищете (понятные человеку имена переменных), как правило, не сохраняется компилятором. Он не нужен для запуска кода, поэтому нет смысла его включать.

Отладчики могут часто проверять либо внеполосную символьную информацию, либо символьные данные, хранящиеся в двоичных файлах для этой самой цели, чтобы показать такие имена, но повторное выполнение этого для этой цели, вероятно, больше работы, чем стоит.

Я бы предложил поискать несколько «хитростей» (= решений), чтобы реализовать это самостоятельно.

1 голос
/ 08 июля 2011

Это невозможно (см. Другие ответы).

Один обходной путь , для этого используется автоматическая генерация кода. Вы записываете определения полей в файл, а затем генерируете из него файлы .h и .cpp. Я использовал это для кода, который имел около 100 классов с большим количеством полей. Он был в состоянии генерировать код для отправки их в потоки (в основном для отладки) и для связи через сокеты. Он очень надежный (никогда не тестировал ни одну из этих функций), но, поскольку он не является чистым C ++, он не может быть решением для вас.

0 голосов
/ 16 июля 2019

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

0 голосов
/ 13 ноября 2018

Макрос watch - один из самых полезных приемов за всю историю.

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

Если вы отлаживаете свой код, watch(variable); напечатает имя переменной и ее значение. (Это возможно, потому что он построен во время предварительной обработки.)

0 голосов
/ 08 июля 2011

Нет способа перечислить членов класса в 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
...