如何用我的字符串“交错”C / C ++源(只在适当的地方内部函数)?

例如,有来源:

void func1() { func3(); if(qqq) { func2(); } func4( ); } 

它应该转变为:

 void func1() { MYMACRO func3(); MYMACRO if(qqq) { MYMACRO func2(); MYMACRO } MYMACRO func4( ); MYMACRO } 

即在每行语句的末尾插入“MYMACRO \ n”,仅在函数内部插入。

怎么做容易? 我应该使用正则表达式吗? 我应该使用什么工具?

例如,gcc可以输出所有语句的所有行号在函数内开始(或结束)吗?

@related 如何告诉gcc使用我自己的函数调用每个_line_代码来检测代码?

@related 我应该使用什么样的探查器来测量_real_ time(包括等待系统调用)花在这个函数上,而不是_CPU_ one

这样做你想要完成什么? 根据任务的描述,可能有一种更容易的方法来解决问题。 如果您确定这是完成任务的最佳方式,请继续阅读。


您必须实现某种基本的C语言解析器才能执行此操作。 由于您正在处理文本,我建议使用像perl,python或ruby这样的脚本语言来修改您的文本而不是编写C程序来执行此操作。

您的解析器将一次遍历文件一行,对于每一行,它将确定是否需要插入宏。 解析器需要跟踪许多事情。 首先,它需要跟踪它当前是否在评论中。 遇到/*序列时,设置“in comment”标志,并在下次遇到*/序列时将其清除。 只要设置了该标志,就不会添加宏调用。 此外,您需要跟踪您是否在函数内部。 假设您的代码相当简单明了,您可以使用一个从零开始的“大括号计数器”,每当遇到{时递增,并在遇到}时递减。 如果您的大括号计数器为零,那么您不在函数内部,并且您不应该添加宏调用。 您还需要添加特殊代码来检测和忽略作为结构定义,数组初始化程序等一部分的大括号。请注意,如果您的代码执行更复杂的操作,则简单的大括号计数将不起作用:

 void some_function (int arg) { #ifdef CHECK_LIMIT_ONLY if (arg == 0) { #else if (arg < 10) { #endif // some code here ... } } 

虽然您可能认为代码片段只是代码编写不好的情况,但它只是您可能遇到的问题类型的一个示例。 如果你的代码中有一些东西打破了简单的支撑计数,那么这个问题就变得非常困难了。 判断代码是否会破坏括号计数的一种方法是,如果使用非零括号计数到达文件末尾,或者在任何时间点,括号计数变为负数。

一旦确定何时处于函数中而不是注释中,就需要确定该行是否需要在其后插入宏。 您可以从一些简单的规则开始,测试脚本,并查看是否有任何错过的情况。 对于初学者来说,任何以分号结尾的行都是语句的结尾,你需要在它之后插入一个宏。 与计算大括号类似,当您在函数内部时,您将需要计算括号,以便您可以确定是否在函数调用,循环条件或其他复合语句中。 如果您在其中一个内部,则不会添加宏。 要跟踪的其他代码位置是{ ... }块的起始行和结束行。 如果一行以{}结尾,则会在其后添加一个宏。

对于像这样的复杂任务,你肯定想要编写一些代码,在一段相对简单的代码上试一试,看看它出了什么问题。 进行调整以覆盖您第一次错过的案例并重新测试。 当它可以正确解析简单代码时,给它一些更复杂的东西,看它有多好。

''更新:''为了解决一些人对添加打印命令的额外延迟所表达的担忧,请记住您不必在每次宏调用时打印时间戳。 相反,让宏调用抓住时间戳并将其粘贴到列表中。 程序完成后,打印列表中的所有时间戳。 这样,您可以保存所有与打印相关的延迟,直到测试结束。

重写您的来源以便以下工作:-)

而不是gcc ... file1.c file2.c ...

 gcc ... `sed -e's/;/;\nMYMACRO/' file1.c` file1extra.c \ `sed -e's/;/;\nMYMACRO/' file2.c` file2extra.c \ ... 

这是一些快速而肮脏的C#代码。 基本上只是原始文件IO的东西。 这不是很好,但我确实在3分钟左右鞭打它。 此代码表示function块在开头用“// FunctionStart”的注释行划分,在末尾用“// FunctionEnd”划分。 有更优雅的方法,这是快速/肮脏/ hacky方法。

使用托管应用程序执行此任务可能有点过分,但您可以通过简单地添加到此function来执行大量自定义操作。

  private void InsertMacro(string filePath) { //Declrations: StreamReader sr = new StreamReader(filePath); StreamWriter sw = new StreamWriter(filePath + ".tmp"); string line = ""; bool validBlock = false; //Go through source file line by line: while ((line = sr.ReadLine()) != null) { if (line == "//FunctionStart") validBlock = true; else if (line == "//FunctionEnd") validBlock = false; sw.WriteLine(line); if (validBlock) sw.WriteLine("MYMACRO"); } //Replace legacy source with updated source: File.Delete(filePath); File.Move(filePath + ".tmp", filePath); //Clean up streams: sw.Close(); sr.Close(); }