管理不同组件实现的控制器构造的设计问题

时间:2018-04-15 09:04:07

标签: c++ templates c++14 variadic-templates factory-pattern

我正在解决一个相对简单的问题,但似乎无法找到解决这个问题的方法 - 甚至有点hackish ......,我只是陷入困境并且可以使用一些似乎不会出现的想法。下面显示的代码示例不是(完全)工作,而是显示我的问题。

我有一个控制器类,包含3个方法initprocessdispose。 Controller基本上是一个或多个要执行的所谓组件的包装器。

此外,还有一个component_manager类来创建组件实例并管理它们的生命周期。组件管理器派生自定义4个虚拟方法的接口,例如initpreparerecycledispose

组件本身也有一个定义良好的接口icomponent。所有组件都派生自该接口,并且所有组件都是默认构造的(不需要参数)。

为了显示一些代码,我从我正在试验的代码中提取了重要的部分,而忽略了`controller'包装类及其周围的所有噪音。

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;
    });
}

总而言之,典型的控制器用法如下所示:

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

这将是我首选的使用方式,将组件作为可变参数模板参数传递。在控制器process方法中,迭代并执行这些组件类型。我真的想用process方法传递组件而不是控制器实例化。

我的基本问题依赖于组件管理器prepare方法。 component_manager及其prepare方法的简单实现如下所示:

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

但是,我将有不同的组件管理器实现,例如,我想通过使用对象池来回收和重用创建的组件指针。在这种情况下,我可能会有像

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

最后我将有各种不同的component_managersprepare的每个实现和其他回收/处理方法会有所不同,但是,对于我需要组件类型T的所有情况。但是......模板方法不能是虚拟的。我知道这个!

有没有办法解决上述问题,或者可能是一种更好的方法或设计解决这个问题?我一直坐着盯着这个太久了,所以如果我错过了一些非常简单的事情,请耐心等待。

谢谢你的时间!

1 个答案:

答案 0 :(得分:0)

我不是继承和虚拟方法的专家......而且我不确定你理解你的确切要求......无论如何,正如你所说,模板方法不能是虚拟的。

所以你必须考虑到这一点。

但模板方法可以在模板类中调用虚方法。

所以我建议在prepare()中创建虚拟iprocessing_component_manager以返回icomponent指针。

struct iprocessing_component_manager
 {
   virtual ~iprocessing_component_manager () = default;

   virtual icomponent * prepare () = 0;
 };

接下来,准备实现prepare()

的单一类型模板派生类
template <typename T>
struct type_processing_component_manager
   : public iprocessing_component_manager
 {
   virtual ~type_processing_component_manager () = default;

   virtual T* prepare () override
    { return new T; }
 };

观察prepare()返回指向T的指针,而不是指向icomponent的指针。

现在,您可以编写一个可变参数模板tuple_processing_component_manager,该模板继承自所有type_processing_component_manager<Ts>,并使用模板(显然不是virtualprepare()调用右侧(类型)依赖)虚拟继承prepare()方法。

template <typename ... Ts>
struct tuple_processing_component_manager
   : public type_processing_component_manager<Ts>...
 {
   virtual ~tuple_processing_component_manager () = default;

   template <typename T>
   T * prepare ()
    { return type_processing_component_manager<T>::prepare(); }
 };

在主process()中,您可以拥有基于可变参数处理组件管理器的component_mgr

auto component_mgr
 { std::make_unique<tuple_processing_component_manager<Ts...>>() };

并在lambda中使用它,如下所示

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

  std::unique_ptr<component_t> x
   { component_mgr->template prepare<component_t>() };

  std::cout << x->id() << "\n";

以下是一个完整的工作示例。

#include <tuple>
#include <memory>
#include <utility>
#include <iostream>

namespace detail
 {
   template <typename Tuple, typename F, std::size_t ... Is>
   constexpr void for_each_impl (Tuple && tuple, F && f,
                                 std::index_sequence<Is...>) 
    {
      using swallow = int[];

      (void)swallow { 1,
         ((void)f(std::get<Is>(std::forward<Tuple>(tuple))), 0)... };
    }
 }

template <typename Tuple, typename F>
constexpr void for_each (Tuple && tuple, F && f) 
 {
   constexpr auto 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>{});
 } 

struct icomponent
 {
   virtual ~icomponent () = default;

   virtual std::string id () = 0; 
 };

struct component_a : public icomponent
 { virtual std::string id () override { return "component a"; } };

struct component_b : public icomponent
 { virtual std::string id () override { return "component b"; } };

struct component_c : public icomponent
 { virtual std::string id () override { return "component c"; } };

struct iprocessing_component_manager
 {
   virtual ~iprocessing_component_manager () = default;

   virtual icomponent * prepare () = 0;
 };

template <typename T>
struct type_processing_component_manager
   : public iprocessing_component_manager
 {
   virtual ~type_processing_component_manager () = default;

   virtual T* prepare () override
    { return new T; }
 };

template <typename ... Ts>
struct tuple_processing_component_manager
   : public type_processing_component_manager<Ts>...
 {
   virtual ~tuple_processing_component_manager () = default;

   template <typename T>
   T * prepare ()
    { return type_processing_component_manager<T>::prepare(); }
 };

template <typename ... Ts>
void process ()
 {
   auto component_mgr
    { std::make_unique<tuple_processing_component_manager<Ts...>>() };

   std::tuple<Ts...> components;

   for_each(components, [&](auto& comp)
    {  
      using component_t
         = typename std::remove_reference<decltype(comp)>::type;

      std::unique_ptr<component_t> x
       { component_mgr->template prepare<component_t>() };

      std::cout << x->id() << "\n";
    });
}

int main ()
 {
   process<component_a, component_b, component_c>();
 }

如果您对std::tuple的{​​{1}}以及components处理不感兴趣,可以在递归{{1}中直接使用for_each()结构}

type_processing_component_manager