0

我想做一个简单快速的通用控制台调试器。这个小库应该嵌入到主程序中。

所以我想在控制台模式下运行程序时做这样的事情:

“输入:打印 i” “输出:15.53” “输入:设置颜色 255” “输入:打印颜色” “输出:255”

“i”和“color”都是代码中预先声明的变量。它不是解释器,只是检查和修改变量内容的便捷方式。

GDB 不是我的问题的有效解决方案,因为我将将此代码用于我将编码的计算机图形程序,因此它需要能够在“发布模式”下运行。

到目前为止,我发现的一个非常简单的解决方案是创建一个包含 void 指针、指针数据类型和表示变量名的字符串的结构列表。但它不会像我想象的那样自动。

有没有办法转换一个字符串,比如说“颜色”,以获得 C++ 中名为颜色的整数变量的地址?如果没有,我该如何解决这个问题?

4

11 回答 11

4

你可能不知道这个习语,这可能会让你更容易实现:

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

输出是

i: 5
于 2009-10-29T18:23:06.997 回答
4

不是为了装腔作势,但您可以使用dlopenanddlsym来获取全局声明变量的地址。

于 2009-10-29T18:24:25.637 回答
3

变量名是给程序员的,C++ 不存储元数据。(关于源代码的数据)。您不能在运行时按名称访问变量。

也就是说,您可以使用诸如std::map或 unordered_map (tr1boost)之类的东西,它们会为您提供键和值。问题是容器中的值必须是同质的。这可以通过使用Boost.Any来缓解。

于 2009-10-29T18:14:44.057 回答
2

不,你不能这样做。一旦将源代码编译为二进制形式并去掉其符号,二进制文件中就没有人类可读名称的记录。

C++ 中的一种选择是使用从 std::string 到变量的任何数据类型的 std::map。然后你可以参考my_map["my_variable_name"] = new_value. 但这会带来不小的性能影响,在大多数情况下并不是一个好主意。

于 2009-10-29T18:15:21.700 回答
1

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 级别。

于 2009-10-29T18:19:49.240 回答
1

可以将 GDB 与外部符号文件(symbol-file命令)一起使用。因此,如果您使用外部文件中的调试信息构建可执行文件,您甚至不必编写自己的调试器......只是在调试客户已安装的可执行文件时不要忘记带上符号文件。

于 2009-10-29T20:37:55.023 回答
0

努力工作或使用Lua。链接:

于 2009-10-29T18:21:10.993 回答
0

您可以对其进行优化并使用调试符号(可能在 GCC 中)构建它,然后弄清楚如何读取调试符号等来调试自己,就像 gdb 一样.. 但这似乎有点矫枉过正.. 你确定这是一个可行的设计,并且您使用正确的语言来做这样的事情?究竟什么时候会使用这个控制台调试器?

于 2009-10-29T18:22:44.943 回答
0

您认为的简单解决方案本质上是唯一可用于非调试 C/C++ 代码的解决方案。但是,我认为您应该考虑两个绊脚石:

  1. 您将只能观察全局变量。检查员无法使用函数中的任何局部变量,因为当您处理来自命令行的输入时,这些堆栈帧将不存在,可能是顶级处理循环的一部分。(除非您在每个函数中处理命令行输入,我不建议这样做,在这种情况下,您将永远不知道在键入命令时会检查哪个函数的堆栈帧。)
  2. 您将阻止编译器进行某些寄存器优化。如果您的代码曾经获取变量的地址(例如,在检查器的符号表中包含指向该变量的指针),那么编译器必须将该变量放在内存位置,即使它会将该变量单独保存在寄存器中为了速度。
于 2009-10-29T18:35:13.157 回答
0

是什么让你认为你不能在“发布模式”下运行 GDB?GDB 依赖于识别变量的是调试符号,无论指定什么其他编译器标志都可以构建它们。您可以启用优化和构成“发布模式”的所有其他内容,并且仍然生成调试符号供 GDB 使用。

于 2009-10-30T12:22:31.453 回答
0

Lua 的另一种选择是使用 Boost::Python;它也允许你创建一个 quick-n-dirty 脚本 API。

于 2009-10-30T10:24:19.883 回答