Access data from base class with the correct typecasting of it derived templated class

I have the following scenario:

class ScalarField
{
   void* _buffer; //Array for data.
};

And a derived class:

template <typename T> 
class ScalarFieldT : public ScalarField
{
    ScalarFieldT(int size)
    {
       _data = new T[size];
       _buffer = _data;
    }

   T& get(int index)
   {
       return _data[index];
   }

   T* _data; // Typed array for data
};

Notice that T can assume only basic types such float, int, double and so on.

This is a very old legacy code, so I don't have too much flexibility to adjust it properly doing a better design. What a need to do is to access the data from ScalarField::_buffer with the correct typecasting of it derived class.

Something like this:

void main()
{
   int n = TOTAL_SIZE;
   ScalarFieldT<int> typedScalarField(n);

   ScalarField* scalarField = &typedScalarField;

   // This is what I need to do:
   int index = ELEMENT_INDEX;        
   float value = scalarField->method(index); // Get the value from base class correctly converted from int to float, for example.       
}

The point is, I only have access to the base class abstraction, but I need to get the value from _buffer converted to another simple data type, such float, int, uchar, etc.

What do you guys recommend?

Thanks in advance!

Upvotes: 1

Views: 86

Answers (2)

skypjack
skypjack

Reputation: 50540

You can use a type that provides a set of cast operators as a return type for method.
It follows a minimal, working example:

#include<memory>

class ScalarField {
protected:
    struct Value {
        virtual operator float() const = 0;
        virtual operator int() const = 0;
        // ...
    };

public:
    virtual const Value & method(int i) = 0;

protected:
    void* buffer;
};

template <typename T> 
class ScalarFieldT : public ScalarField {
    struct TValue: Value {
        TValue(T value): value{value} {}
        operator float() const override { return float(value); }
        operator int() const override { return int(value); }
        // ...

    private:
        T value;
    };

public:
    ScalarFieldT(int size) {
        data = new T[size];
        buffer = data;
    }

    T& get(int index) {
        return data[index];
    }

    const Value & method(int i) {
    std::make_unique<TValue>(data[i]);
}

private:
    std::unique_ptr<Value> value;
    T* data;
};

int main() {
    ScalarFieldT<int> typedScalarField(10);
    ScalarField* scalarField = &typedScalarField;
    float f = scalarField->method(2);
    int i = scalarField->method(5);
}

Upvotes: 1

Stephan Lechner
Stephan Lechner

Reputation: 35154

Hm - please don't throw stones on me for a probably silly answer, but...

Let's assume that T is restricted to numeric data types, e.g. int, long, double, float, and let's further assume that the largest integral value to be stored is about 2^53. Then double could serve as an data exchange data type, to which any other data type can be converted without losing precision.

Then one could define virtual double ScalarField::method(int index) = 0, which is then overloaded accordingly in the typed variants, which do an "up"-cast from actual type T to double.

Unless you are allowed to encapsulate the values in an object (e.g. struture/class Value), the following code using the "use double as data exchange data type" could work:

class ScalarField
{
public:
    void* _buffer; //Array for data.
    virtual double method(int index) = 0;
};

template <typename T>
class ScalarFieldT : public ScalarField
{
public:
    ScalarFieldT(int size)
    {
        _data = new T[size];
        _buffer = _data;
    }

    virtual double method(int index) { return get(index); }

    T& get(int index)
    {
        return _data[index];
    }

    T* _data; // Typed array for data
};

Upvotes: 0

Related Questions