1
votes

I am trying to implement a particular template design in C++, and ran into an issue that I wanted to make is solvable. Here is the setup, with explanation of the issue to follow.

I declare an templated abstract base class as follows:

template <class TModel, class TVertex>
class AttributeEvaluator2f
{
public:
    virtual void evaluate( TModel& model, TVertex& v, float x, float y ) = 0;
};

I then want to implement sub classes that specialize one of the template parameters, like so:

template <class TVertex>
class SurfacePositionFromSphere : public AttributeEvaluator2f<Sphere3f,TVertex>
{
public:
    virtual void evaluate( Sphere3f& sphere, TVertex& v, float theta, float phi )
    {
        sphere.SamplePosition( v.position, theta, phi );
    };
};

I would then instantiate an object based on these classes like this:

SurfacePositionFromSphere<BasicVertexDX11::Vertex> attribute();

The compilation error indicates that I never implemented the abstract method the original base class. This is probably because in the subclass, I am declaring the method directly with the first parameter as already specialized (Sphere3f in this case).

So the question is, is it possible to override an abstract base class method argument's type in this way? If not, is there a similar mechanism that could be used to provide similar functionality?

Thanks in advance for your help!

EDIT: The exact error message is gigantic due to the template unwrapping, but here is the major portion of it for your reference: error C2259: 'Glyph3::AttributeEvaluator2f' : cannot instantiate abstract >class 1> with 1> [ 1> TModel=Glyph3::Sphere3f, 1> TVertex=Glyph3::BasicVertexDX11::Vertex 1> ] 1> due to following members: 1> 'void Glyph3::AttributeEvaluator2f::evaluate(TModel &,TVertex &,float,float)' : is abstract 1> with 1> [ 1> TModel=Glyph3::Sphere3f, 1> TVertex=Glyph3::BasicVertexDX11::Vertex 1> ] 1> c:\users\zij1fh\documents\visual studio 2012\projects\hg3\trunk\hieroglyph3\source\rendering\attributeevaluator2f.h(26) : see declaration of 'Glyph3::AttributeEvaluator2f::evaluate' 1> with 1> [ 1> TModel=Glyph3::Sphere3f, 1> TVertex=Glyph3::BasicVertexDX11::Vertex 1> ] 1> c:\program files (x86)\microsoft visual studio 11.0\vc\include\xmemory0(751) : see reference to function template instantiation 'void std::allocator<_Ty>::construct<_Objty,_Ty>(_Objty *,_V0_t &&)' being compiled 1> with 1> [ 1> _Ty=Glyph3::AttributeEvaluator2f, 1> _Objty=Glyph3::AttributeEvaluator2f, 1> _V0_t=Glyph3::AttributeEvaluator2f 1> ] 1> c:\program files (x86)\microsoft visual studio 11.0\vc\include\xmemory0(751) : see reference to function template instantiation 'void std::allocator<_Ty>::construct<_Objty,_Ty>(_Objty *,_V0_t &&)' being compiled 1> with 1> [ 1> _Ty=Glyph3::AttributeEvaluator2f, 1> _Objty=Glyph3::AttributeEvaluator2f, 1> _V0_t=Glyph3::AttributeEvaluator2f 1> ] 1> c:\program files (x86)\microsoft visual studio 11.0\vc\include\xmemory0(903) : see reference to function template instantiation 'void std::allocator_traits<_Alloc>::construct<_Ty,_Ty>(std::allocator<_Ty> &,_Objty *,_V0_t &&)' being compiled

EDIT2: The usage indicated above is not correct (as pointed out by Andy). This is the exact usage that I have:

VertexEvaluator2f< Sphere3f, BasicVertexDX11::Vertex > evaluator;
evaluator.Evaluators.push_back( SurfacePositionFromSphere<BasicVertexDX11::Vertex>() );

Within the VertexEvaluator2f class, I iterate over the attributes to produce a vertex. Here is the declaration of that class as well, for completeness:

template <class TModel, class TVertex>
class VertexEvaluator2f
{
public:
    void SetModel( TModel& model ) {
        m_Model = model;
    };

    void evaluate( TVertex& v, float x, float y ) {
        for ( auto& evaluator : Evaluators ) {
            evaluator.evaluate( m_Model, v, x, y );
        }
    };

    std::vector< AttributeEvaluator2f< TModel, TVertex > > Evaluators;

protected:
    TModel m_Model;
};
1
What is the exact error message you are getting, an what is the associated line? The design should work - but the declaration you show does not instantiate an object, rather declare a function called attributeAndy Prowl
It is a ridiculously long error message, but I'll add that to the description above...Jason Zink
So what is the error?Andy Prowl

1 Answers

2
votes

So the question is, is it possible to override an abstract base class method argument's type in this way?

Yes, it is possible. The definitions of your classes look correct, and you can see them being compiled correctly here (where I used some dummy Vertex and Sphere3f class).

The problem is most likely (or at least also) in the fact that this line:

SurfacePositionFromSphere<BasicVertexDX11::Vertex> attribute();

Is actually declaring a function named attribute that accepts no argument and returns an object of type SurfacePositionFromSphere<BasicVertexDX11::Vertex>.

When you later try to access member functions of that object, the compiler complains (after all, you're trying to access some member functions of a function pointer, which is non-sense for the compiler).

In order to create an object instead of declaring a function, remove the pair of parentheses after the object's name:

SurfacePositionFromSphere<BasicVertexDX11::Vertex> attribute;
//                                                          ^
//                                             No parentheses

UPDATE:

You are declaring your vector of attributes this way:

std::vector< AttributeEvaluator2f< TModel, TVertex > > Evaluators;

std::vector<>, like all standard containers, has value semantics. This means that it will contain objects (and not pointers to objects!) of type AttributeEvaluator2f< TModel, TVertex >, and that class is indeed abstract.

What you want in this case is to use (smart) pointers, for instance:

#include <memory>

template <class TModel, class TVertex>
class VertexEvaluator2f
{
    // ...

    void evaluate( TVertex& v, float x, float y ) {
        for ( auto& evaluator : Evaluators ) {
            evaluator->evaluate( m_Model, v, x, y );
        //           ^^
        }
    };

    std::vector<
        std::shared_ptr<AttributeEvaluator2f<TModel, TVertex>>
    //  ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
        > Evaluators;

protected:
    TModel m_Model;
};

And this is how you would add elements to the collection:

VertexEvaluator2f< Sphere3f, BasicVertexDX11::Vertex > evaluator;
evaluator.Evaluators.push_back( 
    std::make_shared<SurfacePositionFromSphere<BasicVertexDX11::Vertex>>()
    );