Home Design issues with a controller construct managing different component implementations
Reply: 0

Design issues with a controller construct managing different component implementations

user6037
1#
user6037 Published in May 21, 2018, 3:05 am

I am wrapping my head around on a relatively simple problem, but seemingly cannot find a way to resolve this issue - even a bit hackish ..., I just got stuck and could use some idea that seem not to pop up. The code sample shown below is NOT (fully) working and is rather there to show my problem.

I have a controller class with 3 methods init, process and dispose. The Controller basically is a wrapper for one or more so called components to be executed.

Furthermore there is a component_manager class to create component instances and manage their life cycle. The component manager is derived from an interface defining 4 virtual methods, such as init, prepare, recycle and dispose.

A component itself also has a well defined interface icomponent. All components derive from that interface and all components are default constructable (no arguments needed).

To show some code I've extracted the important parts from the code I am experimenting with, leaving out the `controller' wrapper class and all the noise around it.

namespace detail {

// Iterator for std::tuple    
template<typename Tuple, typename F, std::size_t ...Indices>
constexpr void for_each_impl(Tuple&& tuple, F&& f, std::index_sequence<Indices...>) 
{
    using swallow = int[];
    (void)swallow{1,
        (f(std::get<Indices>(std::forward<Tuple>(tuple))), void(), int{})...
    };
}

} // Namespace detail

template<typename Tuple, typename F>
constexpr void for_each(Tuple&& tuple, F&& f) 
{
    constexpr std::size_t N = std::tuple_size<std::remove_reference_t<Tuple>>::value;
    detail::for_each_impl(std::forward<Tuple>(tuple), std::forward<F>(f),
        std::make_index_sequence<N>{});
} 

// A component
class icomponent {
public:
    virtual ~icomponent() = default;
    virtual std::string id() = 0; 
};

// Sample components a, b and c
class component_a : public icomponent { 
public:    
    virtual std::string id() override { return "component a"; } 
};
class component_b : public icomponent { 
public:    
    virtual std::string id() override { return "component b"; } 
};
class component_c : public icomponent { 
public:    
    virtual std::string id() override { return "component c"; } 
};

// Interface defining a component manager
class iprocessing_component_manager
{
public:
    virtual ~iprocessing_component_manager() = default;

    virtual T* prepare() = 0;

    // More members like init(), recycle() and dispose()
};

// An simple, possible implementation of component manager
class simple_processing_component_manager 
    : public iprocessing_component_manager
{
public:
    virtual ~simple_processing_component_manager() = default;

    // ... other implemented methods ...

    virtual T* prepare()
    {
        // ... creation of component
    }
};

// In the controller wrapper, the process method
template<typename ...T>
void process()
{
    // This initialization is normally within the controller wrapper
    std::unique_ptr<simple_processing_component_manager> component_mgr = std::make_unique<simple_processing_component_manager>();

    // Convert to tuple
    std::tuple<T...> components;

    // Iterate over (component) types ...
    for_each(components, [&](auto& comp) {  

       using component_t = typename std::remove_reference<decltype(comp)>::type;

       // Steps would be ...

       // Instantiate the component (doesn't work, just showing what I need to accomplish)
       component_t* x = component_mgr->prepare<component_t>();
       //                                     ^^^^^^^^^^^^^

       // Perform component execution ... here I just print the id of the component
       std::cout << x->id() << "\n";

       // After processing component_mgr recycle() and / or dispose() are called
       delete x;
    });
}

Putting it all together, a typical controller usage would look like this:

controller c(/* some initialization parameters */);
result = c->process<component_a, component_c>(/* some more parameters */);

That would be my preferred way of usage, passing the components as variadic template arguments. Within the controller process method, these component types are iterated and executed. I would really like to pass the components with the process method and not with the controller instantiation.

My basic problem relies in component managers prepare method. A simple implementation of a component_manager and its prepare method would look like this:

T* prepare(/*...*/)
{
    return new T(); // Create component instance
}

However, I will have different implementations of component managers, for example, I would like to recycle and reuse the created component pointers by using an object pool. In such case I may have something like

return borrow_object<T>(/* some key */); // Get from pool, will be returned back to pool with `recycle`...

A the end I will have various distinct component_managers and each implementation of prepare and the other recycle/disposal methods will differ, however, for all cases I need the component type T. But ... a template method cannot be virtual. I am aware of this!

Is there any way to fix the above problem or perhaps a way better approach or design for this problem? I been sitting and staring at this too long, so please bear with me if I missed something really simple.

Thanks for your time!

You need to login account before you can post.

About| Privacy statement| Terms of Service| Advertising| Contact us| Help| Sitemap|
Processed in 0.311763 second(s) , Gzip On .

© 2016 Powered by mzan.com design MATCHINFO