2015-02-24 14 views
11

Więc mam ten kod naprawdę brzydki:Czy jest coś Niczym 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);} 

używam zagnieżdżone conditional_t s dokonać orzecznictwem stwierdzenie rodzajów. Czy jest coś, co czyni to bardziej elegancko, czy też muszę przygotować własne oświadczenie na temat templatyzacji?

Uwaga: Jestem świadomy, że faktycznie to wykorzystanie reinterpret_cast jest zły: Why Doesn't reinterpret_cast Force copy_n for Casts between Same-Sized Types?

+1

Co ten kod próbuje osiągnąć? – Slava

+0

Można zrobić jeden szablon char, krótki, int, długo długi ... – amchacon

+0

Dlaczego to jest jak po języków programowania zależnych od typowania? –

Odpowiedz

10

musiałem zrobić coś takiego kiedyś tak napisałem small wrapper do acheive wynik starannie. Można go używać w następujący sposób (patrz here na 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){ ... } 

za kulisami to dość dużo robi to, co już masz, ale przez owinięcie go trzymamy go (więcej) czytelny. Dostępna jest również wersja umożliwiająca przełączanie typu direclty na typ T, jeśli jest to potrzebne.

Edit: W @ sugestia Deduplicator tu jest kod za nim

#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

Być może zechcesz zaimportować swój kod z githuba tutaj ... aby odpowiedź była kompletna. W każdym razie, właśnie pisałem coś takiego, ale dostałeś to. – Deduplicator

+0

@ Dan Nie jestem pewien, czy rozumiem, jak to zastosować. Jeśli możesz go podłączyć, test powinien być dość prosty: 'auto val = foo (13.0); static_assert (is_same :: value);' –

+0

@ JonathanMee: Na bok, można go uprościć, jeśli wartość można wyprowadzić z typu, tak jak w twoim przypadku. – Deduplicator

2

Coś takiego może:

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

Miejmy nadzieję, że OP pozbywa się wcześniej wszystkich typów równych rozmiarów ... – Deduplicator

4

Wersja szablonu z instrukcji switch jest wyspecjalizowanym szablon.

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

Miejmy nadzieję, że OP weźmie wcześniej wszystkie typy o równych rozmiarach ... – Deduplicator

+0

Należy zauważyć, że w niektórych systemach 'sizeof (int)' może być równe 'sizeof (long) 'i ten kod nie zostałby skompilowany – borisbn

+2

Prawda, ale instrukcje' switch' mają ten sam problem! –

3

tak długo, jak zrozumieć ryzyko, że ten sam rodzaj wielkości nie może być zamienny, można po prostu wtyczkę do 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; 

np.

#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

To wygląda obiecująco. Ale nie mam Boost. Zastanawiam się, czy jest to coś, co można w jakiś sposób osiągnąć za pomocą 'std :: map '? –

+1

@ JonathanMee, nie jest możliwe z 'std :: map', można to zrobić za pomocą' switch', ale byłby to test w czasie wykonywania zamiast kompilacji ... – Nim

1

typ tag:

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

void_t (C++ w najbliższych 17 do kompilatora blisko ciebie):

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

enable_first_t bierze paczkę std::enable_if (zwróć uwagę na brak of _t) i zwraca pierwszą, która przejdzie test. Można użyć tag<X> zastąpić 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 
>; 

ten zachowuje się trochę jak switch, ale wypowiedzi są pełne wyrażenia logiczne.

live example

+0

Dlaczego nie 'szablon przy użyciu void_t = void; '? – dyp

Powiązane problemy