c++ - C++数据流编程: Interconnecting Nodes With Differing Template Parameters

标签 c++ templates

我不太清楚我理想的解决方案是什么样子,因此请原谅任何含糊或不恰当的术语。

我正在从事一个项目,该项目涉及将节点串行连接到管道中以处理来自硬件 Controller 的数据。在用户输入时,该值将以数据流编程样式在节点之间的链中推送。每个节点可以具有不同的输入和输出类型,因此从int开始的内容可能最终在流水线末尾生成用户定义的类型,或者根本没有类型(只是没有参数的函数调用)。在通过管道传递数据方面,我选择将事物实现为一系列仿函数,因此在将数据从节点传递到节点时,存在一定程度的类型擦除。每个仿函数仅处理其参数,然后将它们交给下一个仿函数。

问题在于更改节点的布局或增加或减少它们。我很困惑如何保留这些不同类型的映射,并且仍然能够将它们链接在一起,而不必执行一堆难看且昂贵的dynamic_cast if语句。它们都是从通用的类模板派生的,但是它们的实际类型取决于输入/输出参数。我已将接口(interface)分为输入和输出两半,因此接收节点可以存储指向其源节点类型的指针,而不必知道该节点的输入类型以及之前的所有节点。

class IMapping 
{
    virtual void Enable() = 0;
    virtual void Disable() = 0;
    ...
}


template <typename InputT>
class IMapping_Inlet
{
public:

    virtual void SetSource(IMapping_Outlet<InputT> *pSource) { /* Stores pointer to new source */ };
    virtual IFunctor<InputT> GetFunc() = 0; // IFunctor<T> is just an alias for std::function<void(T)> for now.
    ...

protected:

    void SetFunc(IFunctor<InputT> *pFunc){ m_Func = pFunc; }; // Called from the derived class to set the function that processes InputT

    IMapping_Outlet<InputT> *m_Source;
    IFunctor<InputT> *m_Func;
}


template <typename OutputT>
class IMapping_Outlet
{
public:

    virtual void SetTarget(IMapping_Inlet<OutputT> *pTarget) { /* Stores pointer and calls pTarget->GetFunc() */ };

    virtual void SetFunc(IFunctor<OutputT> *pFunc) { /* Some mappings may want to swap out their functor */ };
    ...

protected:

    IMapping_Inlet<OutputT> *m_Target;
    IFunctor<OutputT> *m_TargetFunc;
}


template <typename InputT, OutputT>
class IMapping : public IMapping, public IMapping_Inlet<InputT>, public IMapping_Outlet<OutputT>
{
public:

    virtual void Init() { /* Derived type creates a functor (probably pointing to one of its own member functions) and passes it to IMapping_Inlet::SetFunc() */ };
    ...

}


class MyDerivedMapping : public IMapping<int, bool>
{
    void Init() override
    {   // Haven't tested this yet 
        IMapping_Inlet<int>::SetFunc(
            [auto tail = m_Target->GetFunc()] (int i) {
                bool b = i > 10 ? true : false;
                tail(b);
            }
        );
    };
}

因此,假设您知道两个节点的类型,并且它们的输入/输出类型匹配,则可以轻松地将它们映射在一起。但是,封闭的管道类无法将所有节点存储在一个通用集合中,并且仍然能够根据具体类型将它们映射在一起。如果我们有虚拟模板函数,那将是很好的,因为我可以在IMapping接口(interface)中定义一个模板化的SetSource/SetTarget函数,然后根据template参数进行调度。

我当时想我可以根据其输入和输出类型对管道进行模板化,并让映射节点在其间做任何他们想做的事情。管道仅应在其输入类型与第一个节点的输入类型匹配且其输出类型与最后一个节点的输出类型匹配时在意。

我一直在阅读有关TMP,CRTP,SFINAE,协变量返回类型等的信息,但是其中大部分是新 Material ,困扰着我。感觉好像属于函数式编程领域,但是我太缺乏经验了,无法理解我如何实际使用一种弥合运行时与编译时之间差距的函数式方法。

作为我想做的事的一个例子:
[T, U]表示IMapping<T, U>,这是一个以T作为输入并传递U作为输出的映射。
我有节点[int, bool]->[bool, int],想将它们插入第一个节点之后的节点[int, int]->[int, double]->[double, double]的更长字符串中。

我希望能够从基类指针中查询节点的输入或输出类型,并在类型兼容时向下转换和映射。

我在Alexandrescu的Modern C++ Design中已经读到的一些技巧似乎可以使他们适应这种情况,但是关键是在运行时进行一些决策。将指针传递到推断其类型的模板函数中,然后执行一些元编程魔术会很棒,但是我认为它将基于基类指针而不是派生类型来选择模板特化。有什么方法可以在基类中实现标识函数,该函数将基于派生的模板类返回协变返回类型?

CRTP似乎是解决虚拟模板函数问题的一种好方法,但是在存储Base指针时,我仍然需要知道派生类型。我可以从非模板基类继承,但随后我们回到第一个平方。似乎此障碍将抑制基于策略的设计方法。如果对象在具有不同策略的类型之间不保持公共(public)接口(interface),那么如何从多个策略组成对象。每个具体类型将具有不同的模板参数。如果您需要做的就是通过一个简单的函数调用来调用一些副作用,那么从基类继承就可以很好地工作,但是比这更高级的事情似乎是不切实际的。

我对如何在不牺牲运行时间效率,将来的可扩展性和某种程度的类型安全性的前提下解决这类体系结构感到困惑。在过去的经典 Vanilla c++范例中似乎没有一个简单的解决方案,而我只是对模板技巧和功能性方法不够了解,无法知道从哪里开始寻找或寻找什么。

我希望有人可以指出正确的方向或提出解决方法。

最佳答案

我已经开发了一种现代的基于C++流的编程库DSPatch,它具有与您所描述的非常相似的面向对象的API。我认为您会发现最有趣的类是RunType类-用于将数据往返于组件。

RunType类利用内部模板类和公共(public)模板方法来允许用户获取并将包含的变量设置为所需的任何类型。由于所包含的变量是在内部进行模板化的,因此它可以在运行时更改类型。

现在,为避免在get/set期间产生尽可能多的开销,我在类型更改时仅使用typeid()一次,然后缓存该类型以用于后续的get/set类型匹配-请参见RunType::CopyFrom()。对于值移动而不是复制的情况,我们根本不需要匹配类型-请参阅RunType::MoveTo()。

只要有可能,DSPatch中组件之间的数据都是通过移动而不是复制来传输的。

希望这可以帮助!

关于c++ - C++数据流编程: Interconnecting Nodes With Differing Template Parameters,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/34981193/

相关文章:

c++ - 已经使用 tr1::hash 在此范围内声明了“hash”;

c++ - 同一程序多次执行的未定义行为

c++ - 如何将值输出到 C++11 中的流元组

c++ - 有条件地将函数成员添加到模板类中

c++ - 从文件中读取多个字节并存储它们以便在 C++ 中进行比较

c++ - "Vector Subscript Out of Range"使用 std::vector 作为 std::map 中的值

c++ - 我怎样才能确保我的程序在内核模式下成功运行?

c++ - 具有与实例一样多的参数的可变参数模板

c++ - 如何正确地将比较器传递给另一个模板函数

python - 使用 django 表单模板