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

前端 未结 2 1443
慢半拍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<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.

    0 讨论(0)
  • 2021-01-06 11:43

    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.

    0 讨论(0)
提交回复
热议问题