2009-10-29 67 views
0

我想做一些简单而快速的控制台调试器。这个小的lib应该嵌入到主程序中。如何使用带变量名称的字符串获取变量地址?

所以我想这样做的东西一样运行在控制台模式程序这一点的同时:

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

”i“和”color“都是预先声明的变量in-code。它不是一个解释器,只是检查和修改变量内容的方便方法。

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

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

有没有什么办法来转换一个字符串,让我们说“颜色”,以获得在C++中命名为颜色的整数变量的地址?如果不是,我怎么能解决这个问题?

+4

GDB的确是最小的必要“控制台调试器”。 “转换一个字符串让我们说”color“来获得名为color的整型变量的地址的唯一方法是读取调试符号。这就是GDB所做的,当你处于“发布模式”时,这就是你失去的东西 – Arkadiy 2009-10-29 18:19:33

+0

所以是的,它是一个解释器。您想要解释一个字符串值并检索变量的地址。您需要手动完成这项工作。所有源符号信息都从可执行文件中删除。 – 2009-10-29 18:42:22

+1

@Martin:但调试信息_can_可以存储在外部文件中,所以不必丢失。 – xtofl 2009-10-29 20:33:52

回答

3

变量名是针对程序员的,C++不存储元数据。 (关于源代码的数据)。运行时不能通过名称访问变量。这就是说,你可以使用类似std::map或unordered_map(tr1boost),它给你一个关键和价值。问题在于容器中的值必须是同质的。这可以通过使用Boost.Any来缓解。

+0

无论如何,你会想要存储类型,因为脚本语句“foo = 7”需要根据foo的类型 - int,float甚至字符串的不同来解释吗? 'boost :: variant <>'是一个更好的选择;您可以枚举您的脚本语言支持的所有类型。 – MSalters 2009-10-30 10:21:16

2

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

C++中的一个选项是使用std :: string中的std :: map指向变量的任何数据类型。然后你可以参考my_map["my_variable_name"] = new_value。但是这涉及到不重要的性能影响,并且在大多数情况下不是一个好主意。

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

0

你可以对其进行优化,并(在GCC可能)与调试符号建立它,然后找出如何读取调试符号,这样只是调试自己像gdb会..但这真的好像过度杀伤..你确定这是一个可行的设计,并使用适当的语言做这样的事情?该控制台调试器到底何时会被使用?

4

您可能不知道这个成语,这可能使你更容易实现这一点:

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

输出是

i: 5 
+0

问题是,如果它不是你想要打印的int,那么'%i'将不起作用。 – 2009-10-30 07:36:52

+0

小细节,使用C++很容易解决。例如。 'std :: clog <<(#V)<<(V)<< std :: endl' – MSalters 2009-10-30 10:18:04

4

不适合心脏佯攻,但你可以使用dlopendlsym来获取全局声明变量的地址。

0

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

  1. 您将只能观察全局变量。您的检查器中的任何局部变量都将无法提供给您的检查器,因为当您从命令行处理输入时,这些堆栈框架可能不存在,可能是作为顶级处理循环的一部分。 (除非在每个函数中处理命令行输入,我不建议这样做,在这种情况下,当您键入命令时,您将永远不会知道将检查哪个函数的堆栈帧)。
  2. 您将防止编译器确定注册优化。如果你的代码获取了一个变量的地址(例如,在你的检查器的符号表中包含一个指向该变量的指针),那么编译器必须将该变量放置在一个内存位置,即使该变量只会将该变量保存在一个寄存器中为了速度。
1

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

0

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

0

什么让你觉得你不能在“发布模式”下运行GDB? GDB用来识别变量的是调试符号,无论指定了哪些其他编译器标志,都可以构建它们。您可以启用优化以及构成您的“发布模式”的其他任何内容,并且仍然会生成供GDB使用的调试符号。