2015-02-24 6 views
11

So habe ich diesen wirklich hässlich Code:Gibt es etwas wie ein Templatized Case-Statement

template <typename T> 
std::conditional_t<sizeof(T) == sizeof(char), 
        char, 
        conditional_t<sizeof(T) == sizeof(short), 
           short, 
           conditional_t<sizeof(T) == sizeof(long), 
               long, 
               enable_if_t<sizeof(T) == sizeof(long long), 
                  long long>>>> foo(T bar){return reinterpret_cast<decltype(foo(bar))>(bar);} 

Ich bin mit verschachtelten conditional_t s case-Anweisung der Art zu machen. Gibt es da draußen etwas, das das eleganter macht, oder muss ich meine eigene templated-case-Aussage aufstellen?

Hinweis: Ich bin eigentlich bewusst, dass diese Verwendung von reinterpret_cast schlecht ist: Why Doesn't reinterpret_cast Force copy_n for Casts between Same-Sized Types?

+1

Was dieser Code zu erreichen versucht? – Slava

+0

Sie könnten eine Vorlage für char, kurz, int, lang lang ... – amchacon

+0

Warum fühlt sich das wie Ihre nach abhängigen Programmiersprachen an? –

Antwort

10

Ich hatte einmal so etwas zu tun, damit ich ein small wrapper schrieb ordentlich um das Ergebnis zu acheive. Man könnte es wie folgt verwenden (siehe here für einen 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){ ... } 

Hinter den Kulissen ist es ziemlich viel tut, was Sie bereits haben, aber durch Umwickeln wir halten es (mehr) lesbar. Es gibt auch eine Version, die es Ihnen erlaubt direkt auf den Typ T umzuschalten, wenn Sie das brauchen.

Edit: An @ Deduplicator Vorschlag hier ist der Code dahinter

#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!"); 
}; 
+3

Vielleicht möchten Sie hier Ihren Code von GitHub importieren ... damit wäre die Antwort vollständig. Wie auch immer, ich habe gerade so etwas geschrieben, aber du hast es verstanden. – Deduplicator

+0

@Dan Ich bin mir nicht sicher, wie ich das anwenden kann. Wenn Sie es anschließen können, sollte es ziemlich einfach zu testen sein: 'auto val = foo (13.0); static_assert (is_same :: value);' –

+0

@ JonathanMee: Als Nebensächlich, Es kann vereinfacht werden, wenn der Wert wie in Ihrem Fall vom Typ abgeleitet werden kann. – Deduplicator

2

So etwas wie dies vielleicht:

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); 
+2

Hoffen wir, dass OP alle gleichgroßen Typen vorher aussortiert ... – Deduplicator

4

Die Vorlage Version einer switch-Anweisung eine spezielle Vorlage ist.

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

Hoffen wir, dass OP alle gleichgroßen Typen vorher aussortiert ... – Deduplicator

+0

Beachten Sie, dass bei einigen Systemen 'sizeof (int)' gleich 'sizeof (lang) 'und dieser Code würde nicht kompiliert – borisbn

+2

True, aber' switch' Anweisungen haben das gleiche Problem! –

3

Solange Sie das Risiko, dass die gleiche Größe Art verstehen nicht konvertierbar sein kann, können Sie einfach Plugin eine 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; 

z

#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

Das sieht nach einer vielversprechenden Lösung aus. Aber ich habe keinen Boost. Ich frage mich, ob das etwas ist, was mit einer 'std :: map' irgendwie erreicht werden könnte? –

+1

@ JonathanMee, nicht möglich mit 'std :: map', Sie könnten es mit einem' switch' machen, aber es wäre ein Laufzeit-Test statt einer Kompilierzeit ... – Nim

1

Ein Typ tag:

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

void_t (kommt in C++ 17 zu einem Compiler in Ihrer Nähe):

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

enable_first_t nimmt eine Packung std::enable_if (man beachte das Fehlen von _t) und gibt das erste zurück, das den Test besteht. Sie können tag<X>std::enable_if<true, X> ersetzen verwenden:

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

dies viel wie ein switch verhält, aber die Aussagen sind voll Booleschen Ausdrücken.

live example

+0

Warum nicht 'Vorlage mit void_t = void; '? – dyp