2017-02-06 14 views
4

Использование C++ 14 и некоторая комбинация Curiously Recurring Template Pattern (CRTP) и, возможно, Boost.Hana (или boost::mpl, если хотите), могу ли я построить список типов во время компиляции (или статическое время инициализации) без явного объявления?C++ 14 Метапрограммирование: автоматически создавать список типов во время компиляции/инициализации

В качестве примера, у меня есть что-то вроде этого (см это на Coliru):

#include <iostream> 
#include <boost/hana/tuple.hpp> 
#include <boost/hana/for_each.hpp> 

namespace 
{ 
    struct D1 { static constexpr auto val = 10; }; 
    struct D2 { static constexpr auto val = 20; }; 
    struct D3 { static constexpr auto val = 30; }; 
} 

int main() 
{ 
    // How to avoid explicitly defining this? 
    const auto list = boost::hana::tuple< D1, D2, D3 >{}; 

    // Do something with list 
    boost::hana::for_each(list, [](auto t) { std::cout << t.val << '\n'; }); 
} 

Я хочу, чтобы избежать явного списка типов - D1, D2 и D3 - в создание list, потому что это означает, что я должен поддерживать этот список вручную, когда мне кажется, что я должен сказать компилятору в объявлении класса или вокруг него: «Добавьте этот класс в свой список». (Моя конечная цель - автоматизировать регистрацию на заводе, и это недостающий механизм.)

Могу ли я сделать это с помощью какого-либо наследования и/или метапрограммирования для составления списка во время компиляции или статического времени инициализации?

+0

http://b.atch.se/ приходит на ум, - но Комитет пытается исправить лазейку, которая позволяет такие трюки, поэтому вы, вероятно, не должны этого использовать. – Quentin

ответ

1

Чтобы сделать это во время компиляции, потребуется метапрограммирование «с точки зрения состояния». В этой статье here, Filip Roséen объясняет, как реализовать следующее, используя чрезвычайно передовые C++ 14:

LX::push<void, void, void, void>(); 
LX::set<0, class Hello>(); 
LX::set<2, class World>(); 
LX::pop(); 

LX::value<> x; // type_list<class Hello, void, class World> 

Кроме того, Мэтт Калабрезе использовали подобные методы реализует семантические на основе концепций в C++ 11 см video и slides на слайде # 28.

Конечно, эти методы полагаются на компилятор, поддерживающий поиск двухфазного имени.

В качестве альтернативы вы можете зарезервировать свой код для поддержки регистрации времени выполнения, что намного проще и может работать переносимо для компиляторов, таких как MSVC. Это то, что используют библиотеки, такие как Prove или args. Он использует общий auto_register класс:

template<class T, class F> 
int auto_register_factory() 
{ 
    F::template apply<T>(); 
    return 0; 
} 

template<class T, class F> 
struct auto_register 
{ 
    static int static_register_; 
    // This typedef ensures that the static member will be instantiated if 
    // the class itself is instantiated 
    typedef std::integral_constant<decltype(&static_register_), &static_register_> static_register_type_; 
}; 

template<class T, class F> 
int auto_register<T, F>::static_register_ = auto_register_factory<T, F>(); 

Тогда вы можете написать свой собственный класс CRTP:

struct foo_register 
{ 
    template<class T> 
    static void apply() 
    { 
     // Do code when it encounters `T` 
    } 
}; 

template<class Derived> 
struct fooable : auto_register<Derived, foo_register> 
{}; 
+0

CRTP бит - это волшебство, которое я искал. Благодаря! – metal

+0

Вот полная демонстрация [этой техники в действии] (http://coliru.stacked-crooked.com/a/de4467d26dd02b42). Я не мог заставить эту автоматическую регистрацию работать так, как написано, потому что «static_register_» оптимизировался, поэтому это немного изменилось. Он работает на Clang и GCC. – metal

+0

@metal Проблема заключается в том, что шаблонный шаблон никогда не может быть создан. Поэтому добавление конструктора по умолчанию или обращение к функции в базовом классе (т. Е. 'This-> register_') - это еще один способ заставить его. –

4

Похоже, вы хотите получить кортеж для компиляции всех типов в пространстве имен или в другой области. Для этого вам понадобится статическое отражение, которое еще не добавлено в C++ (но было бы очень полезно, как вы обнаружили). Вы можете прочитать one proposal for static reflection here и N4428 proposal here.

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

+0

Компилятор не должен автоматически открывать список самостоятельно. Я хочу помочь, отметив каждый триггер (например, наследуя от класса политики или некоторого CRTP). Вещь, которую я пытаюсь избежать, заключается в том, чтобы вручную сохранить список классов, которые автоматически зарегистрированы. – metal

+0

Я мог бы сделать что-то вроде [это с Boost.Preprocessor] (http://stackoverflow.com/a/19630812/201787). Увы, я надеялся на что-то более элегантное. – metal

+0

Да, вы можете наследовать от класса CRTP, который добавляет ваш класс в статический список, но вы довольно быстро запускаете проблемы с порядком инициализации и проблемы с заданием. – thirtythreeforty

1

Единственный способ, которым я знаю это прямо сейчас, - это метапрограммирование с сохранением состояния, как описано here. Но это сложно, трудно реализовать, и комитет пытается исключить это из-за недействительности.

+0

[Вот удар по тому, что я пытался сделать с CRTP] (https://sourceforge.net/p/loki-lib/discussion/93010/thread/bf08774d/), но он не работает на более поздних компиляторах , Я надеялся на дальнейшее волшебство, и C++ arcana смогла преодолеть разрыв. – metal

+1

CRTP позволит вам регистрировать типы при статическом времени инициализации, а не во время компиляции. Это может быть или не быть достаточно для вас. –

+0

Хороший вызов: я не различал статический init и время компиляции, но должен был иметь. Статического времени начала должно быть достаточно в моем случае, но тем не менее точный механизм ускользает от меня. Я уточнил формулировку в моем вопросе. – metal