Search code examples
c++arraysconstants

Is there any C/C++ way for concatenating constant arrays other than macro?


I want to declare some constant arrays which have some shared part, currently I have to use macros to achieve this.

I want:

struct Param{int a; int b;};

const static std::vector<Param> SHARED_DATA = {
    {1,2},
    {3,4},
};

const static std::vector<Param> TEST_DATA_ONE = {
    {5,6},
    SHARED_DATA,
    {7,8},
};

const static std::vector<Param> TEST_DATA_TWO = {
    {9,10},
    SHARED_DATA,
    {11,12},
};

Obviously the above code can not compile.

What I'm using now:

#define SHARED_DATA \
    {1,2}, \
    {3,4}

#define TEST_DATA_ONE \
    {5,6}, \
    SHARED_DATA, \
    {7,8} \

#define TEST_DATA_TWO \
    {9,10}, \
    SHARED_DATA, \
    {11,12} \

const Param PACK_DATA_ONE[] = {
    TEST_DATA_ONE,
    {1,2},
};

const Param PACK_DATA_TWO[] = {
    TEST_DATA_TWO,
    {1,2},
};

Is there any C/C++ way for concatenating constant arrays other than macro?


Solution

  • std::vector is more of a runtime thing so you could make runtime functions for concatenating. However if you use std::array then you can combine at compile time.

    #include <array>
    #include <type_traits>
    
    // Compile time function to add one value to an array
    template<typename type_t, std::size_t N>
    static constexpr void append(std::array<type_t, N>& arr, type_t& value, std::size_t& offset)
    {
        arr[offset] = value;
        offset++;
    }
    
    // Compile time function to add another array to the array
    template<typename type_t, std::size_t N, std::size_t M>
    static constexpr void append(std::array<type_t, N>& arr, const std::array<type_t,M>& values, std::size_t& offset)
    {
        for (std::size_t n = 0; n < values.size(); ++n, ++offset)
        {
            arr[offset] = values[n];
        }
    }
    
    // Create a variadic template function to build an array
    // from a mix of values and other arrays
    template<typename type_t, typename... args_t>
    static constexpr auto build_array(args_t&&... args)
    {
        // calculate the size of the final array by summing
        // the sizes of the arguments divided by the size of the underlying type
        const auto size_v = ((sizeof(args_t)/sizeof(type_t)) + ...);
        std::array<type_t, size_v> values{};
    
        // start adding values at the start of the array
        std::size_t offset{0ul};
    
        // the compiler will select the correct append function to use at compile time
        // what you see here is a fold expression and it will "loop" over all args
        (append(values, args, offset),...);
    
        return values;
    }
    
    
    int main()
    {
        constexpr std::array arr1{ 2, 3 };
        constexpr std::array arr2{ 5, 6 };
    
        constexpr auto arr3 = build_array<int>(1, arr1, 4, arr2, 7);
    
        static_assert(arr3.size() == 7);
        static_assert(arr3[0] == 1);
        static_assert(arr3[1] == 2);
        static_assert(arr3[2] == 3);
        static_assert(arr3[3] == 4);
        static_assert(arr3[4] == 5);
        static_assert(arr3[5] == 6);
        static_assert(arr3[6] == 7);
    
        return 0;
    }