Adiel
Adiel

Reputation: 1215

Calling derived template class method from base non-template class

I'm trying to create the following flow:

Having a 'Base' class, with no params or functionality, just so i can hold Base pointers in a method.

Its derived class, is a template, which implements operator() on given template argument object type.

I'm trying, by using a pointer to base class, call the derived class operator(), in run-time.

I've tried implementing it using CRTP (https://en.wikipedia.org/wiki/Curiously_recurring_template_pattern) But that doesn't seem to work the-other-way-around - when the derived class is a class template.

Here is my code:

class Base {};

template<typename Ref_Obj, typename Obj_Type>
class Derived : public Base {
    private:
        typedef bool (Ref_Obj::*Func_p)(Obj_Type) const;
        Func_p m_func;
        const Ref_Obj& m_db;

    public:
        Derived<Ref_Obj, Obj_Type>(const Ref_Obj& db, Func_p func): m_db(base), m_func(filter) {}

        inline bool operator()(const Obj_Type& obj) const {
            return (m_db.*m_func)(obj);
        }
}

Now, usage is in another class template, that contains vector of Base class pointers, as follows, and have its own operator() :

template<typename Obj_Type2> /* Upon usage, Obj_Type2 is the same as Obj_Type, this is just to differ the two in the code here */
class BlaBla {
    private:
        std::vector<const Base *> m_vec;

    public:
        /* relevant constructors ... */

        inline bool operator()(const Obj_Type2 &obj) const {
            for(std::vector<const Base *>::const_iterator it = m_vec.begin(); it != m_vec.end(); ++it) {

                /*** This is the problematic line V ***/
                if( (*it).operator()(obj) ) { /* DO SOMETHING */ }                    
        }
}

Of course the compiler is complaining that there is no matching function to call for in the problematic line that is marked in the code below, but i can't figure out a way to do the relevant call.

1st Solution that came to mind, is to create a virtual operator()(...), with a specific object type e.g. virtual operator()(const uint32_t &obj) in Base class, which works, but my intention is that operator()(...) will receive a variety of object types, and stating a virtual method for each of them is not elegant and seems to break all the template concept i want to implement here.

2nd Solution that came to mind, is somehow passing Ref_Obj and Obj_Type typenames to Base class, to be used in sort of interface method, that will use static_cast to call the appropriate Derived operator (As in CRTP) - But that doesn't seem to work because Derived class is a class template, and also BlaBla class doesn't directly know Ref_Obj typename.

Is there any other way to make the appropriate call to Deriver operator()

Upvotes: 2

Views: 1610

Answers (1)

Sam Varshavchik
Sam Varshavchik

Reputation: 118292

There is no clear clean way to do this. The fundamental problem is that there is no such thing as a virtual template method.

The cleanest way I can think of doing this that implements complete type erasure is with a dynamic cast, and a virtual method:

class Base {
public:

    virtual bool operator()(const Base &) const=0;
};

template<typename Ref_Obj, typename Obj_Type>
class Derived : public Base {
    private:
        typedef bool (Ref_Obj::*Func_p)(Obj_Type) const;
        Func_p m_func;
        const Ref_Obj& m_db;

    public:
        Derived<Ref_Obj, Obj_Type>(const Ref_Obj& db, Func_p func): m_db(base), m_func(filter) {}

        inline bool operator()(const Base& obj) const override {
            const Obj_Type *derived_obj=dynamic_cast<const Obj_Type *>(&obj);

            if (!derived_obj)
            {
                  throw; // Or maybe return false, or something...
            }
            return (m_db.*m_func)(*derived_obj);
        }
};

Obj_Type must also be derived from Base. This gets the job done at runtime, but there is no compile-time type-checking.

The other approach is to bite the bullet, and forego 100% type erasure:

template<typename Obj_Type>
class Base {
public:

    virtual bool operator()(const Obj_Type &) const=0;
};

template<typename Ref_Obj, typename Obj_Type>
class Derived : public Base<Obj_Type> {
    private:
        typedef bool (Ref_Obj::*Func_p)(Obj_Type) const;
        Func_p m_func;
        const Ref_Obj& m_db;

    public:
        Derived<Ref_Obj, Obj_Type>(const Ref_Obj& db, Func_p func): m_db(base), m_func(filter) {}

        inline bool operator()(const Obj_Type& obj) const override {
            return (m_db.*m_func)(obj);
        }
};

So, you can still abstract away operator() on some object to its pure interface, and define the details in the subclass.

Or, another alternative would be a combination of the two:

class SuperBase {
public:

    virtual bool operator()(const Base &) const=0;
};

template<typename Obj_Type>
class Base : public SuperBase {
public:

    virtual bool operator()(const Obj_Type &) const=0;

    bool operator()(const Base &obj) const override
    {
          // Do the dynamic cast check, and forward it to the other
          // operator().
    }
};

Upvotes: 1

Related Questions