I am writing an arbitrary-ranked tensor (multidimensional array) class in C and would like to have static and dynamic memory versions of it. However, I am struggling to think of a way to avoid having to duplicate methods in the static/dynamic versions of the class, considering that the underlying data containers would be different. I hope the following minimal example illustrates my point:
// Product function
template <typename ...data_type>
constexpr auto Product(data_type ..._values)
{
return (_values * ...);
}
// Static memory version
template <class t_data_type, unsigned ...t_dimensions>
class StaticTensor
{
private:
std::array<t_data_type, Product(t_dimensions...)> Entries; // Store entries as contiguous memory
public:
StaticTensor() = default;
~StaticTensor() = default;
void StaticMethod()
{
// Some code that operates on Entries.
}
};
// Dynamic memory version
template <class t_data_type>
class DynamicTensor
{
private:
std::vector<t_data_type> Entries;
public:
DynamicTensor() = default;
~DynamicTensor() = default;
template <typename ...t_dimensions>
void Resize(t_dimensions ...dims)
{
Entries.resize(Product(dims...));
}
void DynamicMethod()
{
// Some code that operates on Entries.
}
};
I have considered inheritance-based/polymorphic approaches, but it seems that I'd still have to implement separate methods in each of the specialised classes. I would ideally like all the methods to operate on the underlying iterators in std::array
and std::vector
, without having to worry about which data container they belong to. Can anyone suggest how I can go about doing this?
CodePudding user response:
You can use CRTP techniques to create a TensorBase
, then convert *this
to Derived&
to access the derived class's Entries
inside Method()
:
template <class Derived>
class TensorBase
{
public:
void Method()
{
auto& Entries = static_cast<Derived&>(*this).Entries;
// Some code that operates on Entries.
}
};
Then your StaticTensor/DynamicTensor
can inherit TensorBase
to obtain the Method()
. In order to enable the base class to access private members, you also need to set the base class as a friend:
// Static memory version
template <class t_data_type, unsigned ...t_dimensions>
class StaticTensor
: public TensorBase<StaticTensor<t_data_type, t_dimensions...>>
{
using Base = TensorBase<StaticTensor<t_data_type, t_dimensions...>>;
friend Base;
private:
std::array<t_data_type, Product(t_dimensions...)> Entries;
public:
StaticTensor() = default;
~StaticTensor() = default;
};
// Dynamic memory version
template <class t_data_type>
class DynamicTensor
: public TensorBase<DynamicTensor<t_data_type>>
{
using Base = TensorBase<DynamicTensor<t_data_type>>;
friend Base;
private:
std::vector<t_data_type> Entries;
public:
DynamicTensor() = default;
~DynamicTensor() = default;
};