Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Can I declare a template specialization from a typelist?

Pretty sure I know the answer to this already, but it's worth a shot.

So, say I have a typelist:

template <typename ...Ts>
struct typelist{};

That contains some objects:

struct foo{};
struct bar{};
struct quux{};

using objects = typelist<foo, bar, quux>;

Now I have a templated class (baz) that can take any of these objects. But, due to codebase size and compilation times, I want to have the implementation of my templated method in a cpp file.

So at the bottom of baz.cpp I have:

template <> class baz<foo>;
template <> class baz<bar>;
template <> class baz<quux>;

The problem is I have lots of classes like baz, and the list of objects that they work with is also ever changing. So... is there anyway I can keep my single typelist of objects and use that in the cpp file of each baz-like object to specialize? Then, all I have to do is update my typelist when I have a new object and all the object files will rebuild.

like image 809
Sam Kellett Avatar asked Sep 07 '15 18:09

Sam Kellett


People also ask

Can you partially specialize a C++ function template?

You can choose to specialize only some of the parameters of a class template. This is known as partial specialization. Note that function templates cannot be partially specialized; use overloading to achieve the same effect.

Are template specializations inline?

An explicit specialization of a function template is inline only if it is declared with the inline specifier (or defined as deleted), it doesn't matter if the primary template is inline.

What is a template specialization?

The act of creating a new definition of a function, class, or member of a class from a template declaration and one or more template arguments is called template instantiation. The definition created from a template instantiation is called a specialization.

Can template class inherit?

Inheriting from a template classIt is possible to inherit from a template class. All the usual rules for inheritance and polymorphism apply. If we want the new, derived class to be generic it should also be a template class; and pass its template parameter along to the base class.


2 Answers

The template <> class baz<foo>; line forward-declares a specialization and not a template instantiation, which, I assume, is what you want.

I don't think there's a direct way to do this, you'll have to do some metaprogramming. You can use Boost.Preprocessor to generate all the needed code:

#define TYPES (foo)(bar)(quux)

using objects = typelist< BOOST_PP_SEQ_ENUM(TYPES) >;

// Generate extern template declarations in the header
#define EXTERN_TEMPLATE_BAZ(r, data, arg)\
    extern template class baz< arg >;

BOOST_PP_SEQ_FOR_EACH(EXTERN_TEMPLATE_BAZ, _, TYPES)

// Generate template instantiations in the .cpp
#define TEMPLATE_BAZ(r, data, arg)\
    template class baz< arg >;

BOOST_PP_SEQ_FOR_EACH(TEMPLATE_BAZ, _, TYPES)

There may be a way to do this without preprocessor but doing this would impose additional requirements on the baz type. The point is to use the type in a context where it has to be instantiated, including all its methods.

like image 52
Andrey Semashev Avatar answered Oct 10 '22 09:10

Andrey Semashev


I'm pretty sure this is impossible without using the preprocessor. You may be able to reconstruct the template argument pack from an argument, but you have to actually pass an instance of the argument, which seems sub-optimal. Second, explicit template instantiations are not allowed at block scope (i.e. in a template function), so there is no way to write a template that explicitly instantiates another template.

As Nir indicates, why don't you just use an X Macro?

#define MY_FOREACH_TYPES(func, ...) \
  func(type1, ##_VA_ARGS__) \
  func(type2, ##_VA_ARGS__) \

#define MY_INSTANTIATE(Type, Class) \
  template <> class Class<Type>;

MY_FOREACH_TYPES(MY_INSTANTIATE, bar)

Now just update MY_FOREACH_TYPES when your type list changes.

like image 23
Matthew Avatar answered Oct 10 '22 11:10

Matthew