Search code examples
templatesc++11stdarray

C++11 return a std::array with different sizes


I'd like to create such a structure:

struct Arrays{
    typedef unsigned char byte_t;

    std::array<byte_t, X>& get(int x, int y)
    {
        switch(x){
            case 1: return arr_1.at(y);
            case 2: return arr_2.at(y);
            ... up to ...
            case 50: return arr_50.at(y);
            default: break;
        }
    }

    std::vector<std::array<byte_t, 1>> arr_1;
    std::vector<std::array<byte_t, 2>> arr_2;
    ... up to ....
    std::vector<std::array<byte_t, 50>> arr_50;
};

I know that

std::array<byte_t, 1>

and

std::array<byte_t, 2>

are different structures, so this is impossible in this way. How can I managed with it in different manner? Do I have to return byte_t* from the method?


Solution

  • As the previous comments have said, the array size is a compile-time constant. The only way to achieve this then is to pass x as a template argument. How about the following? (For convenience, I have defined a class vector_of_arrays to hold your vectors arr_1 to arr_50 in such a way that I can refer to each one by the number rather than having to switch and give a name)

    #include <array>
    #include <type_traits>
    #include <vector>
    
    template <typename type, size_t max_size>
    class vector_of_arrays : public std::vector<std::array<type, max_size>>
    {
    private:
        vector_of_arrays<type, max_size - 1> _next;
    
    public:
        template <size_t size>
        typename std::enable_if<(size < max_size), vector_of_arrays<type, size>&>::type
        get_vector_for_size()
        {
            return _next.get_vector_for_size<size>();
        }
    
        template <size_t size>
        typename std::enable_if<(size == max_size), vector_of_arrays<type, size>&>::type
        get_vector_for_size()
        {
            return *this;
        }
    };
    
    template <typename type>
    class vector_of_arrays<type, 1> : public std::vector<std::array<type, 1>>
    {
    public:
        template <size_t size>
        typename std::enable_if<(size == 1), vector_of_arrays<type, size>&>::type
        get_vector_for_size()
        {
            return *this;
        }
    };
    
    struct arrays
    {
        typedef unsigned char byte_t;
        vector_of_arrays<byte_t, 50> va;
    
        template <size_t x>
        std::array<byte_t, x>& emplace_array()
        {
            va.get_vector_for_size<x>().emplace_back();
            return *(va.get_vector_for_size<x>().end() - 1);
        }
    
        template <size_t x>
        std::array<byte_t, x>& get(int y)
        {
            return va.get_vector_for_size<x>().at(y);
        }
    };
    

    To test this code, you could do something like

    arrays foo;
    auto& arr1 = foo.emplace_array<3>();
    arr1[0] = 1.;
    arr1[1] = 2.;
    arr1[2] = 3.;
    
    auto& arr2 = foo.get<3>(0);
    std::cout << arr2[0] << ' ' << arr2[1] << ' ' << arr2[2] << std::endl;