Устранение рекурсивного создания экземпляров шаблонов в C ++

Я хочу определить макрос, который можно вызывать в разных местах (в области видимости файла), чтобы создавать функции, которые что-то делают. (В приведенном ниже примере функции просто распечатывают сообщение, но, конечно же, мое настоящее намерение состоит в том, чтобы сделать другие полезные вещи.) Проблема в том, что мне нужна некоторая функция «менеджера» (в моем примере это будет просто 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

, как и ожидалось.

У этого решения есть несколько недостатков.

  1. Может ' t вызвать REGISTER дважды в одной строке.
  2. Будет прервано, если используется #line .
  3. Необходимо установить диспетчер после всех вызовов REGISTER .
  4. Увеличенное время компиляции из-за рекурсивных экземпляров.
  5. Если все "фиктивные" экземпляры f не встроены, глубина стека вызовов во время выполнения будет такой же большой, как и количество строк между START_REGISTRATION; и f <__LINE__> (); в диспетчере.
  6. Раздутый код: если только "фиктивные" экземпляры f все они встроены, количество экземпляров также будет таким же большим.
  7. Чрезмерная глубина рекурсии создания экземпляров, вероятно, превысит предел компилятора (500 по умолчанию в моей системе). [12 199] Вопросы 1-4 Я не особо возражаю. Проблему 5 можно устранить, если каждая функция будет возвращать указатель на предыдущую, а менеджер будет использовать эти указатели для итеративного вызова функций вместо того, чтобы они вызывали друг друга. Проблема 6 может быть устранена путем создания аналогичной конструкции шаблона класса, которая способна вычислять для каждого вызова 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();
    }
    
10
задан Ari 6 June 2011 в 07:35
поделиться