这是一个关于宏的更理论性的问题(我认为)。我知道宏获取源代码并生成目标代码而不评估它,使程序员能够创建更多功能的句法结构。如果我不得不对这两个宏系统进行分类,我会说有"C风格"宏和"Lisp风格"宏。

似乎调试宏可能有点棘手,因为在运行时,实际运行的代码与源代码不同。

调试器如何根据预处理的源代码跟踪程序的执行?是否有一个特殊的"调试模式",必须设置为捕获有关宏的额外数据?

在C中,我可以理解你会为调试设置一个编译时开关,但是解释语言(如某些形式的Lisp)如何做到这一点?

很抱歉没有尝试这个,但是lisp工具链需要比我花费更多的时间来弄清楚。

有帮助吗?

解决方案

我不认为"C风格"和"Lisp风格"宏在编译方式上有根本的区别。两者都在编译器看到它之前转换源。最大的区别是C的宏使用C预处理器(一种较弱的辅助语言,主要用于简单的字符串替换),而Lisp的宏是用Lisp本身编写的(因此可以做任何事情)。

(作为旁白:我有一段时间没有看到非编译的Lisp了。..当然不是世纪之交以来。但是,如果有的话,被解释似乎会使宏调试问题更容易,而不是更难,因为你有更多的信息。)

我同意迈克尔的观点:我根本没有看到处理宏的c的调试器。使用宏的代码在任何事情发生之前都会被转换。该 编译C代码的"调试"模式 通常只是意味着它存储 函数、类型、变量、文件名等 --我不认为他们中的任何一个存储有关宏的信息。

  • 用于调试程序 使用方法 宏, ,Lisp几乎是一样的 这里的C:你的调试器看到 编译的代码,而不是宏 申请。通常宏是 保持简单,并调试 独立使用前,避免 对此的需要,就像C一样。

  • 用于调试宏 自己, ,在你去某个地方使用它之前,Lisp确实有功能 这使得这比在C中更容易, 例如,repl和 macroexpand-1 (虽然在C 显然有一种方法可以 macroexpand整个文件,完全,在 一次)。你可以看到 宏扩展的前后, 就在你的编辑里,当你写作的时候 它。

我不记得任何时候我遇到了调试的情况 进入 宏定义本身会很有用。要么是宏定义中的一个错误,在这种情况下 macroexpand-1 立即隔离问题,或者它是一个错误,在这种情况下,正常的调试设施工作正常,我不关心我的调用堆栈的两个帧之间发生了宏扩展。

其他提示

lispworks 开发人员可以使用步进工具

lispworks提供了一个步进,其中一个人可以通过完整的宏观扩展过程

您应该真正调查球拍具有宏的调试代码的那种支持。这种支持有两个方面,正如肯提到。一方面存在调试宏的问题:常见的LISP是最好的方法,即手动展开宏表单。通过CPP,情况类似但更原始 - 您只需仅通过CPP扩展运行代码并检查结果。但是,这两种都不足够了解更多涉及的宏,这是拥有宏的动机调试器在球拍上 - 它向您逐一的语法扩展步骤,其中包含额外的基于GUI的迹象,如绑定标识符等。

使用宏侧面,球拍始终比其他方案和LISP实现更先进。这个想法是每个表达式(作为语法对象)是代码加上包含其源位置的附加数据。这种方式当表单是宏时,具有来自宏的零件的扩展代码将具有正确的源位置 - 从宏的定义而不是从它使用(表单不是真正存在的地方)。某些方案和LISP实现将使用子表单的标识实现这一限制,因为所提到的DMitry-VK。

我不知道Lisp宏(我怀疑可能与C宏)或调试可能完全不同)或调试,但大多数 - C / C ++调试器不会特别好处理C预处理器宏的源级调试。

一般来说,C / C ++调试器他们不会进入宏定义。如果宏扩展到多个语句中,则调试器通常只需留在同一个源线(其中为每个调试器的步骤操作调用宏)。

这可以使调试宏比他们可能更痛苦 - 又是另一个原因,避免在C / C ++中。如果宏以真正的神秘方式行为行为不端,我将删除进入装配模式以调试它或展开宏(手动或使用编译器的交换机)。这很罕见,你必须去那个极端;如果您正在编写复杂的宏,您可能采取错误的方法。

通常在C源级调试中有线粒度(“下一个”命令)或指令级粒度(“步入”)。宏处理器将特殊指令插入处理后的源,允许编译器将编译的CPU指令序列映射到源代码行。

在LISP中,宏与编译器之间存在任何惯例,以跟踪源代码到编译的代码映射,因此并不总是可以在源代码中进行单步执行。

明显选择是在宏展开代码中执行单步步。编译器已经看到了最终,扩展,版本的代码,可以跟踪到机器代码映射的源代码。

其他选项是使用操纵期间的Lisp表达式具有身份的事实。如果宏很简单,并且刚刚将代码粘贴到模板中,那么扩展代码的某些表达式将与从源代码读取的表达式相同(相对于EQ比较)。在这种情况下,编译器可以将一些表达式从扩展的代码映射到源代码。

简单的答案是它很复杂;-)有几个不同的东西有助于调试程序,甚至更多用于跟踪宏。

在C和C ++中,预处理器用于展开宏并包含到实际源代码中。使用#line指令在此扩展源文件中跟踪始发文件名和行号。

http://msdn.microsoft.com/ en-US / Library / B5W2CZAY(VS.80).aspx

当启用C或C ++程序时,汇编程序在跟踪源行,符号名称,类型描述符等的对象文件中生成其他信息。

http://sources.redhat.com/gdb/onlinedocs/stabs.html

操作系统具有功能,使调试器可以附加到过程并控制进程执行;暂停,单级步进等。

当调试器附加到程序时,它通过查找调试信息中程序地址的含义来将过程堆栈和程序计数器转换为符号形式。

动态语言通常在虚拟机中执行,无论是解释器还是字节码VM。它是VM,提供挂钩,以允许调试器控制程序流程和检查程序状态。

许可以下: CC-BY-SA归因
不隶属于 StackOverflow
scroll top