Как получить адрес переменной, используя строку с именем переменной?

Я хотел бы сделать что-то вроде простого и быстрого общего отладчика консоли. Эта небольшая библиотека должна быть встроена в основную программу.

Поэтому я хотел бы сделать что-то подобное при запуске программы в режиме консоли:

"input: print i" "output: 15.53" "input: set color 255" "input: print color" "output: 255"

И "i", и "color" будут предварительно объявленными переменными в коде. Это не интерпретатор, просто удобный способ проверить и изменить содержимое переменных.

GDB не является правильным решением для моей проблемы, так как я буду использовать этот код для программ компьютерной графики, которые я буду кодировать, поэтому он должен быть в состоянии работать в "режиме выпуска".

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

Есть ли способ преобразовать строку, скажем, "color", чтобы получить адрес целочисленной переменной с именем color в C++? Если нет, то как я могу решить проблему?

11 ответов

Не для сердца, но вы можете использовать dlopen а также dlsym получить адрес глобально объявленных переменных.

Вы можете не знать об этой идиоме, которая может упростить вам реализацию этого:

#include <stdio.h>
#define REPORT(V) printf("%s: %i\n", #V, V);
int main() {
  int i = 5;
  REPORT(i);
  return 0;
}

Выход

i: 5

Имена переменных предназначены для программиста, а C++ не хранит метаданные. (Данные об исходном коде). Вы не можете получить доступ к переменной по имени во время выполнения.

Тем не менее, вы можете использовать что-то вроде std::map или unordered_map ( tr1 или boost), что дает вам ключ и значение. Проблема в том, что значение должно быть однородным в контейнере. Это может быть облегчено с помощью Boost.Any.

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

Одним из вариантов в C++ было бы использование std::map из std::string для любого типа данных ваших переменных. Затем вы можете обратиться к my_map["my_variable_name"] = new_value, Но это подразумевает незначительное влияние на производительность и не является хорошей идеей в большинстве случаев.

Вы можете использовать GDB с внешним файлом символов (symbol-file команда). Поэтому, если вы строите свой исполняемый файл с отладочной информацией во внешнем файле, вам даже не нужно писать свой собственный отладчик... Просто не забудьте взять файл символов, когда будете отлаживать установленный клиентский исполняемый файл.

Работай усердно или используй Lua. Ссылки:

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

Я думаю, способ, которым вы упомянули (явно указав места, к которым вы хотите получить доступ из вашего "отладчика"), кажется, лучший путь. Вы можете использовать макросы как синтаксический сахар, чтобы немного облегчить бремя:

struct descriptor {
    const char* name;
    const char* type;
};

#define DEFGLOBAL(type, name) \
    static struct descriptor name ## _descriptor = { \
         # name, # type \
    }; \
    type name

DEFGLOBAL(int, some_number);

Я бы рекомендовал взглянуть на исходный код Emacs. Они делают что-то вроде вышеупомянутых строк, чтобы экспортировать переменные C в интерпретатор Lisp. Они также используют его для экспорта примитивных функций и т. Д. На уровень Lisp.

Что заставляет вас думать, что вы не можете запустить GDB в "режиме релиза"? Для идентификации переменных GDB использует символы отладки, и их можно создавать независимо от того, какие указаны другие флаги компилятора. Вы можете включить оптимизацию и все остальное, что составляет ваш "режим выпуска", и при этом генерировать символы отладки для использования GDB.

Альтернативой для Lua было бы использование Boost::Python; это также позволяет вам создавать API-интерфейсы скриптов quick-n-dirty.

Простое решение, о котором вы подумали, по сути является единственным решением, доступным для неотладочного кода C/C++. Тем не менее, я думаю, что есть два камня преткновения, которые следует учитывать:

  1. Вы сможете наблюдать только глобальные переменные. Любые локальные переменные в ваших функциях не будут доступны вашему инспектору, так как эти стековые кадры не будут существовать, когда вы обрабатываете ввод из командной строки, предположительно как часть цикла обработки верхнего уровня. (Если вы не обрабатываете ввод командной строки в каждой функции, что я бы не рекомендовал, и в этом случае вы никогда не узнаете, какой кадр стека функции будет проверяться при вводе команды.)
  2. Вы не допустите, чтобы компилятор выполнил определенные оптимизации регистра. Если ваш код когда-либо берет адрес переменной (например, чтобы включить указатель на эту переменную в таблицу символов вашего инспектора), то компилятор должен поместить эту переменную в ячейку памяти, даже если в противном случае он будет хранить эту переменную исключительно в регистре для скорости.

Вы можете оптимизировать его и построить его с помощью символов отладки (возможно в GCC), а затем выяснить, как читать символы отладки и тому подобное, чтобы отлаживать себя так же, как это делает GDB... но это действительно кажется излишним... Вы уверены, что это жизнеспособный дизайн и что вы используете правильный язык, чтобы сделать такую ​​вещь? Когда именно этот консольный отладчик будет когда-либо использоваться?

Другие вопросы по тегам