После редактирования я пойду на ответ. Требуется, чтобы ваш компилятор поддерживал __FUNCTION__
, что и MSVC, и GCC.
Сначала напишите набор функций, которые отображают строки в целые числа в памяти, все они хранятся в каком-то глобальном экземпляре структуры. Это оставлено в качестве упражнения для читателя, функционально это хэш-карта, но я назову полученный экземпляр «global_x_map». Функция get_int_ptr
определена так, чтобы возвращать указатель на int
, соответствующий указанной строке, и, если она еще не существует, создать ее и инициализировать ее значением 0. reset_int_ptr
просто присваивает 0 счетчику на данный момент позже вы поймете, почему я не просто написал *_inc_x_tmp = 0;
.
#define INC_X do {\
int *_inc_x_tmp = get_int_ptr(&global_x_map, __FILE__ "{}" __FUNCTION__); \
/* maybe some error-checking here, but not sure what you'd do about it */ \
++*_inc_x_tmp; \
} while(0)
#define PRINT_X do {\
int *_inc_x_tmp = get_int_ptr(&global_x_map, __FILE__ "{}" __FUNCTION__); \
printf("%d\n", *_inc_x_tmp); \
reset_int_ptr(&global_x_map, _inc_x_tmp); \
} while(0)
Я выбрал разделитель "{}" на том основании, что он не будет встречаться в искаженном имени функции C - если ваш компилятор по какой-то причине может поместить это в искаженное имя функции, то, конечно, вы должны иметь изменить это. Использование чего-либо, чего нет в имени файла на вашей платформе, также будет работать.
Обратите внимание, что функции, использующие макрос, не являются входящими, поэтому это не совсем то же самое, что определение автоматической переменной. Я думаю, что возможно сделать это вновь входящим, все же. Передайте __LINE__
в качестве дополнительного параметра get_int_ptr
. Когда запись будет создана, сохраните значение __LINE__
.
Теперь карта должна хранить не только int
для каждой функции, но и стек целых. Когда он вызывается с этим первым видимым значением строки, он должен поместить новый int в стек и возвращать указатель на это int после каждого вызова этой функции с любым другим значением строки. Когда вызывается reset_int_ptr
, вместо установки счетчика в 0 он должен вытолкнуть стек, чтобы будущие вызовы возвращали предыдущий int.
Конечно, это работает только в том случае, если «первый» вызов INC_X всегда одинаков, вызывается только один раз за выполнение функции, и этот вызов не появляется в той же строке, что и другой вызов. Если он находится в цикле, блоке if()
и т. Д., Он идет не так. Но если он находится внутри блока, то объявление автоматической переменной тоже может пойти не так. Это также работает только в том случае, если всегда вызывается PRINT_X (проверьте выходы из-за ранней ошибки), иначе вы не восстановите стек.
Все это может показаться сумасшедшим количеством инженерных разработок, но, по сути, это то, как Perl реализует динамически изменяемые переменные: у него есть стек для каждого имени символа. Разница в том, что, как и в C ++ с RAII, Perl автоматически извлекает этот стек при выходе из области видимости.
Если вам нужно, чтобы он был как потокобезопасным, так и повторно входящим, то сделайте global_x_map
thread-local вместо global.
Edit: этот идентификатор __FILE__ "{}" __FUNCTION__
по-прежнему не является уникальным, если в заголовочных файлах определены статические функции - разные версии в разных TU будут использовать один и тот же счетчик в версии без повторного входа. В реентрирующей версии все нормально, хотя, я думаю. У вас также будут проблемы, если __FILE__
- это базовое имя, а не полный путь, поскольку вы можете получить коллизии для static
функций с одинаковыми именами, определенных в файлах с одинаковыми именами. Это scuppers даже вернувшаяся версия. Наконец, ничего из этого не проверено.