Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Ignoring duplicate explicit instantiations of template classes in C++

If I have a class:

template <typename T>
class MyClass
{ 
 // ...
};

and I explicitly instantiate it:

template class MyClass<int>;
template class MyClass<int>; // second time

I get an error on some compilers (Clang for example, but not on VC++ 2010). Why would I want to do this? Well, in some cases T may be a typedef to another type.

template class MyClass<my_type_1>;
template class MyClass<my_type_2>;

With certain build options, my_type_1 is the same as my_type_2 and in other cases it is different. How would I make sure that the above compiles in all scenarios? Is there a way to ignore the duplicate instantiation?

like image 369
Samaursa Avatar asked Jun 18 '13 17:06

Samaursa


3 Answers

You could find another way to explicitly instantiate the template in a way that you can do metaprogramming on it.

Then instead of doing one instantiation per line, do them all in a pack. Run an n^2 algorithm on them (at compile time) to eliminate duplicates (or, honestly, you could probably skip that: depending on how you instantiate the template, it might not care).

Something like this, assuming Instantiate< Template, types< blah, foo, bar > > actually instantiates the list on the template passed in as the first argument:

#include <utility>
#include <type_traits>

template<typename T>
struct Test {};

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

template<template<typename>class Template, typename Types>
struct Instantiate {};

template<template<typename>class Template, typename T0, typename... Ts>
struct Instantiate<Template, types<T0, Ts...>>:
  Instantiate<Template, types<Ts...>>
{
  Template<T0>& unused();
};

template<typename U, typename Types>
struct prepend;

template<typename U, template<typename...>class pack, typename... Ts>
struct prepend< U, pack<Ts...> > {
  typedef pack<U, Ts...> types;
};
template<typename U, typename Types>
using Prepend = typename prepend<U, Types>::types;

template<typename U, typename Types, typename=void>
struct remove_type_from_types;
template<typename U, template<typename...>class pack>
struct remove_type_from_types<U, pack<>, void>
{
  typedef pack<> types;
};

template<typename U, template<typename...>class pack, typename T0, typename... Ts>
struct remove_type_from_types< U, pack<T0, Ts...>,
  typename std::enable_if< std::is_same<U, T0>::value >::type
>: remove_type_from_types< U, pack<Ts...> >
{};

template<typename U, template<typename...>class pack, typename T0, typename... Ts>
struct remove_type_from_types< U, pack<T0, Ts...>,
  typename std::enable_if< !std::is_same<U, T0>::value >::type
>
{
  typedef Prepend< T0, typename remove_type_from_types< U, pack<Ts...> >::types > types;
};

template<typename Types>
struct remove_duplicates {
  typedef Types types;
};

template<template<typename...>class pack, typename T0, typename... Ts>
struct remove_duplicates<pack<T0, Ts...>> {
private:
  typedef typename remove_type_from_types< T0, pack<Ts...> >::types filtered_tail;
  typedef typename remove_duplicates< filtered_tail >::types unique_tail;
public:
  typedef Prepend< T0, unique_tail > types;
};
template<typename Types>
using RemoveDuplicates = typename remove_duplicates<Types>::types;

static Instantiate<Test, RemoveDuplicates<types<int, double>> > unused;

int main() {

}

As noted, you can probably do away with the entire eliminate-duplicates bit, because of how I'm instantiating the use of the template. I am also not sure if the above use of each template is sufficient to instantiate it (ie, that it won't be optimized away somehow, and that the symbol will be exported).

(Recursion depth is n in the number of types, and total work done is n^2 in the number of types: that is shallow enough and fast enough for any reasonable number of types, I suspect. Fancier unique type removal is difficult, due to lack of weak ordering on naked types...)

like image 155
Yakk - Adam Nevraumont Avatar answered Nov 01 '22 05:11

Yakk - Adam Nevraumont


Don't specialize for the typedefs, instead specialize for the relevant underlying types (such as int). That way you can typedef as many/few times as you like and you still always get the specializations you want.

like image 21
Mark B Avatar answered Nov 01 '22 07:11

Mark B


You can define a preprocessor flag for you configuration and then put template inside an #ifdef block.

like image 2
catscradle Avatar answered Nov 01 '22 05:11

catscradle