Препроцессор

Я хочу иметь препроцессор C, который фильтрует некоторые операторы #define из исходного кода, ничего не меняя.

Почему? Это следует использовать для удаления определенного клиентского кода из источников, если источник передается другому клиенту.

Кто-нибудь знает о существующем решении?

Спасибо! Саймон


person Community    schedule 15.09.2009    source источник
comment
Вместо этого я бы переместил клиентский код в его собственный заголовок. Можно запустить только препроцессор, но он все расширит.   -  person Pete Kirkham    schedule 15.09.2009
comment
Это не вариант. Потому что существует много таких определений, и это нарушит логику функций, если они будут перемещены в заголовок. Но спасибо за это предложение!   -  person    schedule 15.09.2009
comment
Я не понимаю, что не так со стандартным препроцессором C. Вы можете уточнить?   -  person MarkJ    schedule 15.09.2009


Ответы (9)


Можно вместо CPP использовать что-то вроде awk? Добавьте в свой код несколько флагов, окружающих удаляемый фрагмент кода. Например:

(...)
//BEGIN_REMOVE_THIS_CODE

printf("secret code");

//END_REMOVE_THIS_CODE
(...)

затем напишите сценарий awk, чтобы удалить этот код, что-то вроде ...

BEGIN { write=1;}
/^\/\/BEGIN_REMOVE_THIS_CODE/ { write=0; next;}
/^\/\/END_REMOVE_THIS_CODE/ { write=1; next;}
    {
    if(write==1) print $0;
    }
person Pierre    schedule 15.09.2009
comment
Всем спасибо, думаю, попробую. - person ; 15.09.2009
comment
Как предлагается, это включает в себя изменение исходного кода, чтобы можно было распознать особые шаблоны. Если код еще не существует (так что вы можете начать с этой схемы), тогда все в порядке. Если ваш код уже существует и использует #ifdef, то я настоятельно рекомендую (настоятельно) использовать один из многих инструментов, которые имеют дело с «XXX всегда определяется» и «YYY всегда не определен». - person Jonathan Leffler; 15.09.2009

Я рекомендую использовать дополнительный уровень макроязыка для фильтрации кода, например filepp. Вы можете использовать удобный для препроцессора синтаксис C, чтобы указать, какие части каким клиентам принадлежат.

//%ifdef CLIENT_A
  code for client A
//%endif

//%ifdef CLIENT_B
  code for client B
//%endif

//%if "CLIENT_A" || "CLIENT_B"
  code for client A and B
//%endif

Префикс '//%' позволяет вам скомпилировать код без изменений. Вы можете запустить filepp перед тем, как передать код клиенту.

person sambowry    schedule 15.09.2009

Это похоже на то, о чем я спрашивал в Есть ли препроцессор C, который удаляет блоки ifdef на основе определенных значений. Лучшим ответом, который я получил, был sunifdef, или «Сын unifdef», который надежно сработал для меня над каким-то излишне искаженным условным кодом (накопленная за более чем 20 лет разработки на самых разных платформах неадекватная теория того, как использовать делать компиляцию для конкретной платформы).

person Jonathan Leffler    schedule 15.09.2009

Не думаю, что для этого нужен препроцессор. Если в вашем коде нет вложенных #ifdef, любой механизм регулярных выражений может удалить все, что находится между #ifdef CLIENT и #endif (используйте нежадное сопоставление для сопоставления первого #endif, а не последнего).

person qrdl    schedule 15.09.2009
comment
Я не думаю, что регулярное выражение - правильный инструмент для этой работы. Это не всегда первый #endif, который вы хотите сопоставить. Если код выглядит как #if CLIENT... #if FOO... #endif... #endif, вы хотите сопоставить второй #endif. Если ifs наоборот, вы хотите сопоставить первый. - person Graeme Perrow; 15.09.2009
comment
@Graeme - вот почему я написал Если у вас нет вложенной части # ifdef. Однако новый движок регулярных выражений Perl поддерживает сопоставление открывающих / закрывающих фигурных скобок, поэтому он может выполнять свою работу даже в случае вложенных #ifdefs. - person qrdl; 15.09.2009
comment
@qrdl - извините, я как-то пропустил эту часть вашего ответа. - person Graeme Perrow; 15.09.2009

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

Поместите вызов функции, который будет заглушен или (я забыл правильный термин) слабо связан, чтобы на его место можно было поместить другую функцию.

person Robert    schedule 15.09.2009

Если вы используете gcc, вы можете использовать:

gcc <insert files here> -E

Параметр -E указывает gcc только на предварительную обработку исходных текстов, а не на их компиляцию.

Или вы можете использовать grep, чтобы отфильтровать определенные файлы и позволить препроцессору работать только с ними.

grep -r '#define CLIENT_CODE' ./*.h 
person aviraldg    schedule 15.09.2009
comment
Примечание. Настоятельно рекомендуется не делать то, что вы говорите, что хотите делать. Предварительная обработка источников может нарушить работу макросов совместимости, если ваш клиент использует другую систему. - person aviraldg; 15.09.2009
comment
Не хотели бы те, кто проголосовал против, оставить комментарий, объясняющий, что не так с этим ответом? Я даю ему +1, так как мне он кажется идеальным. Даже если вы не используете gcc в качестве компилятора, вы все равно можете загрузить его и использовать в качестве препроцессора для этой задачи. - person MarkJ; 15.09.2009
comment
@MarkJ - комментарий Aviraldg объясняет, почему это не идеальное решение. Я сам не отрицал его, но если приложение не будет построено на системе, идентичной той, на которой выполняется предварительная обработка, это может привести в лучшем случае к сбою компиляции, а в худшем - к очень трудным для поиска ошибкам. - person Graeme Perrow; 15.09.2009
comment
@MarkJ: Я не голосовал против, но, возможно, сделал бы, если бы оно было положительным. Проблема в том, что препроцессор C предварительно обрабатывает исходный код и включает содержимое ‹stdio.h› и так далее. Вряд ли это было то, что имел в виду спрашивающий - им нужен источник, за исключением некоторых битов. Итак, я с Грэмом и Авиралдом - это неправильный ответ. - person Jonathan Leffler; 15.09.2009

Вы также можете попробовать unifdef, который проще, чем sunifdef.

person Tony Finch    schedule 18.01.2010

Почему бы тебе не сделать что-нибудь вроде:

client_a_specific_functions_definition.c

double discount_for_paying_upfront() { return 0.1; };
// ...

client_b_specific_functions_definition.c

double discount_for_paying_upfront() { return 0.05; };
// ...

Когда вы раздаете код, это просто вопрос выбора нужного файла с их конкретными определениями.

Затем вы должны создать файл заголовка, чтобы включить его, где вам нужно получить доступ к клиентскому коду, например:

client_functions.h

#pragma once 

double discount_for_paying_upfront();

#define stringify(x) #x
#define FILE2(a) stringify(client_##a##_specific_functions_definition.c)
#define FILE(a) FILE2(a)

#include FILE(CLIENT_NAME)

#undef stringify
#undef FILE2
#undef FILE

Тогда скажите, что вы #include "client_functions.h" в своем main.c. Вы можете скомпилировать его с помощью:

 gcc -DCLIENT_NAME=a main.c -o a.exe
 gcc -DCLIENT_NAME=b main.c -o b.exe
person caiohamamura    schedule 28.10.2019

насколько я знаю ... препроцессор можно запустить как отдельный шаг (используя правильные параметры компилятора). Таким образом, вы можете делать с обработанным кодом все, что захотите.

person Toad    schedule 15.09.2009
comment
Привет, это было первое, о чем я подумал. Но я не нашел простой возможности использовать препроцессор для замены только определенных операторов #define. У вас есть для этого пример? - person ; 15.09.2009
comment
похоже, что aviraldg опередил меня с настройками компилятора и всем остальным. - person Toad; 15.09.2009
comment
Кстати, в старые добрые времена препроцессор был отдельным шагом и отдельным исполняемым файлом. Вполне возможно, что еще можно найти отдельный препроцессор. - person Toad; 15.09.2009
comment
@reiner В некоторых средах это все еще отдельный шаг. GCC, например, не выполняет предварительную обработку - он вызывает cpp для выполнения работы. cpp доступен как отдельный двоичный файл. - person qrdl; 15.09.2009
comment
@reiner: ваш ответ был отклонен, потому что, хотя вы можете запустить препроцессор C отдельно (либо с помощью параметров компилятора, либо как отдельный исполняемый файл), то, что хочет спрашивающий, не то, что делает препроцессор. Например, если код содержит «#include‹ stdio.h ›», вывод все равно должен содержать «#include‹ stdio.h ›», а макросы (такие как getchar ()) следует оставить без изменений. С другой стороны, некоторые фрагменты кода, защищенные с помощью #ifdef CLIENT / remove this / #else / keep this / #endif, должны содержать только секцию вывода 'keep this'. Стандартный препроцессор не может этого сделать. - person Jonathan Leffler; 15.09.2009