4
#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);

有没有办法做类似的事情?或者某种完全其他的模式(但不是运行时多态性\虚拟函数)来解决这个问题?

PS 在我的应用程序中,算法有几个子算法作为参数,子算法也有类似的结构。而且有些子算法有不同的创建界面。使用运行时多态性,我可以使用一种工厂模式,整个事情看起来不错(http://ideone.com/YAYafr),但我真的不能在这里使用虚函数。

PPS 我怀疑问题措辞反映了我在代码中实际提出的问题,所以我很乐意得到任何建议。

4

2 回答 2

3

是的。我称这种技术为魔法开关。

你创建一个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数组中。

virtual调用开销,但它发生在顶层。

于 2013-04-17T18:46:32.610 回答
0

您应该使用一个接口,同时 SubAlgorithm1 和 SubAlgorithm2(您需要比这更好的名称)实现该接口。根据 runtime_flag 创建任一类的对象。

于 2013-04-17T18:45:33.867 回答