class inheriting from multiple template class instances returning their types

Viewed 109

I want to inherit multiple times from a base template class and have a method that returns the corresponding value for every type used in the inheritance, look at the test2 instance (following code definitely won't compile, because the getNextData() methods have the same signature and the problem is that I don't know how to fix it), I marked the important lines in the code:

#include <iostream>

namespace ns
{

template<typename Output>
class Base
{
protected:
    struct ResultData { bool success; Output data; };
public:
    virtual ResultData getNextData() = 0;
};

template<typename Output>
class Derived : public Base<Output> {/*implements some pure virtual method*/};

struct Data
{
    int32_t member;
};

struct Final final : public Derived<Data>
{
    ResultData getNextData() final
    {
        return { true, {1} };
    }
};

template<typename T1, typename T2 = int32_t>
    class DerivedMultiple : public virtual Derived<T1> // <= important
    , public Base<T2> // <= important
{
public:
    ResultData<T1> getNextData() override { return { false, {} }; }
    virtual ResultData<T2> getNextData() { return { false, {} }; }
};

struct FinalMultiple final : public DerivedMultiple<Data, std::string>
{
    ResultData getNextData() final { return {true, {1} }; } // <= important
    ResultData getNextData() final { return {true, "test"}; } // <= important
};

struct ReferenceT1 {
    ReferenceT1(Base<Data>& base) {}; // <= important
};

struct ReferenceT2 {
    ReferenceT2(Base<std::string>& base) {}; // <= important
};

}

int main()
{
    ns::Final test1;
    std::cout << test1.getNextData().success << std::endl;
    std::cout << test1.getNextData().data.member << std::endl;

    ns::FinalMultiple test2;
    std::cout << test2.getNextData().success << std::endl;
    std::cout << test2.getNextData()/*for Data*/.data.member << std::endl; // <= important
    std::cout << test2.getNextData().success << std::endl;
    std::cout << test2.getNextData()/*for std::string*/.data << std::endl; // <= important
    
    ns::ReferenceT1 referenceT1(test2); // <= important
    ns::ReferenceT2 referenceT2(test2); // <= important
}

There was an obvious solution to pass the values as reference and fill them in the getNextData(<type>&) method, but that's how it started, I refactored that at the beginning, because I want to return the values, not fill them (many reasons):

    bool getNextData(T1& output) override { return false; }
    bool getNextData(T2& output) override { return false; }

I tried virtual inheritance, but it didn't help in this context.

I came up with 2 solutions, both bypass the need for different signature in a very dirty way and neither of them works when you uncomment the 2 commented lines in them (can't derive from the base class twice and have those methods returning only the expected type).

First solution:

#include <iostream>

namespace ns
{

template<typename Output>
class Base
{
protected:
    struct ResultData { bool success; Output data; };
public:
    virtual ResultData getNextData() = 0;
};

template<typename Output>
class Derived : public Base<Output> {};

struct Data
{
    int32_t member;
};

struct Final final : public Derived<Data>
{
    ResultData getNextData() final
    {
        return { true, {1} };
    }
};

template<typename T1, typename T2 = int32_t>
    class DerivedMultiple : public virtual Derived<T1>
    //, public Base<T2>
{
protected:
    struct ResultDataT2 { bool success; T2 data; };
public:
    virtual ResultDataT2 getNextDataT2() { return { false, {} }; }
};

struct FinalMultiple final : public DerivedMultiple<Data, std::string>
{
    ResultData getNextData() final
    {
        return {true, {1} };
    }

    ResultDataT2 getNextDataT2() final
    {
        return {true, "test"};
    }
};

struct ReferenceT1 {
    ReferenceT1(Base<Data>& base) {};
};

struct ReferenceT2 {
    ReferenceT2(Base<std::string>& base) {};
};

}

int main()
{
    ns::Final test1;
    std::cout << test1.getNextData().success << std::endl;
    std::cout << test1.getNextData().data.member << std::endl;

    ns::FinalMultiple test2;
    std::cout << test2.getNextData().success << std::endl;
    std::cout << test2.getNextData().data.member << std::endl;
    std::cout << test2.getNextDataT2().success << std::endl;
    std::cout << test2.getNextDataT2().data << std::endl;
    
    ns::ReferenceT1 referenceT1(test2);
    //ns::ReferenceT2 referenceT2(test2);
}

Second solution:

#include <iostream>

namespace ns
{

template<typename Output>
struct ResultData { bool success; Output data; };

template<typename Output>
class Base
{
public:
    virtual ResultData<Output> getNextData() = 0;
};

template<typename Output>
class Derived : public Base<Output> {};

struct Data
{
    int32_t member;
};

struct Final final : public Derived<Data>
{
    ResultData<Data> getNextData() final
    {
        return { true, {1} };
    }
};

template<typename T1, typename T2 = int32_t>
    class DerivedMultiple : public virtual Derived<T1>
    //, public Base<T2>
{
protected:
    struct ResultDataT2 { bool success; T2 data; };
public:
    virtual ResultDataT2 getNextDataT2() { return { false, {} }; }
};

struct FinalMultiple final : public DerivedMultiple<Data, std::string>
{
    ResultData<Data> getNextData() final
    {
        return {true, {1} };
    }

    ResultDataT2 getNextDataT2() final
    {
        return {true, "test"};
    }
};

struct ReferenceT1 {
    ReferenceT1(Base<Data>& base) {};
};

struct ReferenceT2 {
    ReferenceT2(Base<std::string>& base) {};
};

}

int main()
{
    ns::Final test1;
    std::cout << test1.getNextData().success << std::endl;
    std::cout << test1.getNextData().data.member << std::endl;

    ns::FinalMultiple test2;
    std::cout << test2.getNextData().success << std::endl;
    std::cout << test2.getNextData().data.member << std::endl;
    std::cout << test2.getNextDataT2().success << std::endl;
    std::cout << test2.getNextDataT2().data << std::endl;
    
    ns::ReferenceT1 referenceT1(test2);
    //ns::ReferenceT2 referenceT2(test2);
}

Is there a way in C++ to overcome this problem?

Another thing would be to switch to variadic templates in the solution (so the base class can be inherited more than twice and the multiply derived class can return more than 2 types), but that's optional.

1 Answers

I'm not sure I quite understand what you are trying to achieve, but see if this gives you some ideas.

template <typename Impl, typename Intf, typename Tag>
class ForwardingShim;

template <typename Impl, typename Intf>
class ForwardingShim<Impl, Intf, struct ForwardOne> : public Intf {
public:
  typename Intf::ResultData getNextData() final {
    return static_cast<Impl*>(this)->getNextDataOne();
  }
};

template <typename Impl, typename Intf>
class ForwardingShim<Impl, Intf, struct ForwardTwo> : public Intf {
public:
  typename Intf::ResultData getNextData() final {
    return static_cast<Impl*>(this)->getNextDataTwo();
  }
};

template <typename T1, typename T2>
class MultiDerived :
    public ForwardingShim<MultiDerived, Base<T1>, struct ForwardOne>,
    public ForwardingShim<MultiDerived, Base<T2>, struct ForwardTwo> {
public:
  typename Base<T1>::ResultData getNextDataOne();
  typename Base<T2>::ResultData getNextDataTwo();
};

Demo

Related