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.