自动用指向部分特化函数成员的指针填充向量

Fill a vector with pointers to partially specialized function members automatically

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

每个数据 classes 都有一组函数成员(代表数据 class 输出端口)使用整数模板参数进行索引。这些函数使用关键字 auto 动态推导 return 类型,但都接受相同的整数参数 c_Idx,即 template <int N> auto getOutput(int c_Idx) const。与每个函数 getOutput 关联的功能是(由用户)在一组部分专用结构 getOutputImpl 中定义的。因此,每个数据 class 可以具有从 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. 的用户定义部分专用结构的数量
  3. 对于每个这样的结构,确定其名为 apply 的成员的输出类型。
  4. 设置一个(递归)元模板过程,创建指向具有相关签名的函数的指针,并将它们添加到 OutputPort 向量中。

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

这个 post 展示了如何 infer a member function signature,这可能会有用。

我们知道,对于每个未定义 getOutput 的模板参数,其 return 类型为 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>();
}

另外,您可以 "automate" 您的 push_back 如下:

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

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

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>{} ,它将 return 一个向量,其中填充了任何包装函数包装 lambda,它调用你的 apply.

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

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

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

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