I have a variadic template class implementing 4 methods defined in a base class iprocessing_component_manager
. The 4 methods manage the life cycle of a component, such as initialization, preparation and recycling and/or disposal. The fully implemented interface tuple_processing_component_manager
takes component types as a variadic argument.
The component manager is a member of a controller to execute the sequence of components.
The following code is a base skeleton for my controller and working fine so far. It has one possible component manager implementation, here I just show a simple implementation to illustrate:
#include "utilities.h"
#include <cstdlib>
#include <iostream>
#include <tuple>
// https://stackoverflow.com/questions/42255534 (Yakk)
namespace notstd {
template<class T> struct tag_t { constexpr tag_t() {}; using type=T; };
template<class T> constexpr tag_t<T> tag{};
template<class Tag> using type_t = typename Tag::type;
template<class...Ts, class F>
void for_each_type(F&& f) {
using discard=int[];
(void)discard{ 0,(void(
f( tag<Ts> )
),0)...};
}
}
// A component
class icomponent {
public:
virtual std::string id() = 0;
virtual ~icomponent() = default;
virtual void init() = 0;
virtual void dispose() = 0;
};
class component_base : public icomponent
{
public:
virtual ~component_base() = default;
virtual void init()
{
// ... init context
}
virtual void dispose()
{
// ...
}
// ... more
};
// Sample components
class component_a : public component_base {
public:
virtual std::string id() override { return "component a"; }
};
class component_b : public component_base {
public:
virtual std::string id() override { return "component b"; }
};
class component_c : public component_base {
public:
virtual std::string id() override { return "component c"; }
};
// Interface component manager
class iprocessing_component_manager {
public:
virtual ~iprocessing_component_manager() = default;
virtual void init() = 0;
virtual icomponent* prepare() = 0;
virtual void recycle(icomponent* p) = 0;
virtual void dispose() = 0;
};
// Implementation component manager
template<typename T>
class type_processing_component_manager
: public iprocessing_component_manager {
public:
virtual ~type_processing_component_manager() = default;
virtual T* prepare() override
{
// Default create T or fetch from a object pool, etc ...
return new T;
}
};
// Implementation virt. methods component mgr
template<typename ... Ts>
class tuple_processing_component_manager
: public type_processing_component_manager<Ts>... {
public:
virtual ~tuple_processing_component_manager() = default;
virtual void init() override
{
}
template<typename T>
T* prepare()
{
return type_processing_component_manager<T>::prepare();
}
virtual void recycle(icomponent* p) override
{
// Delete pointer or return to an object pool, etc
delete p;
}
virtual void dispose() override
{
}
};
// The controller
template <typename ...Ts>
class controller {
std::unique_ptr<tuple_processing_component_manager<Ts...>> m_component_manager;
// iprocessing_component_manager* m_component_manager;
public:
controller()
: m_component_manager(std::make_unique<tuple_processing_component_manager<Ts...>>())
// : m_component_manager(new tuple_processing_component_manager<Ts...>())
{
}
~controller() = default;
// Do some initialization
void init()
{
m_component_manager->init();
}
// Process components
void process()
{
// A simple loop over components.
notstd::for_each_type<Ts...>([&](auto tag) {
using component_t = notstd::type_t<decltype(tag)>;
component_t* x = m_component_manager->template prepare<component_t>();
// Do some processing, here I just print the component id
std::cout << x->id() << "\n";
// Recycle.
m_component_manager->recycle(x);
});
}
// ... more stuff
};
Executing the controller would look something along the lines
int main(int argc, char** argv)
{
controller<component_a, component_c> c;
c.init();
c.process();
return 0;
}
My problem lies in the controller
class and the m_component_manager
member.
If I construct a controller and initialize m_component_manager
the following is working without any problem, obviously:
std::unique_ptr<tuple_processing_component_manager<Ts...>> m_component_manager = std::make_unique<tuple_processing_component_manager<Ts...>>();
How would I have to change the definition of m_component_manager
to take any variadic implementation of a component_manager and/or what adjustments need to be done to accomplish this?
I tried to use std::unique_ptr<iprocessing_component_manager>
but that is certainly not working out. Despite that I tried to change a few things but get multiple inheritance ambiguity
errors or no unique final overrider for iprocessing_component_manager::prepare()
.
At a later step I would like to be able to pass an instance of a component manager into the controller constructor as an argument, just to be a bit more flexible.
So I am not sure if this is possible at all what I am trying to achieve. Is there any approach to resolve this? I am not very experienced with variadic templates and meta-template programming. So I wonder if anyone can show me a way how to do that.
Already tried to search for similar problems but could not find anything closely related.
Any help is very appreciated. Thanks for your time in advance!