Using C++14 and some combination of the Curiously Recurring Template Pattern (CRTP) and possibly Boost.Hana (or boost::mpl
if you wish), can I build a list of types at compile time (or static initialization time) without an explicit declaration?
As an example, I have something like this (see it on 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'; } );
}
I want to avoid the explicit list of types -- D1
, D2
, and D3
-- in the creation of list
because it means I have to maintain that list manually when it seems like I should be able to tell the compiler in or around the class declaration, "Add this class to your running list". (My ultimate aim is to automate factory registration, and this is the missing mechanism.)
Can I do this using some inheritance and/or metaprogramming trickery to compose the list at compile-time or static init time?
To do it at compile-time will require "stateful" metaprogramming. In this article here, Filip Roséen explains how to implement the following using extremely advanced 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>
Also, Matt Calabrese used similar techniques to implements semantic-based concepts in C++11, see the video and slides at slide #28.
Of course, these techniques rely on a compiler supporting conformant two-phase name lookup.
Alternatively, you can restucture your code to support runtime registration instead, which is much simpler, and can work portably across compilers such as MSVC. This is what libraries such as Prove or args use. It uses a generic auto_register
class:
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>();
Then you can write your own CRTP class:
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>
{};
It sounds like you want to get a compile-time tuple of all types in a namespace or other scope. To do that, you would need static reflection, which has not yet been added to C++ (but would be very useful as you have discovered). You can read one proposal for static reflection here, and the N4428 proposal here.
As a workaround, you could write a macro to simultaneously define the type and implicitly add it to the registry during static initialization.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With