0

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...
12
  • Possible duplicate of Create static array with variadic templates Commented Oct 26, 2017 at 12:07
  • 2
    index_sequence might help. Commented Oct 26, 2017 at 12:11
  • 1
    Can you show us your MyClass ? Or a simplified (but compilable) version of it? Commented Oct 26, 2017 at 12:17
  • @underscore_d The question there seems partly applicable, but in my situation my vector is only const, not const static, each instantiation of "Foo" may be templated with different values of N so the initialization procedure for bar would seemingly require a mixture of both template specialization and runtime calls. Commented Oct 26, 2017 at 12:17
  • @max66 I altered the code I posted somewhat for the example to make it simpler to understand the issue I was encountering. In the actual code the templated static method call returns an allocator, which must be templated on a certain type, in this case an array of char of some size. The allocator's allocate method is then called and gives up a pointer to array of char, and that output is what I'd like to put in the vector. Commented Oct 26, 2017 at 12:23

3 Answers 3

2

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.

Sign up to request clarification or add additional context in comments.

Comments

1

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).

Comments

1

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;
 }

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.