Firstly, I think you may want to evaluate your design. Like with functors, it's rarely required to code semi-rigid type wrappers around your generic type arguments.
However, if you do find you have a need for this, here's a solution that uses boost::variant
:
template <typename T>
struct GenericArray
{
template <size_t N> using array_t = boost::multi_array<T, N>;
template <typename Rhs> GenericArray& operator=(Rhs&& rhs) {
_storage = std::forward<Rhs>(rhs);
return *this;
}
template <size_t N> array_t<N> & get() { return boost::get<array_t<N> >(_storage); }
template <size_t N> array_t<N> const& get() const { return boost::get<array_t<N> >(_storage); }
private:
typename detail::make_generic_array_storage<T>::type _storage;
};
The get<>
member function throws a boost::bad_get
exception if you get the dimension wrong at runtime.
Now, the trick is, of course, how _storage
is implemented. I generate a variant over a list of array dimensions using a bit of Boost MPL magic:
namespace detail {
namespace mpl = boost::mpl;
template <typename T, size_t Mindim = 1, size_t Maxdim = 5>
struct make_generic_array_storage
{
template <size_t N> using array_t = boost::multi_array<T, N>;
template<typename N> struct to_array_f { typedef array_t<N::value> type; };
using list = typename mpl::transform<
mpl::range_c<size_t, Mindim, Maxdim>,
to_array_f<mpl::_1>,
mpl::back_inserter<mpl::vector<> >
>::type;
using type = typename boost::make_variant_over<list>::type;
};
}
Nothing overly complicated, if you look at it from a high level :)
Next up: demo! See it Live On Coliru
GenericArray<double> arr;
arr = array_3d;
try { auto& ref3 = arr.get<3>(); }
catch (boost::bad_get const& e) { std::cout << "L:" << __LINE__ << " " << e.what() << "\n"; }
try { auto& ref2 = arr.get<2>(); } // this fails
catch (boost::bad_get const& e) { std::cout << "L:" << __LINE__ << " " << e.what() << "\n"; }
arr = array_2d;
try { auto& ref2 = arr.get<2>(); } // this succeeds
catch (boost::bad_get const& e) { std::cout << "L:" << __LINE__ << " " << e.what() << "\n"; }
std::cout << "Done";
Prints:
L:58 boost::bad_get: failed value get using boost::get
Done
as expected.
Bonus: To implement more array-like operations over the variant storage, have a look here:
which touch on this topic