How to “interleave” C/C++ souce with my string (only inside functions at appropriate places)?

こ雲淡風輕ζ 提交于 2020-01-04 06:59:42

问题


For example, there is the source:

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

It should be transformed to:

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

I.e. to insert "MYMACRO\n" at the end of each line where statement can be, only inside functions.

How to do it easily? Should I use regular expressions? What tools should I use?

For example, can gcc output all line numbers of all statement begins (or ends) inside functions?

@related How to tell gcc to instrument the code with calls to my own function each _line_ of code?

@related What profiler should I use to measure _real_ time (including waiting for syscalls) spend in this function, not _CPU_ one


回答1:


What are you trying to accomplish by doing this? Based on the description of the task, there is probably a much easier way to approach the problem. If you're sure that this is the best way to accomplish your task, read on.


You would have to implement some sort of rudimentary C language parser to do this. Since you are processing text, I would recommend using a scripting language like perl, python, or ruby to modify your text instead of writing a C program to do it.

Your parser will walk through the file a line at a time and for each line, it will determine whether it needs to insert your macro. The parser will need to keep track of a number of things. First, it needs to keep track of whether or not it is currently inside of a comment. When you encounter a /* sequence, set a "in comment" flag and clear it the next time you encounter a */ sequence. Whenever that flag is set, you will not add a macro invocation. Also, you will need to keep track of whether or not you are inside a function. Assuming your code is fairly simple and straightforward, you can have a "brace counter" that starts at zero, increments whenever you encounter a {, and decrements whenever you encounter a }. If your brace counter is zero, then you are not inside of a function and you shouldn't add a macro call. You will also want to add special code to detect and ignore braces that are part of a structure definition, array initializer, etc. Note that simple brace counting won't work if your code does more complicated things like:

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

While you could argue that snippet is simply a case of poorly-written code, it's just an example of the type of problem that you can run into. If your code has something in it that breaks simple brace counting, then this problem just got significantly more difficult. One way to tell if your code will break brace counting is if you reach the end of the file with a non-zero brace count or if at any point in time the brace count goes negative.

Once you can determine when you are in a function and not in a comment, you need to determine whether the line needs a macro inserted after it. You can start with a few simple rules, test the script, and see if there are any cases that it missed. For starters, any line ending in a semicolon is the end of a statement and you will want to insert a macro after it. Similar to counting braces, when you are inside of a function you will want to count parenthesis so that you can determine if you are inside of a function call, loop conditional, or other compound statement. If you are inside one of these, you will not add the macro. The other code location to track is the the start and end lines of a { ... } block. If a line ends in { or }, you will add a macro after it.

For a complicated task like this, you will definitely want to script something up, try it out on a relatively simple piece of code, and see what it gets wrong. Make adjustments to cover the cases you missed the first time and re-test. When it can parse the simple code correctly, give it something more complicated and see how well it does.

''Update:'' To address the concerns that some people have expressed regarding the additional latency of adding print commands, remember that you don't have to print a timestamp at every macro call. Instead, have the macro call grab a timestamp and stick it onto a list. Once your program is done, print all the timestamps off of the list. That way, you save all the print-related delay until after your test is over.




回答2:


rewrite your sources so the following works :-)

Instead of gcc ... file1.c file2.c ... do

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



回答3:


Here's some quick and dirty C# code. Basically just primitive file IO stuff. It's not great, but I did whip it up in around 3 minutes. This code implies that function blocks are demarcated with a comment line of "//FunctionStart" at the beginning and "//FunctionEnd" at the end. There are more elegant ways of doing this, this is the fast/dirty/hacky approach.

Using a managed app to do this task is probably overkill, but you can do a lot of custom stuff by simply adding on to this 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();
        }


来源:https://stackoverflow.com/questions/3989992/how-to-interleave-c-c-souce-with-my-string-only-inside-functions-at-appropr

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!