指向基于虚拟接口的可变参数模板类的指针

时间:2018-04-16 13:28:07

标签: c++ variadic-templates instantiation virtual-inheritance

我有一个可变参数模板类,它实现了在基类iprocessing_component_manager中定义的4个方法。这4种方法管理组件的生命周期,例如初始化,准备和回收和/或处置。完全实现的接口tuple_processing_component_manager将组件类型作为可变参数。

组件管理器是执行组件序列的控制器的成员。

以下代码是我的控制器的基础骨架,到目前为止工作正常。它有一个可能的组件管理器实现,这里我只是展示一个简单的实现来说明:

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

执行控制器会看起来像

int main(int argc, char** argv)
{
    controller<component_a, component_c> c;
    c.init();
    c.process();

    return 0;
}

我的问题在于controller类和m_component_manager成员。

如果我构建一个控制器并初始化m_component_manager,那么下面的工作没有任何问题,显然:

std::unique_ptr<tuple_processing_component_manager<Ts...>> m_component_manager = std::make_unique<tuple_processing_component_manager<Ts...>>();

我如何更改m_component_manager的定义以采用component_manager的任何可变执行和/或需要进行哪些调整才能实现此目的? 我试图使用std::unique_ptr<iprocessing_component_manager>,但这肯定没有用。尽管如此我尝试更改了一些内容但却出现multiple inheritance ambiguity错误或no unique final overrider for iprocessing_component_manager::prepare()

在稍后的步骤中,我希望能够将组件管理器的实例作为参数传递给控制器​​构造函数,只是为了更灵活。

所以我不确定这是否有可能实现我想要实现的目标。有什么方法可以解决这个问题吗?我对可变参数模板和元模板编程不是很熟悉。所以我想知道是否有人能告诉我如何做到这一点。

已经尝试搜索类似的问题,但找不到任何密切相关的内容。

非常感谢任何帮助。感谢您提前的时间!

3 个答案:

答案 0 :(得分:1)

I'm going to have a go at this and learn along with you. I've been working through rabbit hole of spirit x3, under the hood.

So, from what I understand, you don't have to have inheritance. You definitely don't need shared pointers and objects on the heap like you would with a mixed container of derived objects. Real objects of different types can be stored in a tuple.

I'm about done for the day but here is what I've got so far. All your stuff but with a tuple.

In CTRL + U I iterate the tuple to call the method controller::init. What I need to do next is figure out how to remove the constant qualifier so you have the freedom of modifying your objects. Is this what you are looking for?

say

答案 1 :(得分:1)

以下是我使用std::type_info建议的要点:

class imultitype_processing_component_manager
{
public:
    virtual ~imultitype_processing_component_manager() = default;

    virtual void v_add_manager(
        const std::type_info& type,
        std::unique_ptr<iprocessing_component_manager> manager) = 0;
    virtual void v_forget_manager(
        const std::type_info& type) = 0;
    virtual icomponent* v_prepare(
        const std::type_info& type) = 0;
    virtual void recycle(icomponent* comp) = 0;
};

class multitype_processing_component_manager
{
public:
    void v_add_manager(
        const std::type_info& type,
        std::unique_ptr<iprocessing_component_manager> manager)
        override
    {
        // This would replace an existing manager.
        // Could also choose to throw or do nothing if already exists...
        m_managers[type] = std::move(manager);
    }

    template <class T>
    void add_manager(std::unique_ptr<iprocessing_component_manager> manager
                         = nullptr)
    {
        if (!manager)
            manager = std::make_unique<type_processing_component_manager<T>>();
        v_add_manager(typeid(T), std::move(manager));
    }

    void v_forget_manager(const std::type_info& type) override
    {
        m_managers.erase(type);
    }

    template <class T>
    void forget_manager()
    {
        v_forget_manager(typeid(T));
    }

    icomponent* v_prepare(const std::type_info& type) override
    {
        // Throws if type unknown.
        return m_managers.at(type)->prepare();
    }

    template <class T>
    T* prepare()
    {
        return dynamic_cast<T*>(v_prepare(typeid(T)));
    }

    void recycle(icomponent* comp) const override
    {
        // Throws if type unknown.
        m_managers.at(typeid(*comp))->recycle(comp);
    }

    template <typename F>
    for_each_manager(F&& func) const
    {
        for (auto& pair : m_managers)
            func(*pair.second);
    }

private:
    std::unordered_map<std::type_index,
        std::unique_ptr<iprocessing_component_manager>> m_managers;
};

controller可以包含上面的接口或派生类,并向它公开任何适当的接口。如果需要其他多态组件管理器行为,则其他子类可以直接从imultitype_processing_component_manager继承,也可以从multitype_processing_component_manager继承,以便使用并添加其对包含混合单类型管理器的支持。如果需要,imultitype_processing_component_manager可以添加virtual v_for_each_manager(const std::function<void(iprocessing_component_manager&)>& func) = 0;,但std::function与仅仅是仿函数模板参数相比会增加一些开销。

答案 2 :(得分:0)

从代码中休息一下(有时这是一个很大的帮助)我也想提出我已经解决的解决方案。

我基本上改变了controller的界面。我不是将单个组件类型作为可变参数传递,而是简单地使用其可变参数传递processing_component_manager。

控制器现在看起来像这样:

template <typename T> 
class controller {
private:
    std::unique_ptr<T> m_component_manager;

public:
    controller()
    : m_component_manager(std::make_unique<T>())
    {
    }
    ~controller() = default;

    void init()
    {
        m_component_manager->init();
    }

    void process()
    {
        std::cout << m_component_manager->m_component_ts_size << "\n";

        for_each_in_tuple(m_component_manager->m_component_ts, [&](auto tag) {
            using component_t = decltype(tag);

            std::unique_ptr<component_t> p{
                m_component_manager->template prepare<component_t>()
            };

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

            // ...

            m_component_manager->recycle(p.get());            
        });
    }
};

tuple_processing_component_manager我刚刚添加了另一个成员,它将可变参数 - 我的组件 - 转换为元组:

const std::tuple<Ts...> m_component_ts;

现在我可以在process的{​​{1}}方法中迭代那些类型的元组。剩下的所有代码都在我的问题中发布。

controller的用法如下所示:

controller

有了这个我可以 - 除了简单的controller<simple_processing_component_manager<component_a, component_c>> c; c.init(); c.process(); 一次性运行 - 创建额外的管理器,如所述,使用对象池进行指针重用/回收,如果在长期运行过程中使用并添加一些更专业的经理人,无论需要什么。

我应该提一下,我只使用 n 组件传递一种类型的组件管理器。单个组件管理可视为如何在运行时处理组件的策略。

目前,这种方式在我看来是合情合理的。我还没有在我的项目中实现该代码。所以我会看看它是否符合预期。