I have a of templated class that can generate an object instance from an ID. The context is networking code with object replication.
The code below shows a way that I can manage to do this, but it has the drawback of beeing very slow in compilation.
Does anyone know a "better" way to achieve what my example shows. I'm not sure how to make this question more clear, I hope the code speaks for itself.
I have looked at extern templates, but I do not see how to apply that to templated functions in templated classes. If anyone knows how to do that, that would solve the issue.
Alternatively a way to fix the ambiguous problem of MyRegistersSimple
would also be greatly helpfull!
template<typename ID, typename Base>
class Register
{
public:
void create(ID id) { m_types.at(id).second(); }
private:
std::map<ID, std::function<std::unique_ptr<Base>(ID)>> m_types;
};
template<typename tag>
struct ID { int value; };
class ABase {};
class BBase {};
class CBase {};
using ID_A = ID<struct ID_A_TAG>;
using ID_B = ID<struct ID_B_TAG>;
using ID_C = ID<struct ID_C_TAG>;
class MyRegistersSimple :
public Register<ID_A, ABase>,
public Register<ID_B, BBase>,
public Register<ID_C, CBase>
{
};
template<typename... Registers>
class MultiRegister : public Registers...
{
public:
template<typename ID>
void create(ID)
{
// lots of complex template code to find the correct Register from 'Registers...'
// and call 'create' on it
// this makes compilation very slow
}
};
class MyRegistersComplex : public MultiRegister<
Register<ID_A, ABase>,
Register<ID_B, BBase>,
Register<ID_C, CBase>>
{};
void test()
{
MyRegistersSimple simple;
simple.create(ID_A(0)); // -> ambiguous, doest not compile
MyRegistersComplex complex;
complex.create(ID_A(0)); // -> very slow compilation
}
Bring all the bases into scope via using
:
// a helper to avoid copy pasting `using`s
template<typename... Registers> struct MultiRegister : Registers... { using Registers::create...; };
class MyRegisters : public MultiRegister<
Register<ID_A, ABase>,
Register<ID_B, BBase>,
Register<ID_C, CBase>>
{};
void test() {
MyRegisters registers;
registers.create(ID_A(0)); // IDE shows that `Register<ID<ID_A_TAG>, ABase>` is chosen
}
I hope the built-in overload resolution is faster than "lots of complex template code" in the ...Complex version.
I didn't like that manual Register<ID_A, ABase>
ID_x <-> xBase dispatch and dummy ID_x_TAGs, so I removed all of that (if using xBase as the ID
's template parameter is fine). Then Register<ID_A, ABase>
, Register<ID_B, BBase>
etc. become
template<typename Base> using MakeRegister = Register<ID<Base>, Base>;
and the code suggested above is just (test()
omitted - it's the same)
template<typename... Registers> struct MultiRegister : Registers... { using Registers::create...; };
template<typename... Bases> using MakeMultiRegister = MultiRegister<MakeRegister<Bases>...>;
class MyRegisters : public MakeMultiRegister<ABase, BBase, CBase> {};