I have a library with a class templated on a size:
foo.hpp
:
template <size_t N>
struct Foo {
void bar();
};
foo.cpp
:
template <size_t N>
void Foo<N>::bar() {
...
};
I want the compiled version to be instatiated for a specific list of sizes which would be defined in the cpp file. In pseudocode, something like this
foo.cpp
:
template <size_t N>
void Foo<N>::bar() {
...
};
for (const auto & size: {1,2,7,9})
template struct Foo<size>;
Right now, I am manually doing this (instatiating for each type), but this is error prone since I have several files that should be defined for the same size.
I am aware that I could move everything to the header file, and use some enable_if
's to choose which sizes are valid. However, I want to hide the implementation details. Hence the current approach.
EDIT:
FWIW, I was hoping to avoid macros. I thought that something like this would work if you want a range of variables (e.g. 1,2,3,4...):
template<template<size_t> class E, size_t I = 1, std::enable_if_t<I >= 5> *unused = nullptr>
bool instatiator() {
return true;
}
template<template<size_t> class E, size_t I = 1, std::enable_if_t<I < 5> *unused = nullptr>
bool instatiator() {
E<I> e;
return instatiator<E, I + 1>();
}
bool unused = instatiator<Encoder>();
but I can't seem to get that working.
You could use an X macro approach for this:
valid_sizes.hpp
// Note: no include guards!!
PROCESS_SIZE(1)
PROCESS_SIZE(2)
PROCESS_SIZE(7)
PROCESS_SIZE(9)
#undef PROCESS_SIZE
foo.cpp
// ...
#define PROCESS_SIZE(n) template struct Foo<n>;
#include "valid_sizes.hpp"
bar.cpp
// ...
#define PROCESS_SIZE(n) some_other_use_of<n>;
#include "valid_sizes.hpp"
Alternatively, instead of a manual list, you could use Boost.Preprocessor:
process_sizes.hpp
#pragma once
#define PROCESS_SIZES(macro, data) BOOST_PP_SEQ_FOR_EACH(macro, data, (1)(2)(7)(9))
some header file
#define INSTANTIATE_CLASS(r, Name, size) template class Name<size>;
foo.cpp
#include "process_sizes.hpp"
// ...
PROCESS_SIZES(INSTANTIATE_CLASS, Foo)
bar.cpp
#include "process_sizes.hpp"
// ...
PROCESS_SIZES(INSTANTIATE_CLASS, Bar)
#define OTHER_SIZE_BASED_WORK(r, data, size) other_use_of<size>;
PROCESS_SIZES(OTHER_SIZE_BASED_WORK, whatever)
Note that explicit instantiation definitions must occur at namespace scope, so it's not possible put them inside a function like your attempted instantiator
.
And also note that an implicit instantiation (such as would be caused by referring to the class in a context which requires it to be defined) is not "visible" outside the translation unit, so it cannot be used as a substitute for explicit instantiation.
In other words, having the same list of sizes do different things requires duplicating the list for each use, using macros, or using custom code generation outside of the C++ compilation pipeline. I consider macros the simplest and most maintainable of these.
In addition to @Angew answer:
xmacros can be in a different manner, allowing include guards and also avoiding having #include
in the middle of the code:
valid_sizes.hpp
#pragma once
#define SIZES_TABLE(OP)\
OP(1) \
OP(2) \
OP(12)
foo.cpp
#include "valid_sizes.hpp"
// ...
#define INSTANTIATE(Size) template struct Foo<Size>;
SIZES_TABLE(INSTANTIATE)
#undef INSTANTIATE
You can recursively instantiate the Foo
class with a helper class. Unfortunately you need to provide a call to each method of Foo
in the constructor (the methods will not be called though), in order to provide the definitions to the linker:
// foo.cpp:
template <size_t S>
struct recursive_helper {
Foo<S> foo;
recursive_helper<S-1> r_helper;
recursive_helper() {
foo.bar();
foo.baz();
}
};
template <>
struct recursive_helper<0> {
Foo<0> foo;
recursive_helper() {
foo.bar();
foo.baz();
};
};
And then you instantiate the helper class:
template struct recursive_helper<6>;
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