Как «чередовать» C / C ++ souce с моей строкой (только внутри функций в соответствующих местах)? - PullRequest
2 голосов
/ 21 октября 2010

Например, есть источник:

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_ времени (включая ожиданиедля системных вызовов) потратить в этой функции, а не _CPU_ один

Ответы [ 3 ]

2 голосов
/ 21 октября 2010

Чего вы пытаетесь достичь этим? Исходя из описания задачи, возможно, существует гораздо более простой способ решения проблемы. Если вы уверены, что это лучший способ выполнить вашу задачу, читайте дальше.


Для этого вам потребуется реализовать какой-то элементарный синтаксический анализатор языка Си. Поскольку вы обрабатываете текст, я бы порекомендовал использовать язык сценариев, такой как perl, python или ruby, чтобы изменить ваш текст, вместо того, чтобы писать программу на C для этого.

Ваш синтаксический анализатор будет проходить по файлу по одной строке за раз, и для каждой строки он будет определять, нужно ли ему вставлять ваш макрос. Парсер должен будет отслеживать множество вещей. Во-первых, он должен отслеживать, находится ли он внутри комментария или нет. Когда вы встречаете последовательность /*, установите флаг «в комментарии» и снимите его, когда в следующий раз встретите последовательность */. Каждый раз, когда этот флаг установлен, вы не добавите вызов макроса. Кроме того, вам нужно будет отслеживать, находитесь ли вы внутри функции. Предполагая, что ваш код довольно прост и понятен, у вас может быть «счетчик скобок», который начинается с нуля, увеличивается при каждом обнаружении { и уменьшается при каждом обнаружении }. Если ваш счетчик скобок равен нулю, то вы не находитесь внутри функции и не должны добавлять вызов макроса. Вы также захотите добавить специальный код для обнаружения и игнорирования фигурных скобок, которые являются частью определения структуры, инициализатора массива и т. Д. Обратите внимание, что простой подсчет фигурных скобок не будет работать, если ваш код выполняет более сложные вещи, такие как:

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

Хотя вы можете утверждать, что фрагмент - это просто случай плохо написанного кода, это всего лишь пример типа проблемы, с которой вы можете столкнуться. Если в вашем коде есть что-то, что нарушает простой подсчет фигурных скобок, то эта проблема стала значительно сложнее. Один из способов определить, будет ли ваш код нарушать подсчет фигурных скобок, - это если вы достигнете конца файла с ненулевым счетчиком фигурных скобок или если в любой момент времени счетная скобка станет отрицательной.

Как только вы можете определить, когда вы находитесь в функции, а не в комментарии, вам нужно определить, нужен ли строке после нее макрос. Вы можете начать с нескольких простых правил, протестировать скрипт и посмотреть, есть ли пропущенные случаи. Для начала, любая строка, заканчивающаяся точкой с запятой, является концом оператора, и вы захотите вставить макрос после него. Аналогично подсчету скобок, когда вы находитесь внутри функции, вам нужно посчитать скобки, чтобы вы могли определить, находитесь ли вы внутри вызова функции, условного цикла или другого составного оператора. Если вы находитесь внутри одного из них, вы не добавите макрос. Другое местоположение кода для отслеживания - это начальная и конечная строки блока { ... }. Если строка заканчивается на { или }, вы добавите макрос после нее.

Для такой сложной задачи, как эта, вам определенно захочется что-то написать, попробовать на сравнительно простом куске кода и посмотреть, что получится неправильно. Внесите коррективы, чтобы покрыть случаи, которые вы пропустили в первый раз, и повторите тестирование. Когда он сможет правильно проанализировать простой код, дайте ему что-нибудь более сложное и посмотрите, насколько хорошо он работает.

'' Обновление: '' Чтобы устранить озабоченность, выраженную некоторыми людьми относительно дополнительной задержки добавления команд печати, помните, что вам не нужно печатать временную метку при каждом вызове макроса. Вместо этого вызовите макрос, возьмите метку времени и вставьте ее в список. Как только ваша программа будет готова, выведите все временные метки из списка. Таким образом, вы сохраняете всю задержку, связанную с печатью, до окончания теста.

0 голосов
/ 21 октября 2010

Вот быстрый и грязный код на C #.В основном просто примитивный файл ввода-вывода.Это не очень хорошо, но я сделал это примерно за 3 минуты.Этот код подразумевает, что функциональные блоки разграничены строкой комментария «// FunctionStart» в начале и «// FunctionEnd» в конце.Есть более элегантные способы сделать это, это быстрый / грязный / хакерский подход.

Использование управляемого приложения для выполнения этой задачи, вероятно, излишне, но вы можете сделать много пользовательских вещей, просто добавивк этой функции.

        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();
        }
0 голосов
/ 21 октября 2010

переписать ваши источники, чтобы следующие работы: -)

Вместо gcc ... file1.c file2.c ... do

gcc ... `sed -e's/;/;\nMYMACRO/' file1.c` file1extra.c \
        `sed -e's/;/;\nMYMACRO/' file2.c` file2extra.c \
    ...
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...