I'm writing a C++ library that contains a lot of function templates I want to explicitly instantiate and export for several type parameters. In my particular case, I have a lot of numeric function templates that I want to separately instantiate and compile for float
, double
, and long double
. They look something like this:
template <typename T>
T calculate_a(T x) { ... }
template <typename T>
T calculate_b(T x, T y) { ... }
// ...
If I have M function templates and N underlying types, then I have M*N explicit instantiations to type out. Is it possible to write these instantiations more concisely?
My current solution is to use a preprocessor macro that performs all instantiations for a given type:
#define EXPLICITLY_INSTANTIATE(T) \
template T calculate_a<T>(T x); \
template T calculate_b<T>(T x, T y); \
// ...
EXPLICITLY_INSTANTIATE(float);
EXPLICITLY_INSTANTIATE(double);
EXPLICITLY_INSTANTIATE(long double);
However, this is suboptimal because it requires me to separately maintain another copy of each function template's signature. Also, if I want to do this in multiple translation units, then I need to separately maintain a list of underlying types in each. (Suppose that C++2a adds a long long double
type that I want to support; I'll have to add EXPLICITLY_INSTANTIATE(long long double);
to every file.)
Another possible approach is to gather up all of my functions into a (static-only) template class:
template <typename T>
class calculate {
T a(T x) { ... }
T b(T x, T y) { ... }
};
template class calculate<float>;
template class calculate<double>;
template class calculate<long double>;
This solves the first problem of separately maintaining two copies of each signature, but requires me to change each call of calculate_a
into calculate::a<T>
. It doesn't address the second problem.
A function template starts with the keyword template followed by template parameter(s) inside <> which is followed by the function definition. In the above code, T is a template argument that accepts different data types ( int , float , etc.), and typename is a keyword.
Function templates are special functions that can operate with generic types. This allows us to create a function template whose functionality can be adapted to more than one type or class without repeating the entire code for each type.
A template is a simple yet very powerful tool in C++. The simple idea is to pass data type as a parameter so that we don't need to write the same code for different data types. For example, a software company may need to sort() for different data types.
Which one is suitable syntax for function template? Explanation: Both class and typename keywords can be used alternatively for specifying a generic type in a template.
You can avoid repeating function signatures by instantiating templates via taking their addresses:
// forward declarations in a header file
template<typename T>
T square(T num);
template<typename T>
T add(T left, T right);
// implementations and instantiations in a single implementation file
template<typename T>
T square(T num) {
return num * num;
}
template<typename T>
T add(T left, T right) {
return left + right;
}
// instantiations for specific types
#include <tuple>
template<typename... Ts>
auto instantiate() {
static auto funcs = std::tuple_cat(std::make_tuple(
add<Ts>,
square<Ts>
)...);
return &funcs;
}
template auto instantiate<int, double>();
The overhead here is a single array of pointers to all instantiated functions, example on godbolt.
This is what X Macros are made for. It works quite simply.
You have a file that contains all of the types you want to apply this to. Let's call it "type_list.inc". It would look like this:
X(float)
X(double)
X(long double)
When you want to perform some operation over that list of types, you #include
the file, but around the point of inclusion, you #define
the macro X
to do the operation you want to perform:
#define X(T) \
template T calculate_a<T>(T x); \
template T calculate_b<T>(T x, T y); \
// ...
#include "type_list.inc"
#undef X
You still have to maintain two sets of function prototypes. But you only need to maintain one list of types.
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