C++14 Метапрограммирование: автоматическое построение списка типов во время компиляции / инициализации

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

Как пример, у меня есть что-то вроде этого (см. Это на 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 потому что это означает, что я должен поддерживать этот список вручную, когда мне кажется, что я должен быть в состоянии сказать компилятору в объявлении класса или вокруг него: "Добавить этот класс в свой рабочий список". (Моя конечная цель - автоматизировать регистрацию фабрики, а это недостающий механизм.)

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

3 ответа

Решение

Для этого во время компиляции потребуется метапрограммирование с сохранением состояния. В этой статье Филип Розен объясняет, как реализовать следующее с использованием чрезвычайно продвинутого 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, см. Видео и слайды на слайде № 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>
{};

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

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

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

Другие вопросы по тегам