C препроцессор или C ++ magi c для автоматического создания объекта на файл? - PullRequest
2 голосов
/ 02 марта 2020

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

В качестве примера, который бы удовлетворял эту потребность, я мог бы написать Function() как:

FunctionModifiable( const char* pszFile, int i );

Затем создайте макрос таким образом:

#define Function( i ) FunctionModifiable( __FILE__, (i) )

И FunctionModifiable() будет обязан проверять pszFile, скажем, unordered_set<>, который был заполнен во время инициализации, чтобы увидеть, является ли специальным функциональность должна быть активирована.

Однако издержки этого поиска - минус (это высокопроизводительное программное обеспечение, и функция вызывается огромное количество раз), и есть некоторые данные для каждого файла, которые могли бы нужно кэшировать в этой ситуации. Мы можем исключить поиск и получить хранилище для кэшированной информации, передав не __FILE__, а указатель на вспомогательный объект. Этому объекту нужно имя файла, чтобы при однократной инициализации он мог обращаться к переменным конфигурации или окружения или узнать, требуется ли ему специальная обработка.

FunctionHelperObject fho( __FILE__ );

#define Function( i ) FunctionModifiable( &fho, (i) )  // C-style solution
#define Function( i ) fho.MethodModifiable( (i) )        // C++-style solution

ОК, теперь скажите, что я хочу чтобы пользователи не могли определить это fho в каждом файле. (В частности, мы не можем переписать все существующие файлы с вызовом Function (), хотя и говорим, что готовы их перекомпилировать).

У меня была необычная идея поместить определение переменной в заголовочный файл , так что любая программа, включающая заголовок для Function(), получит FunctionHelperObject fho( __FILE__ ) бесплатно. (Такое определение будет #pragma once или защищено переменной препроцессора.

Проблема заключается в том, что __FILE__ в этой точке будет именем заголовка, а не модуля компиляции верхнего уровня. Если есть был символом __CFILE__, это было бы решением, но это не так.

В конечном счете, лучшее, что я могу придумать, имеет недостатки: 1) аспект «модифицируемый» будет доступен только в исходном коде, явно написанном для используйте его, и 2) вам придется сделать это явное написание, и 3) начать становиться немного сложнее. В коде вы хотите добавить возможность изменять поведение, чтобы вы написали USE_MODIFIABLE_FUNCTION где-нибудь после включения соответствующего заголовка. Это был бы макрос, который создает FunctionHelperObject выше, на этот раз в правильном «файле», поэтому __FILE__ будет иметь требуемое значение, и, кроме того, определяет макрос, как показано выше, который будет маскировать ненастраиваемую функцию Function() с одним из двух макросов, показанных выше. Вкратце: предыдущий пример, плюс

#define USE_MODIFIABLE_FUNCTION FunctionHelperObject fho( __FILE__ );\n#define Function( i ) fho.MethodModifiable( (i) )

Код, написанный без USE_MODIFIABLE_FUNCTION, просто назвал бы ненастраиваемый Function() нормальным способом.

Но, безусловно, это другой принятый, переносимый способ обеспечить такое поведение? Хотя я говорил исключительно о препроцессоре C, есть ли какой-нибудь шаблон C ++ magi c или какой-либо другой подход, который будет работать?

Ответы [ 2 ]

2 голосов
/ 03 марта 2020

Кэшируйте результат.

// in the header with Function macro
static FunctionHelperObject functionhelper;
static inline void FunctionModifiableInterface(const char *file, int i) {
    static initialized = 0;
    if (initialized == 0) {
        initialized = 1;
        functionhelper = FunctionHelperObject(file);
    }
    FunctionModifiable(&functionhelper, i);
}
#define Function(i) FunctionModifiableInterface(__FILE__, (i))

Вы не можете предсказать, куда пользователь захочет позвонить вам Function(i), поэтому вы не можете предсказать значение __FILE__. Просто инициализируйте его при первом вызове, что также здорово, потому что вы не инициализируете его, если Function не вызывается. Вы можете сделать такую ​​же проверку initialized внутри FunctionHelperObject конструктора.

Действительно крутой и сложный трюк состоит в том, чтобы изменить систему сборки, чтобы позволить вам передавать макрос с именем файла скомпилированного C файл. Поскольку системы сборки компилируют один C файл за раз, это возможно (и это позор, что компиляторы сами этого не делают). Если вы используете cmake с make бэкэндом (или на самом деле просто make сам по себе), вы можете сделать что-то , подобное этому :

set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -D__MY_FILE__='\"$(notdir $(abspath $<))\"'")

И затем использовать FunctionHelperObject fho(__MY_FILE__) Как вы и хотели, потому что __MY_FILE__ зависит только от имени выходного файла make.

1 голос
/ 03 марта 2020

Один из вариантов выглядит примерно так (грубый пример, сохраняя синтаксис C ++ из OP):

#define Function(i) do { \
    static FunctionHelperObject obj(__FILE__); \
    FunctionModifiable(&obj, (i)); \
} while (0)

Обратите внимание, что вышеприведенное придется изменить, чтобы приспособиться к возвращаемому значению, если функция имеет один .

Также: альтернативой __FILE__ может быть __func__, если она лучше соответствует вашим потребностям.

...