1

Given a function pointer array of function_sig, I want to initialise it to a set of template function pointers that are index via a template parameter. Is this possible?

E.g.

template<int I>
void fn() { /* do something */ }

typedef void(*function_sig)();

template<int ARRAY_SIZE>
struct items
{
  static function_sig array[ARRAY_SIZE];
};

template<int ARRAY_SIZE>
function_sig items<ARRAY_SIZE>::array = { /* what do I put here? */ };

So, is there something that I can put in the initialiser list, such that items<ARRAY_SIZE>::array will be initialised to { fn<0>, fn<1>, ..., fn<ARRAY_SIZE-1> }?

NOTE: I know of a way to do this with preprocessor magic, but I'd like to try without that. Currently, I'm thinking I'll have to do away with the array and replace it with something that would act like an array, but that would lead to a search of O(N) when indexing the pseudo-array, which I'd like to not do.

ildjarn
  • 62,044
  • 9
  • 127
  • 211
Adrian
  • 10,246
  • 4
  • 44
  • 110
  • Does either of these Q&A's help you: [c++11 constexpr flatten list of std::array into array](http://stackoverflow.com/questions/25068481/c11-constexpr-flatten-list-of-stdarray-into-array) or [Implementation C++14 make_integer_sequence](http://stackoverflow.com/questions/17424477/implementation-c14-make-integer-sequence)? – MicroVirus Jun 21 '16 at 20:56
  • @MicroVirus, possibly, though this is for a c++11 compiler (VS2013). I'll still read through those and see if I can come up with something. Thanks. – Adrian Jun 21 '16 at 21:08

2 Answers2

1

The problem is to get a variadic range from 0 to ARRAY_SIZE - 1.

I propose a solution that transfer array in a base class itemsH

#include <iostream>


template <std::size_t ...>
struct range
 { };

template <std::size_t N, std::size_t ... Next>
struct rangeH 
 { using type = typename rangeH<N-1U, N-1U, Next ... >::type; };

template <std::size_t ... Next >
struct rangeH<0U, Next ... >
 { using type = range<Next ... >; };


template<int I>
void fn() { std::cout << "[" << I << "]" << std::endl; }

typedef void(*function_sig)();

template <typename T>
struct itemsH;

template <std::size_t ... RNG>
struct itemsH<range<RNG...>>
 {
   static function_sig array[sizeof...(RNG)];
 };

template<std::size_t ARRAY_SIZE>
struct items : public itemsH<typename rangeH<ARRAY_SIZE>::type>
 { };

template <std::size_t ... RNG>
function_sig itemsH<range<RNG...>>::array[sizeof...(RNG)] = { fn<RNG>... };


int main ()
 {
   items<10>  i_10;

   for ( unsigned ui = 0U ; ui < 10 ; ++ui )
      i_10.array[ui]();

   return 0;
 }

p.s.: I've changed the type of ARRAY_SIZE from int to size_t; hoping isn't a problem

p.s.2: sorry for my bad English.

--- edit: added C++14 example ---

If you (when you) can use C++14, the you can use std::index_sequence and std::make_index_sequence, throwing away range and rangeH.

The example become

#include <utility>
#include <iostream>


template<int I>
void fn() { std::cout << "[" << I << "]" << std::endl; }

typedef void(*function_sig)();

template <typename T>
struct itemsH;

template <std::size_t ... RNG>
struct itemsH<std::index_sequence<RNG...>>
 { static function_sig array[sizeof...(RNG)]; };

template<std::size_t ARRAY_SIZE>
struct items : public itemsH<std::make_index_sequence<ARRAY_SIZE>>
 { };

template <std::size_t ... RNG>
function_sig itemsH<std::index_sequence<RNG...>>::array[sizeof...(RNG)]
   = { fn<RNG>... };


int main ()
 {
   items<10>  i_10;

   for ( unsigned ui = 0U ; ui < 10 ; ++ui )
      i_10.array[ui]();

   return 0;
 }
max66
  • 65,235
  • 10
  • 71
  • 111
  • Your English is fine and the switch to `std::size_t` is fine. I only used `int` because I was lazy. :D I would usually use `std::size_t`. – Adrian Jun 27 '16 at 16:32
0
#include <array>

template<int... Is>
struct int_seq { };

namespace detail {

template<int I, int... Is>
struct make_int_seq : make_int_seq<I - 1, I, Is...> { };

template<int... Is>
struct make_int_seq<0, Is...> {
    using type = int_seq<0, Is...>;
};

} // namespace detail

template<int SizeN>
using make_int_seq = typename detail::make_int_seq<SizeN - 1>::type;

template<int I>
void fn() { /* do something */ }

//typedef void(*function_sig)();
using function_sig = void(*)();

template<int ARRAY_SIZE>
struct items {
    static std::array<function_sig, ARRAY_SIZE> array;
};

template<int... Is>
std::array<function_sig, sizeof...(Is)> create_items_array(int_seq<Is...>) {
    return {{ &fn<Is>... }};
}

template<int ARRAY_SIZE>
std::array<function_sig, ARRAY_SIZE> items<ARRAY_SIZE>::array
  = create_items_array(make_int_seq<ARRAY_SIZE>{});

Online Demo

With C++14, int_seq et al disappear in favor of std::integer_sequence et al.

ildjarn
  • 62,044
  • 9
  • 127
  • 211
  • @Adrian : No, C-arrays are not copyable and thus cannot be returned from functions, and we need the `create_items_array` factory function. – ildjarn Jun 22 '16 at 00:00
  • FYI, some slight modification would have to be made with upgrading to c++14, as your `make_int_seq` will generate 0..N, where as `std::make_integer_sequence` generates 0..N-1. – Adrian Jun 27 '16 at 16:29
  • @Adrian : [No, it doesn't](http://coliru.stacked-crooked.com/a/728d7ff9f9771963), and I've no idea why you think it would... – ildjarn Jun 28 '16 at 06:30