Can I separate creation and usage locations of compile-time strategies?

前端 未结 2 1472
慢半拍i
慢半拍i 2021-01-06 11:00
#include 
#include 
#include 
#include 

using namespace std;

struct SubAlgorithm1 { void operator ()          


        
2条回答
  •  轻奢々
    轻奢々 (楼主)
    2021-01-06 11:35

    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
    bool magic_switch( int n, Func&& f,  std::tuple const & pick ) {
      if( n==Max-1 ) {
        f(std::get(pick));
        return true;
      } else {
        return magic_switch( n, std::forward(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 
    #include 
    #include 
    #include 
    #include 
    #include 
    #include 
    #include 
    
    // metaprogramming boilerplate:
    templateclass Factory, typename SourceTuple>
    struct tuple_map;
    templateclass Factory, templateclass L, typename... SourceTypes>
    struct tuple_map> {
      typedef L< Factory... > type;
    };
    templateclass Factory, typename SourceTuple>
    using MapTuple = typename tuple_map::type;
    template struct seq {};
    template
    struct make_seq: make_seq {};
    template
    struct make_seq<0, s...> {
      typedef seq type;
    };
    template
    using MakeSeq = typename make_seq::type;
    
    // neat little class that lets you type-erase the contents of a tuple,
    // and turn it into a uniform array:
    template
    struct TupleToArray;
    templateclass L, typename... Ts, typename DestType>
    struct TupleToArray, DestType> {
      template
      std::array< DestType, sizeof...(Ts) > operator()( L const& src, seq ) const {
        std::array< DestType, sizeof...(Ts) > retval{ DestType( std::get(src) )... };
        return retval;
      }
    
      std::array< DestType, sizeof...(Ts) > operator()( L const& src ) const {
        return (*this)( src, MakeSeq() );
      }
    };
    template< typename DestType, typename SourceTuple >
    auto DoTupleToArray( SourceTuple const& src )
      -> decltype( TupleToArray()( src ) )
    {
      return TupleToArray()( 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
    struct FullAlgo {
      void operator()( std::vector& 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&) > funcType;
    std::array< funcType, 2 > fullAlgoArray =
      DoTupleToArray< funcType >( fullAlgos );
    
    int main() {
      std::vector 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.

提交回复
热议问题