2015-04-19 8 views
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 выше должны быть выполнены во время компиляции. Меня не интересует эстетика решения, если это не требует вмешательства пользователя, разрабатывающего классы вывода данных. Однако я бы предпочел не использовать собственные макросы компилятора.

Это сообщение показывает, как можно infer a member function signature, что может быть полезно.

+0

Какова цель этого условного выражения? «правда? & AlgorithmOutput :: getOutput <0>: nullptr' – dyp

ответ

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

Это кажется бессмысленным: не скомпилировать, а не скомпилировать и выбросить.

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

(Весь код тестировался, а лишь эскизы!)

+0

Благодарим вас за ответ. Это не сработало «из коробки». Однако мне удалось заставить его работать после внесения некоторых поправок - см. Мой ответ. Поскольку я только начал изучать вариативные шаблоны/C++ 11/14, я считаю, что код может быть очень далек от оптимального. Таким образом, я буду благодарен за любые комментарии к коду в моем ответе. – user1391279