Reputation: 1561
The following class will not compile under C++11; the loop as it stands can only be executed at runtime and so one gets a "char(*)[i] is a variably-modified type" error from the template class static function call within the loop:
#include <cstddef>
#include <vector>
template <std::size_t N>
class Foo
{
private:
const std::vector<char(*)[]> bar = bar_init();
static std::vector<char(*)[]> bar_init()
{
std::vector<char(*)[]> init;
for (size_t i = N; i > 0; i >>= 1)
{
auto ptr_to_array = MyClass<char(*)[i]>::static_return_ptr_to_array();
init.emplace_back(reinterpret_cast<char(*)[]>(ptr_to_array));
}
return init;
}
};
Is there a way I can accomplish the same effect using templates within the initialization function? That is to say, initialize "bar" of size log2(N) at "Foo" class instantiation as a const vector of pointers to array of char with each vector element containing e.g. for N=32 the output of:
MyClass<char(*)[32]>::static_return_ptr_to_array();
MyClass<char(*)[16]>::static_return_ptr_to_array();
MyClass<char(*)[8]>::static_return_ptr_to_array();
//etc...
Upvotes: 0
Views: 422
Reputation: 66230
If you define index container type traits (or you use std::index_sequence
, unfortunately available only starting from C++14)
template <std::size_t ...>
struct indexList
{ };
and you define a type traits to extract a sequence of decreasing power of two
template <std::size_t, typename>
struct iLH;
template <std::size_t N, std::size_t ... Is>
struct iLH<N, indexList<Is...>> : public iLH<(N >> 1), indexList<Is..., N>>
{ };
template <std::size_t ... Is>
struct iLH<0U, indexList<Is...>>
{ using type = indexList<Is...>; };
template <std::size_t N>
struct getIndexList : public iLH<N, indexList<>>
{ };
template <std::size_t N>
using getIndexList_t = typename getIndexList<N>::type;
should be possible write your Foo
simply as
template <std::size_t N>
class Foo
{
private:
const std::vector<char **> bar = bar_init (getIndexList_t<N>{});
template <std::size_t ... Is>
static std::vector<char **> bar_init (indexList<Is...> const &)
{
std::vector<char **> init { MyClass<char(*)[Is]>::getPtr()... };
return init;
}
};
(supposing a static getPtr()
method in MyClass
) that return a char **
and a bar
vector of char **
).
The following is a full compiling example
template <typename T>
struct MyClass;
template <std::size_t Dim>
struct MyClass<char(*)[Dim]>
{
static char ** getPtr ()
{ static char ach[Dim]; static char * ret { ach } ; return &ret; }
};
template <std::size_t ...>
struct indexList
{ };
template <std::size_t, typename>
struct iLH;
template <std::size_t N, std::size_t ... Is>
struct iLH<N, indexList<Is...>> : public iLH<(N >> 1), indexList<Is..., N>>
{ };
template <std::size_t ... Is>
struct iLH<0U, indexList<Is...>>
{ using type = indexList<Is...>; };
template <std::size_t N>
struct getIndexList : public iLH<N, indexList<>>
{ };
template <std::size_t N>
using getIndexList_t = typename getIndexList<N>::type;
template <std::size_t N>
class Foo
{
private:
const std::vector<char **> bar = bar_init (getIndexList_t<N>{});
template <std::size_t ... Is>
static std::vector<char **> bar_init (indexList<Is...> const &)
{
std::vector<char **> init { MyClass<char(*)[Is]>::getPtr()... };
return init;
}
};
int main ()
{
Foo<32U> f32;
}
Upvotes: 1
Reputation: 5624
something like ( in c++11 )
template<int I>
struct tag{};
void init( std::vector<char(*)[]>& result, tag<0> ){}
template<int I>
void init( std::vector<char(*)[]>& result, tag<I> )
{
auto ptr_to_array = MyClass<char(*)[I]>::static_return_ptr_to_array;
result.emplace_back(reinterpret_cast<char(*)[]>(ptr_to_array));
init(result,tag<(I>>1)>{});
}
template <std::size_t N>
class Foo
{
private:
const std::vector<char(*)[]> bar = bar_init();
static std::vector<char(*)[]> bar_init()
{
std::vector<char(*)[]> result;
init( result, tag<N>{} );
return result;
}
};
in c++17 this can be further simplified with an if constexpr and no tag<>. Moreover, note that std::vector<char(*)[]>
is not portable because vector needs a complete type.
Upvotes: 2
Reputation: 29017
I think the crucial insight here, is that you can't write:
int i = ?? // automatic variable
auto val = MyClass<char(*)[i]>::static_return_ptr_to_array()
... template arguments have to be constants.
What you can do is something like:
const std::unordered_map<int,???> allocator_map = {
{1, MyClass<char(*)[1]>::static_return_ptr_to_array},
{2, MyClass<char(*)[2]>::static_return_ptr_to_array},
{4, MyClass<char(*)[4]>::static_return_ptr_to_array},
{8, MyClass<char(*)[8]>::static_return_ptr_to_array},
...
};
and then
const auto it = allocator_map.find(i);
if (it == allocator_map.end())
// throw error
auto val = (it->second)();
Basically, the idea is that you have a static array of allocator functions, and then index into it. (There may be clever ways of using templates to initialize the map. I probably would just write it out by hand though - possibly using a preprocessor macro).
Upvotes: 1