Here is a code snippet illustrating my question:
const float D = 0.1F;
const float A[4] = {sin(0*D), sin(1*D), sin(2*D), sin(3*D)};
Imagine that global array A is much longer and you don't want to do all of this repetitive typing. Is there a shorter way to initialize array A at compile or initialization time, i.e. without having to write initialization function and call it somewhere in my program?
You could initialize A during dynamic initialization time as follows:
const float *init_a(float x_)
{
static float data[4];
for(unsigned i=0; i<4; ++i)
data[i]=sin(i*x_);
return data;
}
const float D=0.1f;
const float *A=init_a(D);
You may use code generator to generate initialization code. That is, write program that will write your initialization code for you. You may actually calculate values at generation-time.
Remember that C++ allows placing , after last element. It's also isn't necessary to specify array size. These two things should ease writing of generator.
This simple python code should work well:
from math import sin
print('const float A[', N, '] = {')
for i in range(N):
print('\t', sin(i*D), ',', sep='')
print('};')
Ok I just realized this doesn't actually answer the question, because it specifies "without having to write initialization function and call it somewhere in my program?" But I can't think of a convenient alternative.
template<size_t N>
std::array<float, N> GetLookupTable(float d)
{
std::array<float, N> table;
// .. populate table here
return table;
}
// a global somewhere
(static?) const auto Table_10x5 = GetLookupTable<10>(5.0f);
This first part is obsolete in C++14, but not long:
template<unsigned...>struct indexes { using type=indexes; };
template<unsigned Max, unsigned...Is>struct make_indexes:make_indexes<Max-1,Max-1,Is...>{};
template<unsigned...Is>struct make_indexes<0,Is...>:indexes<Is...>{};
template<unsigned Max>using make_indexes_t=typename make_indexes<Max>::type;
this is some template meta programming that lets us create and pass around bundles of indexes.
Then some code to generate the array:
namespace details {
template<std::size_t N, unsigned...Is>
std::array<float, N> poly_sin(float src, indexes<Is...>) {
return { (Is*sin(src))... };
}
}
template<std::size_t N>
std::array<float, N> poly_sin(float src) {
return details::poly_sin<N>( src, make_indexes_t<N>{} );
}
The first method takes indexes<Is...> and we plan that Is... is 0, 1, 2, ..., N-1. It then expands the parameter pack into a std::array of the right size.
make_indexes_t<N>{} expands (at compile time) to indexes<0, 1, 2, ..., N-1>{}, which is then passed to details::poly_sin, which then can deduce the Is... and use them within itself.
And point of use:
const float D = 0.1F;
const auto A = poly_sin<4>(D);
if you had a constexpr sin function, you could even make poly_sin a constexpr function and have it basically guaranteed to be evaluated at compile time.
If this is the case, make D constexpr and same with the two poly_sin functions.
As written, this occurs at dynamic initialization time.
While it appears that the array is copied twice, RVO elision means that any decent compiler will directly construct it in A.
If you want to be able to do this in general, first start with the above indexes code. Then add this:
template<class Sig>using result_of_t=typename std::result_of<Sig>::type;
namespace details {
template<std::size_t N, class F, unsigned... Is>
std::array< result_of_t< F(unsigned) >, N >
make_array( F&& f, indexes<Is...> ) {
return { f( Is )... };
}
}
template<std::size_t N, class F>
std::array< result_of_t< F(unsigned) >, N >
make_array( F&& f ) {
return details::make_array( std::forward<F>(f), make_indexes_t<N>{} );
}
const auto A = make_array<4>( [](unsigned i){ return float(i*sin(D)); } );
which uses a lambda to pass in the code that is repeated to build the array. Sadly, lambdas are not by default constexpr so you cannot do it at compile time.
You could either use boost.preprocessor and in particular the BOOST_PP_ENUM macro, like the example below:
#include <iostream>
#include <cmath>
#include <boost/preprocessor/repetition/enum.hpp>
#define SIZE 4
#define D 0.1
#define ORDER(z, n, text) std::sin(n * D)
double const A[SIZE] = { BOOST_PP_ENUM(SIZE, ORDER, ~) };
int main() {
for(auto i : A) std::cout << i << std::endl;
}
Or, you could use std::array instead of raw arrays, and via use of template meta-programming to generate a std::array at compile time. like the example below:
template<typename T, typename F, int SIZE, int... N>
constexpr std::array<T, SIZE>
genarray(F f) {
return std::array<T, SIZE>{{ f(N)... }};
}
template<typename T, typename F, int SIZE, int...> struct recursive_gen;
template<typename T, typename F, int SIZE, int... Args>
struct recursive_gen<T, F, SIZE, 0, Args...> {
static constexpr std::array<T, SIZE> generate(F f) {
return genarray<T, F, SIZE, 0, Args...>(f);
}
};
template<typename T, typename F, int SIZE, int N, int... Args>
struct recursive_gen<T, F, SIZE, N, Args...> {
static constexpr std::array<T, SIZE> generate(F f) {
return recursive_gen<T, F, SIZE, N - 1, N, Args...>::generate(f);
}
};
template<typename T, int SIZE>
struct array_generator {
template<typename F>
static constexpr std::array<T, SIZE> generate(F f) {
return recursive_gen<T, F, SIZE, SIZE - 1>::generate(f);
}
};
std::array<double, 4> const A = array_generator<double, 4>::generate([](int i) { return std::sin(0.1 * i);});
std::array<double, 4> const B = array_generator<double, 4>::generate([](int i) { return std::cos(0.1 * i);});
constexpr int fun(int i) { return 2 * i; }
constexpr std::array<int, 4> const C = array_generator<int, 4>::generate(fun); // generation during compile time
LIVE DEMO
Note however, that in order for generation to take place at compile time input function in array_generator must be constexpr. This is not the case for trigonometric functions (i.e., they are not constexpr). Thus initialization of arrays A and B will take place at initialization time, whereas generation of array C will take place at compile time.
Imagine that global array A is much longer and you don't want to do all of this repetitive typing. Is there a shorter way to initialize array A at compile or initialization time
Create a generator and pass it through std::generate_n() (or plain std::generate()).
#include <algorithm>
#include <array>
#include <cmath>
template <typename Value_t>
struct SinGenerator{
SinGenerator(std::size_t start = 0, Value_t counter_scalar = 1)
: index{start},
scalar{counter_scalar} {
}
Value_t operator()() {
return sin(index++ * scalar);
}
std::size_t index;
Value_t scalar;
};
template <typename Value_t, std::size_t Size>
std::array<Value_t, Size> init_table(const std::size_t start,
const Value_t counter_scalar) {
std::array<Value_t, Size> arr;
SinGenerator<Value_t> gen(start, counter_scalar);
std::generate(arr.begin(), arr.end(), gen);
return arr;
}
const auto kSinTable(init_table<float, 10>(0, 0.1f));
In case you A array will always stays the same and is very big, you can always write a short script, which calculates every value in array and output could be used in source code to have static initialization.
In case formula is simple, even MS Excel could be used to generate that kind of static initialization data.
Related
I would like to find an elegant way of initializing C++ array elements with their indices. I have a lot of code that looks like this:
static constexpr size_t ELEMENT_COUNT = 8;
MyObject x[ELEMENT_COUNT] = {{0}, {1}, {2}, {3}, {4}, {5}, {6}, {7}};
Where MyObject is effectively,
struct MyObject {
size_t mMyIndex;
MyObject(size_t myIndex) : mMyIndex(myIndex) {}
};
The problems should be already clear: when ELEMENT_COUNT changes, I have to adapt the initializer list, which feels very un-C++. And if ELEMENT_COUNT is, say, 1000, this becomes impractical.
Is something like the following possible in C++?:
MyObject mObjects[ELEMENT_COUNT] = initialize_array_with_indices<ELEMENT_COUNT>();
Does such a function like initialize_array_with_indices<N>() exist in the std library? Is it possible? Using std::array<> is an option, if that gets me further.
It is impossible to intialize a built-in array like this. Arrays can only be default-initialized, value-initialized or aggregate-initialized (with exception for string literals). The only one of these allowing to specify different values for the elements is aggregate initialization and that requires explicitly listing each element.
(There is one other exception specific to non-static array members of classes. They may be initialized by copy through implicitly defined constructors of the enclosing class. However that still doesn't allow writing an initializer like you want.)
So you have to use std::iota or a loop after the initialization.
If you use std::array instead of a built-in array, you can define it as
template<typename T, std::size_t N>
constexpr auto initialize_array_with_indices() {
return []<std::size_t... Is>(std::index_sequence<Is...>){
return std::array<T, N>{Is...};
}(std::make_index_sequence<N>());
}
To be used like
auto mObjects = initialize_array_with_indices<MyObject, ELEMENT_COUNT>();
The implementation above requires C++20. It can be written (slightly longer) for previous versions as well. Specifically before C++20 lambdas can't have explicit template parameters, so a helper function must be used instead (or a constexpr array of indices can be filled first with the approach below and then std::apply used on it to get the indices as a pack into the lambda). Also before C++17 it will require copy/move-constructibility of T.
An implementation that assumes that default-initialization of MyObject is possible and not undesirable would be much more straight-forward. (It would simply default-initialize a std::array and then loop through it to set the indices or use std::iota on it.)
Here is another way of doing this with both C++11 as well as C++20. Note that starting with C++17 we can do all this at compile time(if required). Additionally, I've made the converting constructor constexpr.
C++11 Version
This uses SFINAE:
struct C
{
int i;
//added constexpr here
constexpr C(int pi): i(pi)
{
}
};
/*
Version #1 : Used to end the recursion.
#tparam: destType : represents type of elements in the array to which we want to convert to. This parameter must be explicitly passed in angle brackets
#tparam: sourceType: represents type of elements in the array from which we want to convert. This parameter will be deduced using argument deduction
#tparam: sourceSize: represents size of the source array. This will also be deduced using deduction
#tparam: extractedTypes represents type of the values extracted by indexing and casting individual elments of sourceArray
#return: this function template returns an std::array with elements of type destType and size sourceSize
*/
template<typename destType, typename sourceType, std::size_t sourceSize, typename... extractedTypes>
constexpr typename std::enable_if<sizeof...(extractedTypes) == sourceSize, std::array<destType, sourceSize> >::type
cast_array(const std::array<sourceType, sourceSize> &sourceArray, const extractedTypes... extractedValues)
{
return std::array<destType, sourceSize>{{extractedValues...}};//use pack expansion
}
/*
Version 2 : Recursively called until sizeof...(Types) becomes same as sourceSize
#tparam: destType : represents type to be converted to which must be explicitly passed in angle brackets
#tparam: sourceType: represents type of elements in the array from which we want to convert. This will be deduced using argument deduction
#tparam: sourceSize: represents size of the source array. This will also be deduced using deduction
#tparam: extractedTypes represents type of the values extracted by indexing and casting individual elments of sourceArray
#return: this function template returns an std::array with elements of type destType and size sourceSize
*/
template<typename destType, typename sourceType, std::size_t sourceSize, typename... extractedTypes>
constexpr typename std::enable_if<sizeof...(extractedTypes) != sourceSize, std::array<destType, sourceSize> >::type
cast_array(const std::array<sourceType, sourceSize> &sourceArray, const extractedTypes... extractedValues)
{
//recursively call cast_array passing the sourceArray and extracted elements until all elements are extracted
return cast_array<destType>(sourceArray, extractedValues..., static_cast<destType>(sourceArray[sizeof...(extractedValues)]));
}//-----------------------------------------------------------------------------------------------^^^^^^^^^^^^^^^^->extract/pass elements index by index by casting them to destType
static constexpr size_t ELEMENT_COUNT = 8;
template<std::size_t N = ELEMENT_COUNT>
std::array<C, N> make_array()
{
std::array<int, ELEMENT_COUNT> tempArray{};
int count = 0;
for(auto &elem: tempArray)
{
elem = ++count;
}
return cast_array<C>(tempArray);
}
int main()
{
std::array<C, ELEMENT_COUNT> myArray = make_array();
//lets confirm if all objects have the expected value
for(const auto &elem: myArray)
{
std::cout << elem.i << std::endl;;
}
}
Working demo C++11
C++17 Version
With C++17, std::array<T, N>::begin is constexpr, so we can do all this at compile time(if needed). Here no changes have been made to the cast_array function template. Instead constexpr is added to make_array and myArray. So to avoid pasting the same code again and again, in the below code snippet I've only pasted the modified portion of the code. You can check out the full working example here.
//--------------------------------------vvvvvvvvv------->added constexpr here
template<std::size_t N = ELEMENT_COUNT> constexpr std::array<C, N> make_array()
{
std::array<int, ELEMENT_COUNT> tempArray{};
int count = 0;
for(auto &elem: tempArray)
{
elem = ++count;
}
return cast_array<C>(tempArray);
}
int main()
{
//note the constexpr here from C++17
constexpr std::array<C, ELEMENT_COUNT> myArray = make_array();
//lets confirm if all objects have the expected value
for(const auto &elem: myArray)
{
std::cout << elem.i << std::endl;;
}
}
C++17 demo
C++20 Version
Here we make use of requires. Since changes have been done to cast_array also so I will post the whole code here again(except the documentation part)
struct C
{
int i;
//added this constexpr
constexpr C(int pi): i(pi)
{
}
};
template<typename destType, typename sourceType, std::size_t sourceSize, typename... extractedTypes>
constexpr std::array<destType, sourceSize> cast_array(const std::array<sourceType, sourceSize> &sourceArray, const extractedTypes... extractedValues)
requires (sizeof...(extractedTypes) == sourceSize)
{
return std::array<destType, sourceSize>{{extractedValues...}};//use pack expansion
}
template<typename destType, typename sourceType, std::size_t sourceSize, typename... extractedTypes>
constexpr std::array<destType, sourceSize> cast_array(const std::array<sourceType, sourceSize> &sourceArray, const extractedTypes... extractedValues)
requires (sizeof...(extractedTypes) != sourceSize)
{
//recursively call cast_array passing the sourceArray and extracted elements until all elements are extracted
return cast_array<destType>(sourceArray, extractedValues..., static_cast<destType>(sourceArray[sizeof...(extractedValues)]));
}//-----------------------------------------------------------------------------------------------^^^^^^^^^^^^^^^^->extract/pass elements index by index by casting them to destType
static constexpr size_t ELEMENT_COUNT = 8;
//--------------------------------------vvvvvvvvv------->added constexpr here
template<std::size_t N = ELEMENT_COUNT> constexpr std::array<C, N> make_array()
{
std::array<int, ELEMENT_COUNT> tempArray{};
int count = 0;
for(auto &elem: tempArray)
{
elem = ++count;
}
return cast_array<C>(tempArray);
}
int main()
{
constexpr std::array<C, ELEMENT_COUNT> myArray = make_array();
for(const auto &elem: myArray)
{
std::cout << elem.i << std::endl;;
}
}
Working demo C++20
Note
If this is a one time thing then you can also use a lambda instead of a separate function template make_array. See demo with lambda
The answer from #user17732522 is fantastic and works in C++20. As they state, it only works for >=C++20, but with adaptations it can work for C++17 and earlier.
Here is a C++17 version, using a helper function:
template<typename T, std::size_t N, std::size_t... Is>
std::array<T, N> initialize_array_with_indices_helper(std::index_sequence<Is...>) {
return std::array<T, N>{Is...};
}
template<typename T, std::size_t N>
constexpr std::array<T,N> initialize_array_with_indices() {
return initialize_array_with_indices_helper<T, N>(std::make_index_sequence<N>());
}
We have a formatting library similar to fmtlib which allows us to format strings like this:
int foo = 1;
double bar = 2.3;
FMT("foo={} bar={}", foo, bar);
We recently added parsing of the format string at compile time, allowing us to specify, eg width,
precision etc directly in the format string
// print foo with width=5, pad with 0; print bar with width 6, precision 2
// as decimal floating point
FMT("foo={:05} bar={:6.2f}", foo, bar);
The way we do this is as follows:
We have a struct which I can use to pass the format string as a template parameter
struct FmtString
{
template<std::size_t N>
constexpr FmtString(const char (&s)[N])
: str(s)
{}
const char* const str;
};
Together with this FmtString and the arguments to be formatted, we use the following somewhat convoluted dance
to generate a std::tuple of FmtSpecT format specifications.
We use the variadic template parameter pack to generate a std::index_sequence:
// use Ts... to create a tuple of the argument types and an std::index_sequence
template<FmtString S, typename... Ts>
struct CompiledFmtSpec
{
using type = decltype(compileFmtSpec<S, std::tuple<Ts...>>(std::index_sequence_for<Ts...> {}));
};
// pull the indices out of the std::index_sequence
template<FmtString S, typename Tuple, std::size_t... Is>
consteval auto compileFmtSpec(std::index_sequence<Is...>)
{
return typename FmtSpecs<S, Tuple, Is...>::type {};
}
We use the std::index_sequence to generate a variadic std::size_t sequence which we can use to
extract each argument from the tuple of arguments, and its respective sequence value
template<FmtString S, typename Tuple, std::size_t... Is>
struct FmtSpecs
{
using type = std::tuple<FmtSpecWrapper<S, std::tuple_element_t<Is, Tuple>, Is>...>;
};
This results in the following tuple:
std::tuple<
FmtSpecWrapper<S, T0, 0>,
FmtSpecWrapper<S, T1, 1>,
FmtSpecWrapper<S, T2, 2>,
...
FmtSpecWrapper<S, Tn, n>>
We can then specialise FmtSpecWrapper for the particular type and parse its supported format spec
Here is the default implementation
template<FmtString S, typename T, std::size_t I>
struct FmtSpecWrapper
{
using type = decltype(parseFmtString<S, I>());
};
template<FmtString S, std::size_t I>
consteval auto parseFmtString()
{
constexpr const char* pos = findFmtSpec(S, I);
constexpr FmtSpec fmt = parseFmtSpec(pos);
return FmtSpecT<fmt.pad, fmt.align_right, fmt.sign, fmt.width, fmt.precision, fmt.type, fmt.spec_len> {};
}
This results in a FmtSpecT specialisation, which has the following NTTPs:
template<char Pad, bool AlignRight, bool Sign, int Width, int Precision, char Type, int SpecLen>
struct FmtSpecT
{
static constexpr char pad = Pad;
static constexpr bool align_right = AlignRight;
static constexpr bool sign = Sign;
static constexpr int width = Width;
static constexpr int precision = Precision;
static constexpr char type = Type;
static constexpr int spec_len = SpecLen;
};
During formatting we can then pull out each FmtSpecT and use the members to format the argument.
The issue I'd like to try and solve is findFmtSpec(S, I).
Its job is to find the I'th format spec from the format string S.
constexpr const char* findFmtSpec(const FmtString& S, std::size_t i)
{
std::size_t curr = 0;
const char* next = S.str;
while (*next)
{
const char c = *next++;
if (c == '{')
{
if (c == *next) // found an escaped brace; {{ or }}
{
++next;
}
else // found the start of the fmt spec
{
if (curr == i)
return next;
++curr;
++next;
}
}
}
throw std::domain_error("too many arguments for format string");
}
My issue is that it is Big O N^2, as it starts from 0 for every argument.
Ideally I would be able to start from the end of the previous format spec and seek forwards to find the next, turning the complexity into O(N).
Any ideas on how I can modify findFmtSpec so that for each i it starts from the return value of the previouc call?
FmtString S is a template parameter, so it is usable in constant expressions.
Therefore all you need to do is write a constexpr function that extracts the specifiers up-front into a suitable container type, for example std::array<std::string_view, N>. You can either enforce directly in the function that the number of specifiers matches sizeof...(Ts), so that N can just be that, or you can write another constexpr function which first counts the number of arguments:
struct FmtString
{
template<std::size_t N>
constexpr FmtString(const char (&s)[N])
: str(s)
{}
const char* const str;
template<FmtString S>
static constexpr auto findFmtSpec = []{
constexpr auto N = []{
// iterate `S.str` here and return number of specifiers
}();
std::array<const char*, N> result;
// iterate `S.str` again and store beginning of format specifiers consecutively in `result`.
return result;
}();
};
Then parseFmtString can just use FmtString::findFmtSpec<S>[I]. I am using a static member variable template instead of a static function template to make sure that the compiler doesn't keep reevaluating an equivalent function call, although I think the compiler should memoize calls with the same arguments. I used nested lambdas for brevity, but you might want to put these into separate functions. There is also no particular reason to have the variable template be a member. It works outside the class as well.
You can also just generate the whole tuple this way:
template<FmtString S, typename... Ts>
static constexpr auto specs = []{
// number of specifiers, can be `sizeof...(Ts)`
// if no specific error handling is required
constexpr auto N = []{
// iterate `S.str` here and return number of specifiers
}();
if constexpr(sizeof...(Ts) != N) {
// handle argument number mismatch
} else {
// specs as values
constexpr auto specs = []{
std::array<FmtSpec, N> specs;
// iterate `S.str` again and store `FmtSpec` for each specifier consecutively in `specs`
return specs;
}();
// lift specs into type (template arguments)
return []<std::size_t... Is>(std::index_sequence<Is...>){
return std::tuple<FmtSpecWrapper<S, Ts, FmtSpecT<specs[Is].pad, /*...*/>>...>{};
}(std::make_index_sequence<N>{});
}
}();
I'd like to make the function multi_dimensional accept a multidimensional array by reference.
Can this be done with a variation of the syntax below which works for three_dimensional?
#include <utility>
// this works, but number of dimensions must be known (not variadic)
template <size_t x, size_t y, size_t z>
void three_dimensional(int (&nd_array)[x][y][z]) {}
// error: parameter packs not expanded with ‘...’
template <size_t... dims>
void multi_dimensional(int (&nd_array)[dims]...) {}
int main() {
int array[2][3][2] = {
{ {0,1}, {2,3}, {4,5} },
{ {6,7}, {8,9}, {10,11} }
};
three_dimensional(array); // OK
// multi_dimensional(array); // error: no matching function
return 0;
}
The main problem is that you cannot make the number of array dimensions itself variadic. So whichever way you go, you will almost certainly need a recursive approach of some sort to deal with the individual array layers. What exactly such approach should look like will mainly depend on what exactly you're planning to do with the array once it's been given to you.
If really all you want is a function that can be given any multi-dimensional array, then just write a function that can be given anything but only exists as long as that anything is an array:
template <typename T>
std::enable_if_t<std::is_array_v<T>> multi_dimensional(T& a)
{
constexpr int dimensions = std::rank_v<T>;
// ...
}
However, this by itself will most likely not get you very far. To actually do anything meaningful with the array you've been given, you will most likely need some recursive walking through subarrays. Unless you really just want to look at the topmost layer of the structure.
Another approach is to use a recursive template to peel back the individual array levels, for example:
// we've reached the bottom
template <typename T, int N>
void multi_dimensional(T (&a)[N])
{
// ...
}
// this matches any array with more than one dimension
template <typename T, int N, int M>
void multi_dimensional(T (&a)[N][M])
{
// peel off one dimension, invoke function for each element on next layer
for (int i = 0; i < N; ++i)
multi_dimensional(a[i]);
}
I would, however, suggest to at least consider using std::array<> instead of raw arrays as the syntax and special behavior of raw arrays tends to turn everything into a confusing mess in no time. In general, it might be worth to implement your own multi-dimensional array type, like an NDArray<int, 2, 3, 2> which internally works with a flattened representation and just maps multi-dimensional indices to a linear index. One advantage of this approach (besides the cleaner syntax) would be that you can easily change the mapping, e.g., to switch from row-major to column-major layout, e.g., for performance optimization…
To implement a general nD array with static dimensions, I would introduce a helper class to encapsulate the recursive computation of a linear index from an nD index:
template <std::size_t... D>
struct row_major;
template <std::size_t D_n>
struct row_major<D_n>
{
static constexpr std::size_t SIZE = D_n;
std::size_t operator ()(std::size_t i_n) const
{
return i_n;
}
};
template <std::size_t D_1, std::size_t... D_n>
struct row_major<D_1, D_n...> : private row_major<D_n...>
{
static constexpr std::size_t SIZE = D_1 * row_major<D_n...>::SIZE;
template <typename... Tail>
std::size_t operator ()(std::size_t i_1, Tail&&... tail) const
{
return i_1 + D_1 * row_major<D_n...>::operator ()(std::forward<Tail>(tail)...);
}
};
And then:
template <typename T, std::size_t... D>
class NDArray
{
using memory_layout_t = row_major<D...>;
T data[memory_layout_t::SIZE];
public:
template <typename... Args>
T& operator ()(Args&&... args)
{
memory_layout_t memory_layout;
return data[memory_layout(std::forward<Args>(args)...)];
}
};
NDArray<int, 2, 3, 5> arr;
int main()
{
int x = arr(1, 2, 3);
}
I'm using variadic templates, and I would like to find the good way to unpack my parameters
template <typename kernel_type, typename ...kernel_types>
class MetaKernel : public MyKernel<kernel_type, kernel_types...> {
public:
MetaKernel (
unsigned int m,
unsigned int n,
const kernel_type& kernel_,
const kernel_types&... kernels_
) :
MyKernel<kernel_type, kernel_types...>(m, n)
{
Ks.set_max_size(sizeof...(kernel_types));
Ks.set_size(sizeof...(kernel_types));
// for each kernels_, add a MyObsKernel to Ks
// Ks[sizeof...(kernel_types)].reset((new MyObsKernel<kernel_type, kernel_types...>(kernels_, prototypes_, m, n))...);
}
private:
array < scoped_ptr < MyObsKernel<kernel_type, kernel_types...> > > Ks;
}
from the documentation (http://en.cppreference.com/w/cpp/language/parameter_pack), I saw how to unpack like this:
int dummy[sizeof...(Ts)] = { (std::cout << args, 0)... };
but as I'm working on scoped_ptr table, I need to initialize it with a "reset". so my solution does not work. How can I unpack my parameters using scoped_ptr ?
thanks for your help,
Jerome
You may initialize std::array with an initializer-list. Following may help: https://ideone.com/PTwatb
MetaKernel (unsigned int m, unsigned int n, const kernel_type& kernel_, const kernel_types&... kernels_) :
MyKernel<kernel_type, kernel_types...>(m, n),
Ks({scoped_ptr<MyObsKernel<kernel_type, kernel_types...> >(new MyObsKernel<kernel_type, kernel_types...>(kernels_, kernel_, m, n))...})
{
}
As I said in a comment, your question is incomplete. I understand that Ks is an std::array of size N = sizeof...(kernel_types), and that you want to call reset() on each element. If so, here's a solution:
struct _do { template<typename... A> _do(A&&...) { } };
template <typename kernel_type, typename ...kernel_types>
class MetaKernel : public MyKernel<kernel_type, kernel_types...>
{
using Base = MyKernel<kernel_type, kernel_types...>;
using Obs = MyObsKernel<kernel_type, kernel_types...>;
static constexpr size_t N = sizeof...(kernel_types);
public:
MetaKernel (
unsigned int m,
unsigned int n,
const kernel_type& kernel_,
const kernel_types&... kernels_
) : Base(m, n)
{
reset(std::make_integer_sequence<size_t, N>(), m, n, kernels_...);
}
private:
template<size_t... I>
void reset (
std::integer_sequence<size_t, I...>,
unsigned int m,
unsigned int n,
const kernel_types&... kernels_
)
{
_do{Ks[I].reset(new Obs(kernels_, /*prototypes_,*/ m, n))...};
}
array <scoped_ptr <Obs>, N> Ks;
};
I am making use of the auxiliary object _do and C++14 features std::integer_sequence, std::make_index_sequence. If this is not available, you can check Range here, where _do is also explained.
You need a second function apart from the constructor in order to deduce index pack I... and use it to align with parameters kernels_... so that each parameter is used in exactly one call of reset() on one element of Ks.
If you have more such unpacking operations, it is better to have I... available in the entire class and use it directly. You will then need just a second MetaKernelImpl class instead of adding a second version for each function. This class will have an additional template parameter that is specialized for std::integer_sequence<size_t, I...> for some deduced pack size_t... I. Again, have a look at Caller here to see how this can be done.
Consider a compile-time function of the form:
template <unsigned int Value>
constexpr unsigned int function()
{
// Just for the example, but it could be very complicated here
return Value*Value;
}
How to write the runtime equivalent that will call the right compile-time version using template metaprogramming, knowing that the value will always be in the [From, To[ interval:
template <unsigned int From, unsigned int To, /* Something here */>
constexpr unsigned int function(const unsigned int value)
{
// Something here
}
The branching to the right version should be as fast as possible.
For example function<0, 32>(6) (runtime version) should call function<6>() (compile-time version).
EDIT: Explanation:
Why do I want to do this? This function (real use case) need to be as fast as possible (supercomputing issues). By providing the parameter at compile-time, I can generate very efficient code. If I simply move the value from the template parameter to a function parameter, the code is between 10 and 100 times slower. But in fact, this parameter does not have a very wide range of possible values (like between 0 and 32): so it would be far more efficient to branch at runtime on the right compile-time version.
The easiest way is to set up a recursive cascading if/recurse chain.
#define RETURNS(X) -> decltype(X) { return (X); }
template<unsigned From, unsigned To, typename Target>
struct CallIf {
constexpr auto operator()( unsigned N )
RETURNS( (N==From)?Target::template func<From>():CallIf<From+1, To, Target>()( N ) );
};
template<unsigned From, typename Target>
struct CallIf<From, From+1, Target> {
constexpr auto operator()( unsigned N )
RETURNS( Target::template func<From>() );
};
struct Func {
template<unsigned V>
constexpr unsigned func() const {
return function<V>();
}
};
or something like that, and rely on the compiler collapsing that chain of ifs down to one. (if you know the return type, you can do away with that annoying RETURNS macro, or if you have C++1y features you can do the same).
Now, you might want to compare this to a version that does a binary search on value in that range, using a similar recursive call case. Similarly, you could do it by checking and setting bits in a compile time value.
template<unsigned From, unsigned To, typename Target>
struct CallIf {
enum { Mid = From + (To-From)/2 }; // avoid overflow risk
constexpr auto operator()( unsigned N )
RETURNS( (N>=Mid)?CallIf<Mid, To, Target>()(N):CallIf<From,Mid,Target>()(N) );
};
with the same specialization for the 1-width case.
Another approach would be to set up a static array of invocations of function<V> and then do an array dereference at run time:
template<unsigned...> struct indexes {};
template<unsigned Min, unsigned Max, unsigned... Is> struct make_indexes:make_indexes<Min, Max-1, Max-1, Is...> {};
template<unsigned Min, unsigned... Is> struct make_indexes<Min, Min, Is...>:indexes<Is...> {};
template<unsigned From, unsigned To, typename Target>
struct CallIf {
template<unsigned... Is>
unsigned Invoke( indexes<Is...>, unsigned N ) const {
typedef unsigned(*target)();
static target ts[] = { &(function<Is>)... };
return ts[N-From]();
};
unsigned operator()( unsigned N ) const {
return Invoke( make_indexes<From, To>(), N );
}
};
although I'm not sure how to make the above constexpr easily in C++11 at least, and I skipped out on doing return-type deduction.
None of the above are tested or compiled, so most likely will require some repair. But the core concept will work. The specialization might need some work: doing <From, From+1 to terminate is something I haven't done in practice: if that causes issues, you can do <From, Width based helper, and specialize on Width=1.
I personally call this technique (embodied in the CallIf type above) the "magic switch", where we take a run time valid and "magically" make it a compile time value. I only mention this because you can probably find me talking about it on stack overflow by googling Yakk and "magic switch" (and site:stackoverflow.com) for other variations, some of which have been compiled and have live examples attached.
Finally, while the last version (the manual jump table) may be fastest, if you are calling it so often that the speed of this invocation is key, you might want to think about wrapping not just the call site, but the algorithm surrounding it in a magic switch: do the dispatching earlier. If, however, you only get the index at the last moment, and you are ok with a non-constexpr call, it should work. Note that static arrays will be created for each Function and To and From used.
You may create an (constexpr) array of results, something like :
#if 1 // Not in C++11
template <std::size_t ...> struct index_sequence {};
template <std::size_t I, std::size_t ...Is>
struct make_index_sequence : make_index_sequence < I - 1, I - 1, Is... > {};
template <std::size_t ... Is>
struct make_index_sequence<0, Is...> : index_sequence<Is...> {};
#endif
template <unsigned int Value>
constexpr unsigned int function()
{
// Just for the example, but it could be very complicated here
return Value * Value;
}
namespace detail
{
template <std::size_t From, std::size_t...Is>
struct result_array
{
static constexpr std::array<unsigned int, sizeof...(Is)> values = {{::function<From + Is>()...}};
};
template <std::size_t From, std::size_t...Is>
constexpr std::array<unsigned int, sizeof...(Is)> result_array<From, Is...>::values;
template <std::size_t From, std::size_t...Is>
constexpr unsigned int function(unsigned int value, index_sequence<Is...>)
{
return result_array<From, Is...>::values[value - From];
}
} // namespace detail
template <unsigned int From, unsigned int To>
constexpr unsigned int function(const unsigned int value)
{
static_assert(From < To, "Invalid template parameters");
return detail::function<From>(value, make_index_sequence<std::size_t(To + 1 - From)>());
}