2016-01-18 5 views
2

Я думал о том, как создать список всех классов, которые основаны на базовом классе шаблона.C++ Скомпилировать временный список подклассов класса

Прежде всего я хочу, чтобы иметь класс шаблон базы:

template <typename T> 
class Base 
{ 
public: 
    Base() {}; 
    virtual ~Base() {}; 
}; 

и класс, который наследуется от базового класса шаблона:

class Foo : public Base<Foo > 
{ 
public: 
    Foo() {}; 
    virtual ~Foo() {}; 
}; 

Там может быть любое количество других подклассов, как Foo. Результат должен выглядеть как-то:

std::vector<std::string> allTemplates = Base<Base>::getAllTemplateClasses(); 

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

Раньше я думал в разных направлениях. Сначала я подумал, что можно использовать constexpr. Как и каждый класс ребенок нуждается в статической функции с подписью:

constexpr static std::string name() { "Foo";} 

Или подумал, что это возможно с мета-программирования и создания компиляции списка времени, как в примере A Compile Time Data Structure Using,Template-Meta. Проблема здесь в том, что я не знаю заголовка для создания шаблона.

Далее я думал о том, чтобы использовать макросы и строить структуру перечислений, как этот путь Enum structs expanding. Поэтому я не могу найти решение этой проблемы, я хочу спросить вас, возможно ли это вообще?

Edit:

Чтобы сказать ясно: я хочу иметь список дочерних объектов, без необходимости их создания.

+0

Хорошо, я удалил свой ответ, потому что он не совсем работает. Часть, которая отсутствует, описывается здесь: http://stackoverflow.com/questions/5803953/static-constructor-in-c '// C++ должен определять статические элементы извне. has_static_constructor :: constructor has_static_constructor :: cons; 'Объект' registrar' фактически не строился в статической инициализации, потому что, когда он появляется в классе шаблона, это только объявление, а не определение. Я все еще думаю, что эта статическая регистрация - это путь, но вам нужно сделать что-то уродливое с помощью макроса afaik. –

+0

(Точка макроса была бы, когда вы объявляете класс дочерним по отношению к этому базовому классу, вы также хотите, чтобы за пределами определения класса было объявлено объект типа «статический» типа регистратора, конструктор которого регистрирует его имя, и макрос должен помочь убедиться, что эти две вещи происходят вместе. Но idk, если это достаточно хорошее решение, которое стоит писать в любом случае.) –

+0

Как дополнительный комментарий - мне было бы интересно, если есть элегантный способ сделать это, но сейчас я бы не стал спорить, что на самом деле это один. –

ответ

3

С этим хорошим сообщением static constructor и уже ответом, который был подобен этому, я смог найти решение. Магия - это статические конструкторы. Сначала я должен создать контейнер, который содержит подкласс и добавить подклассы:

//base.h 
std::set<std::string> &get_objects(); 
void add_object(const char *name); 

И реализации:

// base.cpp 

std::set<std::string> &get_objects() 
{ 
    static std::set<std::string> theSet; 
    return theSet; 
} 

void add_object(const char *name) 
{ 
    get_objects().emplace(name); 
} 

Так что теперь мы должны создать статический класс, который добавляет строки в список. Это похоже на пост static constructor:

//base.h 
class StaticClassType { 
public: 
    StaticClassType(const char *name) { 
     // Notify when the static member is created 
     add_object(name); 
    } 
}; 

Базовый класс является классом шаблона, который создает статический объект «StaticClassType». C++ гарантировал, что статическая инициализация выполняется до вызова main().

//base.h 
template<typename T> 
class Base { 
protected: 
    // Static member in a template class 
    static StaticClassType m; 
    Base() 
    { 
     (void)m; 
    } 
}; 

Без следующего подряд m не получает createad:

template<typename T> 
StaticClassType Base<T>::m = StaticClassType(typeid(T).name()); 

Теперь мы можем создать два класса, и главное:

class TestClass1 : public Base<TestClass1> { 
public: 
    TestClass1() :Base() {} 
}; 

class TestClass1 : public Base<TestClass1> { 
public: 
    TestClass1() :Base() {} 
}; 

int main() 
{ 
    std::set<std::string> &test = get_objects(); 
    for(auto str : test) 
     std::cout << str.c_str() << std::endl; 
    return 0; 
} 

Выход без строительства какого-либо объекта :

class TestClass1 
class TestClass2 

Есть один, о котором стоит подумать. Мы должны использовать m. В противном случае компилятор оптимизирует код и удаляет m. Я заставляю это поведение сочинительства вызова конструктора:

TestClass1() :Base() {} 

Надеется, вам это нравится, это на самом деле не во время компиляции, но у нас есть список объектов, не делая ничего от сочинительства конструктора и использовать базу в качестве родительского класса.

Смежные вопросы