Search code examples
c++templatesc++14constexprstdarray

Storing set of std::arrays of any (but constant) length


Is there a way to store a set of std::arrays of any (but constant) length where the length of the array can later be used in a constexpr?

I guess standard containers are out of question, but there might be a template solution somehow. All of the information is available at compile time, isn't it?

Example code:

#include <iostream>
#include <string>
#include <vector>
#include <array>
#include <algorithm>

// Class storing an array of any (but constant) size
struct A {
    const std::vector<std::string> container; 
    const int container_size;
    A(const std::vector<std::string>& v, int size) : container(v), container_size(size) { }
};

int main() {
    // List of variable length const arrays and their sizes
    std::vector<A> myAList {
        A({ std::string("String1"), std::string("String2") }, 2),
        A({ std::string("String1") }, 1)
    };

    // How would I go about using the size of each array in a constexpr?
    for (auto const& a : myAList) {
    // Example constexpr:
    //  somefunc(std::make_index_sequence<a.container_size>{});

    // 2nd example converting to actual std::array
    //  std::array<std::string, a.container_size> arr;
    //  std::copy_n(std::make_move_iterator(a.begin()), a.container_size, arr.begin());
    }

    return 0;
}

UPDATE:

More details were requested, so here goes. I don't really care how the array is defined, anything that works... The exact constexpr used is the one in the example code, std::make_index_sequence<CONSTEXPR>{}. I just know that I have a set of constant arrays that are defined at compile time, and it should somehow be possible to refer to their length elsewhere in a constexpr.

Heck, I'd actually be fine with just storing the lengths:

// Class storing an array size
struct A {
    A(int size) : container_size(size) { }
    const int container_size;
};

int main() {
    // List of lengths
    std::vector<A> mySizeList { 2, 1 };

    for (auto const& a : mySizeList) {
    //  somefunc(std::make_index_sequence<a.container_size>{});
    }
    return 0;
}

Solution

  • This assumes that you want to call somefunc with both the const& to the std::vector and the index_sequence, and that you only want to call somefunc. It also offers support for a somefunc with an extended signature (simply pass a 3rd argument with the return value and/or the additional arguments, which will be passed after the std::vector<T> const&).

    Failure to align the size of the vector with the size passed in will result in badness if the size passed in is longer than the vector.

    It also assumes you know what function you are calling at the point of construction. This can be generalized to any finite set of functions you want to call at the point of construction, naturally.

    The technique used is called "type erasure" or "run-time concepts". I erase down to the concept of invoking your some_func with the index sequence in question during construction, and store that erased operation in f.

    template<size_t N>
    using size=std::integral_constant<size_t, N>;
    
    template<
      class T, template<class...>class Operation,
      class Sig=std::result_of_t<Operation<size<0>>(std::vector<T> const&)>()
    >
    struct eraser;
    
    template<class T, template<class...>class Operation, class R, class...Args>
    struct eraser<T,Operation, R(Args...)> {
      std::vector<T> data;
      R(*f)(eraser const*, Args&&...);
      R operator()(Args...args)const{
        return f(this, std::forward<Args>(args)...);
      }
      template<size_t N>
      eraser( std::initializer_list<T> il, size<N> ):
        eraser( il.begin(), size<N>{} )
      {}
      template<class...Ts>
      eraser( T t0, Ts... ts ):
        eraser(
          {std::forward<T>(t0), std::forward<Ts>(ts)...},
          size<sizeof...(ts)+1>{}
        )
      {}
      template<size_t N>
      eraser( T const* ptr, size<N> ):
        data(ptr, ptr+N),
        f([](eraser const*self, Args&&...args)->R{
          return Operation<size<N>>{}(self->data, std::forward<Args>(args)...);
        })
      {}
    };
    template<class T, size_t ... Is>
    void some_func( std::vector<T> const&, std::index_sequence<Is...>) {
      std::cout << "called! [#" << sizeof...(Is) << "]\n";
    }
    template<class N>
    struct call_some_func {
      template<class T>
      void operator()(std::vector<T> const& v) const {
        some_func( v, std::make_index_sequence<N{}>{} );
      }
    };
    
    
    int main() {
      using A = eraser<std::string, call_some_func>;
      // List of variable length const arrays and their sizes
      std::vector<A> myAList {
        A({ std::string("String1"), std::string("String2") }, size<2>{}),
        A({ std::string("String1") }, size<1>{})
      };
    
      std::cout << "Objects constructed.  Loop about to start:\n";
    
      // How would I go about using the size of each array in a constexpr?
      for (auto const& a : myAList) {
        a();
      }
    }
    

    live example