管道类存储具有约束的不同模板

时间:2015-08-26 19:01:18

标签: c++ templates generic-programming

我有一个类模板DataProcessor,如下所示:

struct DataProcessorBase
{
    typedef std::shared_ptr<DataProcessorBase> Ptr;
}; // struct DataProcessorBase

template <class _Input, class _Output>
struct DataProcessor : DataProcessorBase
{
    typedef _Input Input;
    typedef _Output Output;

    virtual Output process(const Input * input) = 0;
}; // struct DataProcessor

我希望创建一个Pipeline类,它将多个DataProcessor实例连接在一起。这意味着处理器1的输出必须与处理器2的输入匹配,依此类推。如下所示:

template <class _Input, class _Output>
class Pipeline : DataProcessor<_Input, _Output>
{
public:
    Output process(const Input * input);
private:    
    std::vector<DataProcessorBase::Ptr> _processors;
}; // class Pipeline

template <class _Input, class _Output>
_Output Pipeline<_Input, _Output>::process(const _Input * input)
{
    // this is where I start guessing...
    auto rawPtr = dynamic_cast<DataProcessor<_Input, TYPEOFFIRSTPROCESSORSOUTPUT>*>(_processors[0]);
    assert(rawPtr);
    for (size_t i = 0; i < _processors.size(); ++i)
    {
        ...
    }
}

我可以说这种实现Pipeline :: process的方式不正确。有人能指出我正确的方向吗?

1 个答案:

答案 0 :(得分:3)

解除拨入和拨出电话。

数据传入和数据出现应该在不同的步骤发生。然后,每个数据使用者都可以知道它需要什么,并为您进行转换(如果出现问题,可能会抛出或错误标记)。

struct processor {
  virtual ~processor () {};
  virtual bool can_read_from( processor const& ) const = 0;
  virtual void read_from( processor& ) = 0;
  virtual bool ready_to_sink() const = 0;
  virtual bool ready_to_source() const = 0;
};
template<class T>
struct sink {
  virtual void operator()( T&& t ) = 0;
  virtual ~sink() {}
};
template<class T>
struct source {
  virtual T operator()() = 0;
  virtual ~source() {}
};
template<class In, class Out, class F>
struct step: processor, sink<In>, source<Out> {
  F f;
  step( F&& fin ):f(std::move(fin)) {}

  step(step&&)=default;
  step(step const&)=default;
  step& operator=(step&&)=default;
  step& operator=(step const&)=default;
  step()=default;

  std::experimental::optional<Out> data;
  virtual void operator()( In&& t ) final override {
    data = f(std::move(t));
  }
  virtual bool ready_to_sink() const {
    return !data;
  }
  virtual Out operator()() final override {
    auto tmp = std::move(data);
    data = {};
    return std::move(*tmp);
  }
  virtual bool ready_to_source() const final override {
    return static_cast<bool>(data);
  }
  virtual bool can_read_from( processor const& o ) final override {
    return dynamic_cast<source<In> const*>(&o);
  }
  virtual void read_from( processor &o ) final override {
    (*this)( dynamic_cast<source<In>&>(o)() );
  }
};
template<class In, class Out>
struct pipe {
  std::shared_ptr<processor> first_step;
  std::vector< std::shared_ptr<processor> > steps;
  pipe(std::shared_ptr<processor> first, std::vector<std::shared_ptr<processor>> rest):
    first_step(first), steps(std::move(rest))
  {}
  Out operator()( In&& in ) {
    (*dynamic_cast<sink<In>*>(steps.first_step.get()))( std::move(in) );
    auto last = first_step;
    for (auto step:steps) {
      step->read_from( *last );
      last = step;
    }
    return (*dynamic_cast<source<Out>*>(last.get())();
  }
};
template<class In, class Out>
struct pipeline:step<In, Out, pipe<In,Out>> {
  pipeline( std::shared_pointer<processor> first, std::vector<std::shared_ptr<processor>> steps ):
    step<In, Out, pipe<In,Out>>({ first, std::move(steps) })
  {}
};