Home > Software design >  How to return multi- dimensional-vector of given dimension and type?
How to return multi- dimensional-vector of given dimension and type?

Time:10-27

Is it possible in modern c to write a function that, given a number n and a type, return a n-dimensional vector type.

 1,float -> return std::vector<float>
 2,float -> return std::vector<std::vector<float>>

and so on...

CodePudding user response:

Here is a compile time approach using template specialization. We apply std::vector<> recursively until we reach the specialization for N == 0, where the final type is set to T:

#include <type_traits>
#include <vector>

template<std::size_t N, typename T>
struct recursive_vector {
    using type = std::vector<typename recursive_vector<N - 1, T>::type>;
};
template<typename T>
struct recursive_vector<0, T> {
    using type = T;
};

template<std::size_t N, typename T>
using vec = recursive_vector<N, T>::type;

so vec<3, float> becomes vector<vector<vector<float>>>:

int main() {
    vec<3, float> v;

    using std::vector;
    static_assert(std::is_same_v<decltype(v), vector<vector<vector<float>>>>);
}

Try out the code, here.

CodePudding user response:

Is it possible in modern C to write a function that given a number n and a type return a n-dimensional vector type?

Compile time

If n is known at compile time, the job easy, which is already given in the other answer. Alternatively, you could also do:

#include <vector>

template<typename T, std::size_t N>
struct vec : public vec<std::vector<T>, N - 1u> {};

template<typename T> struct vec<T, 0u> { using type = T; };
template<typename T, std::size_t N> using vec_t = typename vec<T, N>::type;

Now vec_t<float, 5u> means std::vector<std::vector<std::vector<std::vector<std::vector<float>>>>>

(See a live demo in godbolt.org)


Run time

When n is run time depended, one possible solution is to wrap the possible multidimensional vectors types to std::variant (Since ).

Following is an example code.

#include <vector>
#include <variant> // std::variant

// type aliases for the multi-dim vectors
template<typename T> using Vec1D = std::vector<T>;
template<typename T> using Vec2D = Vec1D<Vec1D<T>>;
template<typename T> using Vec3D = Vec1D<Vec2D<T>>;
// ... so on (hope that you wouldn't need more than 4 or 5!)

template<typename T>
using VecVariants = std::variant<Vec1D<T>, Vec2D<T>, Vec3D<T>/*, so on ...*/>;

template<typename T> auto getVector_of(const std::size_t n)
{
    if (n == 1) return VecVariants<T>{Vec1D<T>{}};
    if (n == 2) return VecVariants<T>{Vec2D<T>{}};
    if (n == 3) return VecVariants<T>{Vec3D<T>{}};
    // ... so on
    return VecVariants<T>{Vec3D<T>{}}; // some default return!
}

template<typename T> void doSomething(const VecVariants<T>& vec)
{
    std::visit([](auto&& arg) {
        using Type = std::decay_t<decltype(arg)>;
        if constexpr (std::is_same_v<Type, Vec1D<T>>)
            std::cout << "... do something with Vec1D\n";
        else if constexpr (std::is_same_v<Type, Vec2D<T>>)
            std::cout << "... do something with Vec2D\n";
        else if constexpr (std::is_same_v<Type, Vec3D<T>>)
            std::cout << "... do something with Vec3D\n";
        // .... so on
        else
            std::cout << "... default!\n";
        }, vec);
}

Now you can pass run time n

int main()
{
    std::size_t n{ 5 };
    while (n)
        doSomething(getVector_of<float>(n--)); // n must be 0u < n <= MAX_n you defined!
}

(See a live demo in godbolt.org)

  • Related