Introduction
A month ago I published a custom implementation of std::visit that stores function pointers in a flat one-dimensional array. If you haven’t read it, start there - this post builds directly on top of it.
visit implementation using flat arrays
For completeness, this is the implementation of visit using a one-dimensional array.
visit implementation using multi-dimensional arrays
A multidimensional array is an array of T elements, where the type T is a generic type - either a scalar or an array itself. Hence, a multidimensional array can be easily coded up as follows:
namespace qc{
/**
* @brief A light n-dimensional array.
*/
template<typename T, std::size_t Size>
struct poly_array{
T m_buffer[Size] = {};
constexpr T& operator[](std::size_t idx){
return m_buffer[idx];
}
// Base case
decltype(auto) constexpr at(std::size_t index){
return m_buffer[index];
}
// Case with a pack of indices
template<typename... Indices>
decltype(auto) constexpr at(std::size_t index, Indices... indices){
return m_buffer[index].at(indices...);
}
};
}The .at(std::size_t, Indices...) method accepts a variadic pack of indices. As a concrete example, if I define a multi-dimensional array a of dimensions \(2 \times 3 \times 4\), a is a collection of two \(3 \times 4\) matrices. So, a.at(0,1,2) is (1,2)-th element in the 0-th matrix. Hence, we should recursively invoke a[0].at(1,2).
Next, we write a helper struct called dispatcher. The dispatcher has a static constexpr method called dispatch that invokes any callable on a pack of variants. It’s basically a smart wrapper over std::invoke, that checks the return type of the callable at compile-time and dispatches to an appropriate branch.
template<size_t... Indices>
struct dispatcher{
template<typename Func, typename... Vs>
static constexpr auto dispatch(Func&& f, Vs&&... vs){
using return_type_t = decltype(std::forward<Func>(f)(std::get<0>(std::forward<Vs>(vs))...));
if constexpr(std::is_same_v<return_type_t, void>){
std::invoke(std::forward<Func>(f), std::get<Indices>(std::forward<Vs>(vs))...);
}else{
return std::invoke(std::forward<Func>(f), std::get<Indices>(std::forward<Vs>(vs))...);
}
};
};Here is the complete code-listing.