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

What I would really love to write instead of the if clause above:

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

Is there any way of doing something similar? Or some kind of completely other pattern (but not run-time polymorphism\virtual functions) to solve that issue?

P.S. In my application Algorithm has several SubAlgorithms as parameters and SubAlgorithms as well have similar structure. Moreover some SubAlgorithms have different creation interface. With run-time polymorphism I can use kind of Factory pattern and the whole thing looks nice (http://ideone.com/YAYafr), but I really cannot use virtual functions here.

P.P.S. I doubt the question phrasing reflects what I actually ask in the code, so I'd be happy to get any suggestions.

Anton Daneyko
  • 6,528
  • 5
  • 31
  • 59
  • whatever you do at run-time will either be as inelegant and direct as your `if` clause, or invoke some sort of indirection via a pointer, reference or virtual table. However, the indirection can be at the top level rather than down in the innermost loop, so that performance is hardly affected – Walter Apr 17 '13 at 18:47
  • I seem to remember one option is define a macro on the command line to solve this problem. – andre Apr 17 '13 at 19:07

2 Answers2

3

Yes. I call the technique the magic switch.

You create a std::tuple of your algorithms. You ceate a template function that will be passed one of those algorithms.

You can add other arguments via perfect variardic forwarding if you want.

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

In pseudo code. Specialize Max==0 to just return false, and you might have to make it a functor so you can partial specialize.

The passed in functor is annoying to write, as a downside.

Another variation is to use a meta-factory (well, a meta programming type factory? Maybe it is a meta-map. Well, whatever.)

#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";
}

which is lots of boilerplate, but what I've just done is allowed you to take your stateless sub algorithm and plug it into your full algorithm one element at a time, then type-erase the resulting full algorithm and store it in a std::function array.

There is a virtual call overhead, but it occurs at the top level.

Yakk - Adam Nevraumont
  • 262,606
  • 27
  • 330
  • 524
  • 1
    that is cute. it's essentially the same design as the OP's original (the only way to avoid indirection, see my comment) but the `if` block is hidden in your `magic_switch()`. – Walter Apr 17 '13 at 18:50
  • @Walter Except, of course, that adding more types requires a higher `Max` and a bigger tuple. You can also deduce the `Max` from the type of the tuple if you wanted. In effect, I am getting the compiler to write the nested `if` statements for me at the top level. You can also use a variation of this technique to generate a type-erased array of `std::function`s with the type of the `SubAlgorithm` applied, then invoke the function via array lookup. – Yakk - Adam Nevraumont Apr 17 '13 at 19:18
0

You should use an interface, with both SubAlgorithm1 and SubAlgorithm2 (you'll need better names than that) implementing the interface. The create an object of either class depending on runtime_flag.

segfault
  • 5,759
  • 9
  • 45
  • 66