jeudi 10 septembre 2020

Can't call class function from a template function class inside a template class

I am trying to create a pattern where an abstract class has several methods where all get the same member. The derived implementation is on a specific member type and the pattern should redundant the need to downcast the types.

The following is not working:

Assume these type classes

//--------------
// Data classes
//--------------
class TypeA
{
    virtual int a() = 0;
};

class TypeB : public TypeA
{
    int a() override
    {
        return 5;
    }
};

Pattern:

//-------------------
// Base action class.
//-------------------
class ExampleBase
{
public:
    virtual bool f1(TypeA& val) = 0;

    virtual bool f2(TypeA& val) = 0;
};

//----------------------------------
// Base specific Typed action class.
//----------------------------------
template<class T>
class ExampleSpecific1 : public ExampleBase
{
public:
    virtual bool specificF1(T& specificVal) = 0;

    virtual bool specificF2(T& specificVal) = 0;

    bool f1(TypeA& val) override
    {
        return fRunner<bool, specificF1>(val, false);
    }

    bool f2(TypeA& val) override
    {
        return fRunner<bool, specificF2>(val, false);
    }

private:

    // Run the specific function with the specific type
    template<class S, S (*pf)(T& val)>
    S fRunner(TypeA& val, S defaultValue)
    {
        S ret = defaultValue;
        T& specificVal = dynamic_cast<T&>(val);

        if (&specificVal != nullptr) {
            ret = pf(specificVal);
        }

        return ret;
    }
};

The implementation

//----------------------
// Class Implementation.
//----------------------
class ExampleImpl : public ExampleSpecific1<TypeB>
{
public:
    bool specificF1(TypeB& specificVal) override
    {
        // Do something
    }

    bool specificF2(TypeB& specificVal) override
    {
        // Do something
    }
};

Usage:

//-----------
// Class Use.
//-----------
void main()
{
    ExampleImpl impl;
    TypeB myVal;

    TypeA& myBaseVal = myVal;
    impl.f1(myBaseVal);
    impl.f2(myBaseVal);
}

I get the following compilation error:

error C2672: 'ExampleSpecific1<TypeB>::fRunner': no matching overloaded function found
note: while compiling class template member function 'bool ExampleSpecific1<TypeB>::f2(TypeA &)'
note: see reference to function template instantiation 'bool ExampleSpecific1<TypeB>::f2(TypeA &)' being compiled
note: see reference to class template instantiation 'ExampleSpecific1<TypeB>' being compiled
error C2975: 'pf': invalid template argument for 'ExampleSpecific1<TypeB>::fRunner', expected compile-time constant expression
note: see declaration of 'pf'

A function template that does work (when the function is not inside a class):

Based on the previous example:

template<class T, bool (*pf1)(T& Val), bool (*pf2)(T& Val)>
class ExampleSpecific2 : public ExampleBase
{
public:
    bool f1(TypeA& val) override
    {
        bool ret = false;
        T& specificVal = dynamic_cast<T&>(val);

        if (&specificVal != nullptr) {
            ret = pf1(specificVal);
        }
        return ret;
    }

    bool f2(TypeA& val) override
    {
        bool ret = false;
        T& specificVal = dynamic_cast< T&>(val);

        if (&specificVal != nullptr) {
            ret = pf2(specificVal);
        }
        return ret;
    }
};

External functions:

bool extF1(TypeB& val)
{
    // Do something.
}

bool extF2(TypeB& val)
{
    // Do something.
}

Useage:

//-----------
// Class Use.
//-----------
void main()
{
    TypeB myVal;
    TypeA& myBaseVal = myVal;

    ExampleSpecific2<TypeB, extF1, extF2> impl2;

    impl2.f1(myBaseVal);
    impl2.f2(myBaseVal);
}

In the example that does not work, I can implement the down cast in each of the implementation and then it works, but it is ugly and is not generic. In the working example I want the function to be in the internal implementation of the class and not external to it, this is important in more complicated scenarios, where the base class is calling several of the derived methods.

  • BTW I don't like this post Title, if you have a better suggestion that I like, this will be great.

Aucun commentaire:

Enregistrer un commentaire