Я хочу определить макрос, который можно вызывать в разных местах (в области видимости файла), чтобы создавать функции, которые что-то делают. (В приведенном ниже примере функции просто распечатывают сообщение, но, конечно же, мое настоящее намерение состоит в том, чтобы сделать другие полезные вещи.) Проблема в том, что мне нужна некоторая функция «менеджера» (в моем примере это будет просто main ( )
), чтобы каким-то образом преуспеть в их вызове (в любом порядке) без зависимости кода от макросов (за исключением, конечно, самих макросов). Я имею в виду, что как только файл будет написан, другой программист сможет просто вставить несколько новых вызовов макросов в разных местах или удалить некоторые из существующих вызовов, и код по-прежнему будет работать без каких-либо дальнейших изменений. Я понимаю, что это можно сделать с помощью статических объектов, но я хочу изучить другой подход. Я собираюсь использовать некоторые хитрости шаблонов и тот факт, что __ LINE __
монотонно увеличивается.
#include <iostream>
using namespace std;
template<int i>
inline void f()
{
f<i-1>();
}
#define START_REGISTRATION \
template<> \
inline void f<__LINE__>() {} /* stop the recursion */ \
template<> void f<__LINE__>() /* force semicolon */
#define REGISTER(msg) \
template<> \
inline void f<__LINE__>() \
{ \
cout << #msg << endl; \
f<__LINE__ - 1>(); \
} \
template<> void f<__LINE__>() /* force semicolon */
// Unrelated code ...
START_REGISTRATION;
// Unrelated code ...
REGISTER(message 1);
// Unrelated code ...
REGISTER(message 2);
// Unrelated code ...
REGISTER(message 3);
// Unrelated code ...
// manager function (in this case main() )
int main()
{
f<__LINE__>();
}
Это печатает
message 3
message 2
message 1
, как и ожидалось.
У этого решения есть несколько недостатков.
REGISTER
дважды в одной строке. #line
. REGISTER
. f
не встроены, глубина стека вызовов во время выполнения будет такой же большой, как и количество строк между START_REGISTRATION;
и f <__LINE__> ();
в диспетчере. f
все они встроены, количество экземпляров также будет таким же большим. REGISTER
, какая функция была создана в предыдущем вызове, и, таким образом, создавать экземпляры только тех функций, которые действительно что-то делают. Избыточное создание экземпляров затем будет перенесено из шаблона функции в шаблон класса, но создание экземпляров класса будет облагать налогом только компилятор; они не будут запускать генерацию кода. Поэтому меня действительно беспокоит проблема 7, и вопрос в том, есть ли способ реструктурировать вещи так, чтобы компилятор выполнял экземпляры итеративно, а не рекурсивно. Я также открыт для различных подходов (кроме тех, которые связаны со статическими объектами). Одно простое решение - сгруппировать все регистрации вместе прямо перед менеджером (или добавить макрос STOP_REGISTRATION
, чтобы завершить блокировку регистраций), но это нарушит значительную часть моей цели (регистрация вещей рядом с кодом, который определяет это).
Изменить: Было несколько интересных предложений, но, боюсь, я не ясно выразился в терминах того, чего я надеюсь достичь. Меня действительно интересуют две вещи: решение проблемы в том виде, в каком она поставлена (т.е. отсутствие статики, одна строка для каждой регистрации, никаких дополнительных изменений при добавлении / удалении регистраций, и, хотя я не сказал об этом, только стандартный C ++ --- следовательно , без наддува). Как я сказал в комментариях ниже, мой интерес носит скорее теоретический характер: я надеюсь изучить некоторые новые методы. Следовательно, я действительно хотел бы сосредоточиться на реструктуризации вещей, чтобы рекурсия была устранена (или, по крайней мере, уменьшена), или на поиске другого подхода, который удовлетворяет ограничениям, которые я изложил выше.
Редактировать 2: Решение MSalter является большим шаг вперед. Сначала я думал, что каждая регистрация повлечет за собой полную стоимость строк до нее, но потом я понял, что, конечно, функция может быть создана только один раз, поэтому с точки зрения создания экземпляров мы платим ту же цену, что и при линейном поиске, но глубина рекурсии становится логарифмической. Если я доберусь до этого, я опубликую полное решение, устраняющее проблемы 5-7. Тем не менее, было бы неплохо посмотреть, можно ли это сделать с постоянной глубиной рекурсии, а лучше всего, с количеством экземпляров, линейным по количеству вызовов (а-ля ускоренное решение).
Изменить 3: Вот полное решение.
#define START_REGISTRATION \
template<int lo, int hi> \
struct LastReg { \
enum { \
LINE_NUM = LastReg<(lo + hi)/2 + 1, hi>::LINE_NUM ? \
static_cast<int>(LastReg<(lo + hi)/2 + 1, hi>::LINE_NUM) : \
static_cast<int>(LastReg<lo, (lo + hi)/2>::LINE_NUM) \
}; \
}; \
template<int l> \
struct LastReg<l, l> { \
enum { LINE_NUM = 0 }; \
}; \
template<int l> \
struct PrevReg { \
enum { LINE_NUM = LastReg<__LINE__ + 1, l - 1>::LINE_NUM }; \
}; \
template<int l> void Register() {} \
template<int l> void Register() /* force semicolon */
#define REGISTER(msg) \
template<> \
struct LastReg<__LINE__, __LINE__> { \
enum { LINE_NUM = __LINE__ }; \
}; \
template<> \
void Register<__LINE__>() \
{ \
cout << __LINE__ << ":" << #msg << endl; \
Register<PrevReg<__LINE__>::LINE_NUM>(); \
} \
template<> void Register<__LINE__>() /* force semicolon */
#define END_REGISTRATION \
void RegisterAll() \
{ \
Register<PrevReg<__LINE__>::LINE_NUM>(); \
} \
void RegisterAll() /* force semicolon */
START_REGISTRATION;
REGISTER(message 1);
REGISTER(message 2);
END_REGISTRATION;
int main()
{
RegisterAll();
}