【问题标题】:Insert text into C++ code between functions在函数之间将文本插入 C++ 代码
【发布时间】:2013-05-17 06:23:44
【问题描述】:

我有以下要求:

  • 在任何函数的入口出口点添加文本。
  • 改变源代码,除了从上面插入(所以没有预处理器或任何东西)

例如:

void fn(param-list)
{
    ENTRY_TEXT (param-list)
    //some code
    EXIT_TEXT
}

但不仅在这种简单的情况下,它还可以使用预处理器指令运行!

例子:

void fn(param-list)
#ifdef __WIN__
{
  ENTRY_TEXT (param-list)
  //some windows code
  EXIT_TEXT
}
#else
{
    ENTRY_TEXT (param-list)
    //some any-os code

    if (condition)
    {
        return; //should become EXIT_TEXT
    }

    EXIT_TEXT
}

所以我的问题是:有合适的方法吗?

我已经尝试使用编译器使用的解析器进行一些工作,但由于它们都依赖于在解析之前运行预处理器,所以它们对我来说毫无用处。

此外,一些不需要预处理器的令牌生成解析器有些无用,因为它们会生成令牌的内存映射,然后导致完整的新源代码,而不仅仅是插入文本。

我正在做的一件事是尝试使用 FLEX(或 JFlex),如果这是一个有效的选项,我将不胜感激。 ;-)

编辑: 澄清一点:目的是允许堆栈跟踪之类的东西。 我想跟踪每个函数调用,为了遵循调用层次结构,我需要在函数的入口点和函数的出口点放置一个宏。 这构建了一个函数调用跟踪。 :-)

EDIT2:特定于编译器的选项不太合适,因为我们有许多不同的编译器可供使用,而且许多编译器可能没有得到任何工具的良好支持。

【问题讨论】:

  • 澄清一下:你想修改每个函数的源代码?
  • 是的,c++源代码文件中任何函数的每个入口和出口点都需要标记。
  • 这是某种文档吗?
  • 你明白了 :-) 检查我的编辑以获得简短的描述!
  • 这就是我们有调试器的原因。你可以停下来看看堆栈跟踪。

标签: c++ parsing lexer


【解决方案1】:

不幸的是,你的想法不仅不切实际(C++ 解析起来很复杂),而且注定要失败。

您遇到的主要问题是异常会完全绕过您的 EXIT_TEXT 宏。


你有几个解决方案。

如前所述,第一个解决方案是使用依赖于平台的方式来计算堆栈跟踪。它可能有点不精确,特别是因为内联:即,在调用者中内联的小函数,它们不会出现在堆栈跟踪中,因为在程序集级别没有生成函数调用。另一方面,它广泛可用,不需要对代码进行任何手术并且不会影响性能

第二种解决方案是只在进入时引入一些东西,然后使用 RAII 来完成退出工作。比您的方案要好得多,因为它自动处理多个返回和异常,但它遇到了同样的问题:如何自动执行插入。为此,您可能希望在 AST 级别进行操作,并修改 AST 以引入您的小宝石。您可以使用 Clang(查找 c++11 迁移工具以获取大量重写示例)或 gcc(使用插件)来完成。

最后,您还有手动注释。虽然它可能看起来动力不足(而且工作量很大),但我要强调的是,您不会将日志记录留给工具...我看到手动执行此操作的 3 个优点:您可以避免在性能敏感部分引入这种开销,您可以仅保留大参数的“摘要”,您可以根据当前函数感兴趣的内容自定义摘要。

【讨论】:

  • 嗨,我几乎知道这有点不可能达到我的要求。正如你所说,第二个选项听起来非常适合我的需要。 (但是,我更喜欢#3,如果我能让每个人都遵守它;)) - 我试一试,看看什么最有效。 - 谢谢!
  • C++ 通常解析起来很复杂,但是...如果您愿意传递不符合合理编码准则(或您的内部风格)的代码,并且您只感兴趣在寻找功能时,这并不难。 (我知道我有一些简单的代码可以为枚举生成映射表——它解析了足够多的 C++ 来查找枚举常量及其范围。这并不难。)
  • @JamesKanze:有趣,我对枚举采取了相反的方式,我使用宏(带有一些 Boost.Preprocessor 魔法)来生成它和映射。
  • @JamesKanze 目前或多或少是我在做什么。我编写了自己的小解析器,但实际上 FIND 函数已经是一个大问题,因为函数签名有很多声明方式。 (不像 enum 关键字函数没有一个 ofc。)
  • @MatthieuM。我也看到这样做了。但是,我需要它用于现有资源。我的解决方案比宏要多得多,但现在我有了它,我觉得它更可取。
【解决方案2】:

我建议使用LLVM libraries & Clang 开始。

您还可以利用 C++ 语言来简化您的流程。如果您只是将一个小对象插入到在函数范围入口处构造的代码中,并依赖于它将在退出时被销毁的事实。这应该会大大简化记录函数的“退出”。

【讨论】:

  • 我已经检查了 LLVM 和类似的东西,但我一直想避免这些,因为它们似乎需要大量的时间和工作才能让它正确。 - 无论如何,谢谢,我会试一试。
【解决方案3】:

这并不能真正回答您的问题,但是,对于您的初始需求,您可以使用 execinfo.h 中的 backtrace() 函数(如果您使用的是 GCC)。

How to generate a stacktrace when my gcc C++ app crashes

【讨论】:

  • 注意:不幸的是,回溯(虽然有用)没有提供问题发生的...实际上,这应该是一个注释。
  • 除此之外,我不得不说 GCC 是可能的,但是使用了几个编译器。 (真的很多!)
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2015-10-16
  • 1970-01-01
  • 2018-03-16
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2014-06-22
相关资源
最近更新 更多