Component 10
Component 10

Reputation: 10497

Satisfying pure virtual function contract with template class inheritance

Apologies if this has been answered before; I scoured Stack Overflow but couldn't quite find anything like it. I have a feeling that what I'm asking is not possible, but I'm sure there must be a way to achieve it.

I'm working with a base class which contains a number of pure virtual functions clustered into logically related groups. Instead of simply implementing these in my derived class, I'd ideally like to group the functions into classes dealing with related functionality and then pull these into my derived class.

I tried this using (simplified example below) but receive the error below:

// The base class - I can't change this
class Base {
    public:
    virtual void toImplement(double a) = 0;
};

// Implements logically grouped functionality required by the base class
class Implementor {
    public:
    virtual void toImplement(double a) {}
};

// Concrete derived class, satisfying Base functional requirements by
// (somehow) drawing on the Implementor class functionality.
template <typename Type>
class Derived : public Base, Type {
};

int main() {
    Derived<Implementor> a; // Fails
}

This fails with the error:

error: variable type 'Derived<Implementor>' is an abstract class
    Derived<Implementor> a;
                         ^
note: unimplemented pure virtual method 'toImplement' in 'Derived'
    virtual void toImplement(double a) = 0;

Can anyone suggest a way in which I can achieve this or something like? Main constraint is that I can't change the base class.

Upvotes: 0

Views: 847

Answers (2)

llllllllll
llllllllll

Reputation: 16434

If I understand the question correctly, you want to use other class to inject implemented methods.

You just need to delegate function call to implementors. The following code is more generic, in the sense that it can combine many implementors together.

Note: it's in C++17 because of fold expression. But you can easily fulfill this functionality in a pre-C++17 way.

#include <tuple>
#include <iostream>
#include <memory>

struct Base {
    virtual void toImplement(double a) = 0;
};

template <class... Impls>
struct Derived : public Base {
    virtual void toImplement(double a) override {
        do_all(a, std::index_sequence_for<Impls...>{});
    }
    std::tuple<Impls...> impls;
private:
    template<std::size_t... Is>
    void do_all(double a, std::index_sequence<Is...>) {
        (std::get<Is>(impls).do_(a), ...);
    }
};

// test
struct Implementor1 {
    void do_(double a) { std::cout << "first impl do: " << a << "\n"; }
};

struct Implementor2 {
    void do_(double a) { std::cout << "second impl do: " << a << "\n"; }
};

int main() {
    std::unique_ptr<Base> ptr = std::make_unique<Derived<Implementor1, Implementor2>>();

    ptr->toImplement(2.3);
}

Upvotes: 1

SergeyA
SergeyA

Reputation: 62603

If you must deal with dreaded diamond inheritance, here is how you do this:

class Base {
    public:
    virtual void toImplement(double a) = 0;
};

class Implementor : public virtual Base {
    public:
    virtual void toImplement(double a) {}
};

template <typename Type>
class Derived : public virtual Base, virtual Type {
};

int main() {
    Derived<Implementor> a; // Fails
}

The way you have it now, toImplement in Implementor has nothing to do with accidentally similarly named function in Base.

Upvotes: 1

Related Questions