2

我正在研究类似管道的设计模式。我的设计目标之一是通过提供指向某个数据类的函数成员的指针来启用管道段的动态链接。

每个数据类都有一组使用整数模板参数索引的函数成员(代表数据类输出端口)。这些函数使用关键字动态推断返回类型auto,但都接受相同的整数参数c_Idx,即template <int N> auto getOutput(int c_Idx) const。与每个功能相关的功能getOutput是(由用户)在一组部分专用的结构中定义的getOutputImpl。因此,每个数据类可以具有从 1 个到某个固定数量K的输出数据端口。

为了允许以通用方式在管道段之间进行动态链接,可以将它们存储在 类型的容器中std::vector<boost::any>。但是,我需要能够用指向函数成员模板的指针自动填充这个向量。

手动实现的示例如下所示

template<class TLeafType>
class AlgorithmOutput
{

protected:

    std::vector<boost::any> OutputPorts;

public:

    AlgorithmOutput()
        {

            //////////////////////////////////////////
            // This procedure needs to be automated //
            //////////////////////////////////////////
            std::function<std::unique_ptr<double>(int)> pOutFun1 = std::bind(
                std::mem_fn(
                    true ? &AlgorithmOutput<TLeafType>::getOutput<0> : nullptr
                    ),
                this,
                std::placeholders::_1
                );
            OutputPorts.push_back(pOutFun1);

            std::function<std::unique_ptr<int>(int)> pOutFun2 = std::bind(
                std::mem_fn(
                    true ? &AlgorithmOutput<TLeafType>::getOutput<1> : nullptr
                    ),
                this,
                std::placeholders::_1
                );
            OutputPorts.push_back(pOutFun2);

        }

    virtual ~AlgorithmOutput() {}

protected:

    TLeafType* asLeaf(void)
        {
            return static_cast<TLeafType*>(this);
        }

    TLeafType const* asLeaf(void) const
        {
            return static_cast<TLeafType const*>(this);
        }

public:

    template <int N>
    auto getOutput(int c_Idx) const
        {
            return asLeaf() -> getOutput<N>(c_Idx);
        }

    boost::any getOutputPort(int PortIdx)
        {
            return OutputPorts[PortIdx];
        }

};

class PipeOutputClass: public AlgorithmOutput<PipeOutputClass>
{

public:

    template <int N>
    auto getOutput(int c_Idx) const
        {
            return getOutputImpl<N>::apply(this, c_Idx);
        }

    template<int N, typename S> friend struct getOutputImpl;

    template<int N, typename = void>
    struct getOutputImpl
    {
        static auto apply(
            PipeOutputClass const* p_Self,
            int c_Idx
            )
            { throw std::runtime_error("Wrong template argument."); }
    };

    template <typename S>
    struct getOutputImpl<0, S>
    {
        static std::unique_ptr<double> apply(
            PipeOutputClass const* p_Self,
            int c_Idx
            )
            {
                std::unique_ptr<double> mydouble(new double(10));
                return mydouble;
            }
    };

    template <typename S>
    struct getOutputImpl<1, S>
    {
        static std::unique_ptr<int > apply(
            PipeOutputClass const* p_Self,
            int c_Idx
            )
            {
                std::unique_ptr<int > myint(new int(3));
                return myint;
            }
    };

};

上面例子的问题是我pOutFunX手动定义了成员函数指针,而我想自动化这个过程。

请注意,我不考虑与上述设计有显着差异的设计解决方案。


在这里,我提出了一些关于解决这个问题的可能方法的一些想法。我为我目前正在考虑的解决方案制定了一个计划,如果您尝试回答这个问题,这可能会有用:

  1. 获取名为 的用户定义的部分专用结构的数量getOutputImpl
  2. 对于每个这样的结构,确定其名为 的成员的输出类型apply
  3. 设置一个(递归)元模板过程,该过程创建指向具有相关签名的函数的指针并将它们添加到OutputPort向量中。

我假设上面的步骤 1-3 都必须在编译时完成。如果不需要用户设计数据输出类的任何干预,我不关心解决方案的美观性。但是,我不希望使用自定义编译器宏。

这篇文章展示了如何推断成员函数签名,这可能很有用。

4

2 回答 2

2

我们知道,对于每个getOutput未定义的模板参数,它的返回类型是void. 所以我们可以确定K如下:

template <int K>
constexpr std::enable_if_t<std::is_void<decltype(getOutput<K>(0))>{}, int> getK() {
    return K-1;
}
template <int K>
constexpr std::enable_if_t<!std::is_void<decltype(getOutput<K>(0))>{}, int> getK() {
    return getK<K+1>();
}

此外,您可以按如下方式“自动化”您push_back的 s:

     AlgorithmOutput() : AlgorithmOutput(std::make_index_sequence<getK<0>()>()) {}

private:


     template <std::size_t... indices>
     AlgorithmOutput( std::integer_sequence<indices...> )
     {
         (void)std::initializer_list<int> {
             (OutputPorts.push_back([this] (int i) {return getOutput<indices>(i);}, 0)...
         };
     }

(所有代码未经测试,只是草图!)

于 2015-04-19T18:46:18.167 回答
0
using AO=AlgorithmOutput;
template<size_t N>
using R=decltype(std::declval<AO*>()->getOutput<N>(0));
template<size_t... Is>
std::vector< boost::any > make_vec( std::index_sequence<Is...> ){
  return {
    boost::any(
      std::function< R<Is>(int) >{
        [this](int x){return this->getOutput<N>(x);}
      }
    )...
  };
}

使用 C++14 类型,但易于编写。传递它std::make_index_sequence<Count>{},它将返回一个向量,其中填充了任何包装函数,这些函数包装了调用你的apply.

你的设计看起来像是一堆静态分派、动态分派、类型擦除和低效的指针语义,但我假设(仁慈地)这只是一个更复杂问题的简化,并且设计是有保证的。

但是,您的运行时检查静态参数是否不正确不应该成立。不要在运行时检查编译时错误。

static auto apply( PipeOutputClass const* p_Self, int c_Idx ) { throw std::runtime_error("Wrong template argument."); }

这似乎毫无意义:使其无法编译,而不是编译并抛出。

于 2015-04-19T19:11:29.903 回答