Is There Anything Like a Templatized Case-Statement

后端 未结 5 964
没有蜡笔的小新
没有蜡笔的小新 2020-12-16 18:18

So I have this really ugly code:

template 
std::conditional_t

        
相关标签:
5条回答
  • 2020-12-16 18:18

    The template version of a switch statement is a specialized template.

    template<size_t n> struct matching_type;
    template<> struct matching_type<sizeof(char)> { typedef char type; };
    template<> struct matching_type<sizeof(short)> { typedef short type; };
    template<> struct matching_type<sizeof(int)> { typedef int type; };
    template<> struct matching_type<sizeof(long)> { typedef long type; };
    template<> struct matching_type<sizeof(long long)> { typedef long long type; };
    
    template<typename T>
    matching_type<sizeof(T)>::type foo(T bar)
    {
        return reinterpret_cast<decltype(foo(bar))>(bar);
    }
    
    0 讨论(0)
  • 2020-12-16 18:18

    As long as you understand the risk that the same sized type may not be the convertible, you could simply plugin a mpl::map..

    typedef map<
          pair<int_<sizeof(char)>, char>,
          pair<int_<sizeof(short)>, short>,
          pair<int_<sizeof(int)>, int>,
          pair<int_<sizeof(long long)>, long long>
        > m;
    

    e.g.

    #include <algorithm>
    #include <iostream>
    
    #include <boost/mpl/at.hpp>
    #include <boost/mpl/map.hpp>
    
    using namespace boost::mpl;
    
    typedef map<
          pair<int_<sizeof(char)>, char>,
          pair<int_<sizeof(short)>, short>,
          pair<int_<sizeof(int)>, int>,
          pair<int_<sizeof(long long)>, long long>
        > m;
    
    template <typename T>
    typename at<m, int_<sizeof(T)>>::type foo(T bar)
    { return reinterpret_cast<decltype(foo(bar))>(bar); }
    
    
    struct doh
    {
        std::string a, b, c;
    };
    
    int main()
    {
        {
          char c;
          static_assert(std::is_same<decltype(foo(c)), char>::value, "error");
        }
        {
          short c;
          static_assert(std::is_same<decltype(foo(c)), short>::value, "error");
        }
        {
          int c;
          static_assert(std::is_same<decltype(foo(c)), int>::value, "error");
        }
        {
          long long c;
          static_assert(std::is_same<decltype(foo(c)), long long>::value, "error");
        }
        {
          double c;
          static_assert(std::is_same<decltype(foo(c)), long long>::value, "error");
        }    
        {
          doh c;
          static_assert(std::is_same<decltype(foo(c)), void_>::value, "error");
        }    
    }
    
    0 讨论(0)
  • 2020-12-16 18:19

    A type tag:

    template<class T>struct tag{using type=T;};
    

    void_t (coming in C++17 to a compiler near you):

    template<class...>struct voider{using type=void;};
    template<class...Ts>using void_t=typename voider<Ts...>::type;
    

    enable_first_t takes a pack of std::enable_if (note the lack of _t), and returns the first that passes the test. You can use tag<X> to replace std::enable_if<true, X>:

    template<class T,class=void>struct has_type:std::false_type{};
    template<class T>struct has_type<T, void_t<typename T::type>>:std::true_type{};
    
    namespace details {
      template<class, class...Ts>
      struct enable_first {};
      template<class T0, class...Ts>
      struct enable_first<std::enable_if_t< !has_type<T0>{} >, T0, Ts... >:enable_first<void, Ts...> {};
      template<class T0, class...Ts>
      struct enable_first<std::enable_if_t<  has_type<T0>{} >, T0, Ts...>:T0 {};
    }
    
    template<class...Ts>using enable_first_t=typename details::enable_first<void, Ts...>::type;
    
    template<class T>
    using result = enable_first_t<
      std::enable_if<sizeof(T)==sizeof(char), char>,
      std::enable_if<sizeof(T)==sizeof(short), short>,
      std::enable_if<sizeof(T)==sizeof(long), long>,
      tag<int> // default
    >;
    

    this behaves a lot like a switch, but the statements are full boolean expressions.

    live example

    0 讨论(0)
  • 2020-12-16 18:26

    I had to do something like this once so I wrote a small wrapper to acheive the result neatly. You could use it as follows (see here for a test)

    template<class T>
    typename static_switch<sizeof(T)
                ,int // default case
                ,static_case<sizeof(char),char>
                ,static_case<sizeof(short),short>
                ,static_case<sizeof(long),long>
                >::type foo(T bar){ ... }
    

    Behind the scenes it pretty much does what you already have but by wrapping it we keep it (more) readable. There is also a version to allow you to switch direclty on the type T if you needed that.

    Edit: At @Deduplicator's suggestion here is the code behind it

    #include <type_traits>  
    
    /* 
     * Select a type based on the value of a compile-time constant such as a 
     * constexpr or #define using static_switch. 
     */ 
    
    template<int I,class T> 
    struct static_case { 
        static constexpr int value = I; 
        using type = T; 
    }; 
    
    template<int I, class DefaultType, class Case1, class... OtherCases> 
    struct static_switch{ 
        using type = typename std::conditional< I==Case1::value ,  
                        typename Case1::type, 
                        typename static_switch<I,DefaultType,OtherCases...>::type 
                         >::type; 
    }; 
    
    struct fail_on_default {};
    
    template<int I, class DefaultType, class LastCase> 
    struct static_switch<I,DefaultType,LastCase> { 
        using type = typename std::conditional< I==LastCase::value ,  
                        typename LastCase::type, 
                        DefaultType 
                         >::type; 
    
        static_assert(!(std::is_same<type, fail_on_default>::value),
                      "Default case reached in static_switch!");
    }; 
    
    0 讨论(0)
  • 2020-12-16 18:28

    Something like this perhaps:

    template <size_t N> struct SuitablySized;
    
    template<> struct SuitablySized<sizeof(char)> {
      typedef char type;
    };
    template<> struct SuitablySized<sizeof(short)> {
      typedef short type;
    };
    // Add more cases to taste
    
    template <typename T>
    typename SuitablySized<sizeof(T)>::type foo(T bar);
    
    0 讨论(0)
提交回复
热议问题