Template data structure - Access to getters and setters of a template class deriving from an abstract class

Viewed 108

in a finite element code, I’ve a data structure to manipulate the value of a field (coordinates, temperature, ...) at a point. This structure is basic and looks like this:

class point_data
{
public:
    void allocate(int ldim); // allocation of data;
    const double* get_data() const { return data; }
    void set_data(const double* ldata); // for example
    …
protected:
    double* data;
    …
}

I then use objects of type point_data* to evaluate expressions, for example to impose boundary conditions. To be concrete, if x, y are the coordinate fields, and I impose a temperature T equal to sin(y) on a boundary defined by x == 0, then I have a “sin” operator object, whose evaluate method takes a point_data* argument (in fact a point_data**) and return a point_data* containing the result. Same thing for my “==” object, which also takes a point_data** argument (array of two point_data* this time) and return the result.

As my structure for supporting data of global fields is already template, and since it is this structure that creates point_data objects, I wanted templatized the class point_data. Then, I tried:

class abstract_point_data
 {
 protected:     
     int dim;     
 public:
     abstract_point_data() : dim{0} { ; }
     virtual ~abstract_point_data() { ; }
     
     const int& get_dim() const { return dim; }
     void set_dim(int ldim) { dim = ldim; }
     
     virtual void allocate(int ldim=0) = 0;
     // get_data ??? set_data ???
 };
 
 template <typename T>
 class point_data : public abstract_point_data
 {
 public:
     point_data() : my_data{0}, allocated{false} { ; }
     virtual ~point_data() { delete_data(); }
     
     virtual void set_data(const T* ldata);
     virtual const T* get_data() const { return my_data; }

     virtual void allocate(int ldim);
     
 protected:
     
     T* my_data;
     bool allocated;
     
    void delete_data() { if ( allocated ) { delete [] my_data; allocated = false; } }
 };
 
template <typename T> 
 void point_data<T>::allocate(int ldim)
{
    dim = ldim;
    delete_data();
    my_data = new T[ldim];
    allocated = true;
}


template <typename T>
void point_data<T>::set_data(const T* ldata)
{
    for (int i=0; i<dim; i++)
        my_data[i] = ldata[i];
} 

with the main:

    abstract_point_data* mp = new point_data<double>;
    mp->allocate(1);
    double val = 1.0;
   ( (point_data<double>*) mp)->set_data(&val);    
    
    delete mp;
    

This way, the evaluate method of my operator objects can have an abstract_point_data** as an argument. In can also create the result object, because I know for example that a sine provide a double, or by cloning one of the arguments. But, that’s it. In general, I don’t know the type of my arguments in the evaluate function and therefore I can’t (of course) access the getters and setters. I’ve tried things like CRTP, decltype(auto), or even stupid stuff like std::tuple<bool,int,double> my_types; + decltype(std::get<get_my_index()>(my_types)) to store the type of my template “in” abstract_point_data, but nope, the C++ rules seem inviolable.

Can anyone give me an idea or reference to help me redefine (if possible) an acceptable templatized data structure? Thanks in advance.

In addition : the reason of using abstract_point_data was (if it worked) to only deal with abstract_point_data objects (as method arguments, in containers...), instead of dealing with point_data<int>, point_data<double>, ... (but maybe I missed something).

1 Answers

It depends on what you want to achieve. The abstract base pattern you currently have is indeed useful if you want to have something like a std::vector<std::unique_ptr<abstract_point_data>>. Echoing Ted Lyngmo's comment, you can avoid the potential headaches of memory management and let a container or smart pointer do it for you. The smart pointers support polymorphism, so you could use the above vector to store a std::unique_ptr<point_data<double>>.

To get your functions/objects to work with the derived types, you can make them templates as well, for example:

template<typename T, typename U>
bool operator==(point_data<T> const& x, point_data<U> const& y) 
{ return x.get_data() == y.get_data(); }
Related