I am attempting to add some extern template
into my project, to speed up build times and reduce footprint on disk during build.
I've done so by listing specialisations that I use frequently, e.g.
extern template class std::vector<std::string>;
extern template class std::vector<unsigned int>;
extern template class std::vector<uint32_t>;
extern template class std::vector<size_t>;
The problem is that unsigned int
is size_t
, maybe, and uint32_t
is unsigned int
, maybe. So, depending on the build target, compilation fails for the "non-extern
" variety of the list that actually instantiates the specialisations:
template class std::vector<std::string>;
template class std::vector<unsigned int>;
template class std::vector<uint32_t>;
template class std::vector<size_t>;
The error is like this (line and column number inaccurate):
templates-impl.h:15:36: error: duplicate explicit instantiation of ‘class std::vector<long unsigned int>’ [-fpermissive]
template class std::vector<size_t>;
My goal is to just be able to list the types I use, as I use them, with a minimum of fuss and with no need to hardcode variants of the list for different targets.
I guess in C++14 I could solve this with an if constexpr
and some std::is_same
, at least if that were permitted at namespace scope.
How can I do it in C++11?
To instantiate a template function explicitly, follow the template keyword by a declaration (not definition) for the function, with the function identifier followed by the template arguments. template float twice<float>(float original); Template arguments may be omitted when the compiler can infer them.
In order for any code to appear, a template must be instantiated: the template arguments must be provided so that the compiler can generate an actual class (or function, from a function template).
Implicit instantiation means that the compiler automatically generates the concrete function or class for the provided template arguments. In general, the compiler also deduces the template arguments from the function's arguments. In C++17, the compiler can also deduce the template arguments for class templates.
Alternatively, you may have type-lists of to-be-instantiated types, of which only repeating types are replaced with dummy types (hopefully, to be optimized-out at link time).
In C++11, something like:
template<typename... V>
struct explicit_instlist
{
template<int I>
struct get
{
using VI = typename std::tuple_element<I,std::tuple<V...>>::type;
using type = typename std::conditional< is_first_at<I,VI,V...>::value,
VI, dummy_inst<I,VI> >::type;
};
};
template<unsigned I>
using my_list = typename explicit_instlist<
std::string,
unsigned int,
uint32_t,
size_t
>::template get<I>::type;
/*extern*/ template class std::vector< my_list<0> >;
/*extern*/ template class std::vector< my_list<1> >;
/*extern*/ template class std::vector< my_list<2> >;
/*extern*/ template class std::vector< my_list<3> >;
where dummy_inst<I,T>
generates a unique dummy type to be used in place of T when already used, and is_first_at
works like:
template<int I,typename T>
struct dummy_inst {};
template<int I,typename T,typename... V>
struct is_first_at {};
template<int I,typename T,typename V,typename... Vs>
struct is_first_at<I,T,V,Vs...>: std::conditional< std::is_same<T,V>::value,
std::integral_constant<bool,I==0>,
is_first_at<I-1,T,Vs...> >::type {};
clearly, dummy_inst
will need to be specialized if the to be explictly instantiated template does not support the empty default. Something like boost preprocessor may be used to avoid explictly 'iterating' the explicit instantations and write a macro accepting the type list directly ...
How about drop the unsigned int and size_t specializations and just make them all size-based?
extern template class std::vector<uint8_t>;
extern template class std::vector<uint16_t>;
extern template class std::vector<uint32_t>;
extern template class std::vector<uint64_t>;
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