Заполнить вектор указателями на частично специализированные члены функции автоматически

Я работаю над конвейерным шаблоном дизайна. Одна из моих целей разработки — включить динамическое связывание сегментов конвейера, предоставляя указатели для функций-членов определенного класса данных.

Каждый из классов данных имеет набор функций-членов (представляющих выходные порты класса данных), проиндексированных с использованием целочисленного аргумента шаблона. Эти функции определяют тип возвращаемого значения динамически, используя ключевое слово 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, описанные выше, должны быть выполнены во время компиляции. Меня не волнует эстетика решения, если оно не требует вмешательства пользователя, разрабатывающего классы вывода данных. Однако я бы предпочел не использовать пользовательские макросы компилятора.

Этот пост показывает, как можно вывести сигнатуру функции-члена, что может быть полезно.

2

Решение

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

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

0

Другие решения

Мы знаем, что для каждого аргумента шаблона, для которого 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следующим образом:

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

(Весь код не проверен, просто наброски!)

2

По вопросам рекламы [email protected]