Могу ли я разделить места создания и использования стратегий времени компиляции?

#include <iostream>
#include <vector>
#include <algorithm>
#include <sstream>

using namespace std;

struct SubAlgorithm1 { void operator () (int /*i*/) { cout << "1" << endl; } };
struct SubAlgorithm2 { void operator () (int /*i*/) { cout << "2" << endl; } };

template<typename SubAlgorithm, typename Collection>
void Alrogirthm(SubAlgorithm& f, Collection& stuff) {
// In my code f is invoked ~ 1e9 times (it's a loop that is executed ~
// 1e6 times, and stuff.size() is ~1000). The application spends ~90% of
// it's time in this function, so I do not want any virtual function
// calls to slow down my number-crunching.
for (int i = 0; i < 1; ++i) for_each(stuff.begin(), stuff.end(), f);
}

int main(int , char**) {
vector<int> stuff;
stuff.push_back(1);

bool runtime_flag = true; // let's pretend it was read from config
if (runtime_flag) {
typedef SubAlgorithm1 SubAlgorithm;

SubAlgorithm sub_algorithm;
Alrogirthm(sub_algorithm, stuff);
}
else {
typedef SubAlgorithm2 SubAlgorithm;

SubAlgorithm sub_algorithm;
Alrogirthm(sub_algorithm, stuff);
}

return 0;
}

То, что я действительно хотел бы написать вместо предложения if выше:

TypeClass SubAlgorithm = runtime_flag : SubAlgorithm1 ? SubAlgorithm2;
SubAlgorithm sub_algorithm;
Algorithm(sub_algorithm, stuff);

Есть ли способ сделать что-то подобное? Или какой-то совершенно другой шаблон (но не полиморфизм во время выполнения \ виртуальные функции) для решения этой проблемы?

Постскриптум В моем приложении Алгоритм имеет несколько SubAlgorithms в качестве параметров и SubAlgorithms, а также имеют сходную структуру. Более того, некоторые субалгоритмы имеют различный интерфейс создания. С полиморфизмом во время выполнения я могу использовать вид фабричного шаблона, и все выглядит хорошо (http://ideone.com/YAYafr), но я действительно не могу использовать виртуальные функции здесь.

P.P.S. Я сомневаюсь, что формулировка вопроса отражает то, что я на самом деле задаю в коде, поэтому я был бы рад получить любые предложения.

4

Решение

Да. Я называю технику магическим переключателем.

Вы создаете std::tuple ваших алгоритмов. Вы устанавливаете шаблонную функцию, которой будет передан один из этих алгоритмов.

Вы можете добавить другие аргументы с помощью совершенной вариационной пересылки, если хотите.

template<size_t Max, typename...Ts, typename Func>
bool magic_switch( int n, Func&& f,  std::tuple<Ts...> const & pick ) {
if( n==Max-1 ) {
f(std::get<Max-1>(pick));
return true;
} else {
return magic_switch<Max-1>( n, std::forward<Func>(f), pick );
}
}

В псевдокоде. Специализируйте Max == 0, чтобы просто вернуть false, и вам, возможно, придется сделать его функтором, чтобы вы могли частично специализироваться.

Переданный в функторе раздражает писать, как обратная сторона.

Другой вариант — использовать мета-фабрику (ну, фабрика метапрограммирования? Может быть, это мета-карта. Ну, что угодно.)

#include <iostream>
#include <tuple>
#include <vector>
#include <utility>
#include <cstddef>
#include <functional>
#include <array>
#include <iostream>

// metaprogramming boilerplate:
template<template<typename>class Factory, typename SourceTuple>
struct tuple_map;
template<template<typename>class Factory, template<typename...>class L, typename... SourceTypes>
struct tuple_map<Factory, L<SourceTypes...>> {
typedef L< Factory<SourceTypes>... > type;
};
template<template<typename>class Factory, typename SourceTuple>
using MapTuple = typename tuple_map<Factory, SourceTuple>::type;
template<std::size_t...> struct seq {};
template<std::size_t max, std::size_t... s>
struct make_seq: make_seq<max-1, max-1, s...> {};
template<std::size_t... s>
struct make_seq<0, s...> {
typedef seq<s...> type;
};
template<std::size_t max>
using MakeSeq = typename make_seq<max>::type;

// neat little class that lets you type-erase the contents of a tuple,
// and turn it into a uniform array:
template<typename SourceTuple, typename DestType>
struct TupleToArray;
template<template<typename...>class L, typename... Ts, typename DestType>
struct TupleToArray<L<Ts...>, DestType> {
template<std::size_t... Index>
std::array< DestType, sizeof...(Ts) > operator()( L<Ts...> const& src, seq<Index...> ) const {
std::array< DestType, sizeof...(Ts) > retval{ DestType( std::get<Index>(src) )... };
return retval;
}

std::array< DestType, sizeof...(Ts) > operator()( L<Ts...> const& src ) const {
return (*this)( src, MakeSeq<sizeof...(Ts)>() );
}
};
template< typename DestType, typename SourceTuple >
auto DoTupleToArray( SourceTuple const& src )
-> decltype( TupleToArray<SourceTuple, DestType>()( src ) )
{
return TupleToArray<SourceTuple, DestType>()( src );
}

// Code from here on is actually specific to this problem:
struct SubAlgo { int operator()(int x) const { return x; } };
struct SubAlgo2 { int operator()(int x) const { return x+1; } };

template<typename Sub>
struct FullAlgo {
void operator()( std::vector<int>& v ) const {
for( auto& x:v )
x = Sub()( x );
}
};

// a bit messy, but I think I could clean it up:
typedef std::tuple< SubAlgo, SubAlgo2 > subAlgos;
MapTuple< FullAlgo, subAlgos > fullAlgos;
typedef std::function< void(std::vector<int>&) > funcType;
std::array< funcType, 2 > fullAlgoArray =
DoTupleToArray< funcType >( fullAlgos );

int main() {
std::vector<int> test{1,2,3};
fullAlgoArray[0]( test );
for (auto&& x: test)
std::cout << x;
std::cout << "\n";
fullAlgoArray[1]( test );
for (auto&& x: test)
std::cout << x;
std::cout << "\n";
}

Это довольно много, но то, что я только что сделал, позволило вам взять свой под-алгоритм без сохранения состояния и подключить его к вашему полному алгоритму по одному элементу за раз, а затем стереть полученный полный алгоритм и сохранить его в std::function массив.

Eсть virtual накладные расходы, но это происходит на верхнем уровне.

3

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

Вы должны использовать интерфейс с SubAlgorithm1 и SubAlgorithm2 (вам потребуются более точные имена) для реализации интерфейса. Создание объекта любого класса в зависимости от runtime_flag.

0

По вопросам рекламы ammmcru@yandex.ru
Adblock
detector