2014-09-11 10 views
5

Mam funkcji otoki w C++ 11, przeznaczony do stosowania z lambda, tak:W C++ 11, w jaki sposób mogę specjalizować szablon funkcji, który pobiera obiekt funkcji na podstawie typu powrotu?

template<typename Func> 
int WrapExceptions(Func&& f) 
{ 
    try 
    { 
    return f(); 
    } 
    catch(std::exception) 
    { 
    return -1; 
    } 
} 

I mogę nazwać tak:

int rc = WrapExceptions([&]{ 
    DoSomething(); 
    return 0; 
}); 
assert(rc == 0); 

A życie jest OK. Co chcę zrobić, choć jest przeciążenie lub specjalizują funkcję otoki tak, że gdy funkcja wewnętrzna zwraca void funkcja zewnętrzna zwraca wartość domyślna 0, np:

int rc = WrapExceptions([&]{ 
    DoSomething(); 
}); 
assert(rc == 0); 

mogę rzeczywiście zrobić to w C++ 11? Nie mogę o życiu myśleć jak.

+2

Wystarczy popatrzeć na std :: result_of. http://pl.cppreference.com/w/cpp/types/result_of –

Odpowiedz

7

Możesz użyć SFINAE:

  • z std::result_of

    template<typename Func> 
    typename std::enable_if< 
        std::is_convertible<typename std::result_of<Func()>::type, int>::value, 
        int 
        >::type 
    WrapExceptions(Func&& f) 
    { 
        try { return f(); } 
        catch(std::exception) { return -1; } 
    } 
    
    template<typename Func> 
    typename std::enable_if< 
        std::is_same<void, typename std::result_of<Func()>::type>::value, 
        int 
        >::type 
    WrapExceptions(Func&& f) 
    { 
        try { f(); return 0; /* default value */ } 
        catch(std::exception) { return -1; } 
    } 
    
  • z decltype:

    template<typename Func> 
    auto 
    WrapExceptions(Func&& f) 
    -> typename std::enable_if< 
        std::is_convertible<decltype(f()), int>::value, 
        int 
        >::type 
    { 
        try { return f(); } 
        catch(std::exception) { return -1; } 
    } 
    
    template<typename Func> 
    auto 
    WrapExceptions(Func&& f) 
    -> typename std::enable_if< 
        std::is_same<void, decltype(f())>::value, 
        int 
        >::type 
    { 
        try { f(); return 0; } 
        catch(std::exception) { return -1; } 
    } 
    
4

Może nieco ponad inżynierii, ale można użyć tagu wysyłkowy: przykład

#include <stdexcept> 
#include <type_traits> 
#include <utility> 

namespace detail 
{ 
    struct returns_convertible_to_int {}; 
    struct returns_void {}; 

    template<typename Func> 
    int WrapException_dispatch(Func&& f, returns_convertible_to_int) 
    { 
     return f(); 
    } 

    template<typename Func> 
    int WrapException_dispatch(Func&& f, returns_void) 
    { 
     f(); 
     return 0; 
    } 

    template<typename T, typename dummy = void> 
    struct dispatch 
    { 
     static_assert(std::is_same<T, void>::value, 
         "Incompatible return type"); 
    }; 

    template<typename T> 
    struct dispatch<T, 
     typename std::enable_if< std::is_convertible<T, int>{} >::type> 
    { 
     using type = returns_convertible_to_int; 
    }; 

    template<typename T> 
    struct dispatch<T, 
     typename std::enable_if< std::is_same<T, void>{} >::type> 
    // alt: template<> struct dispatch<void,void> 
    { 
     using type = returns_void; 
    }; 
} 

template<typename Func> 
int WrapException(Func&& f) 
{ 
    try 
    { 
     return detail::WrapException_dispatch(std::forward<Func>(f), 
      typename detail::dispatch<decltype(f())>::type{}); 
    } 
    catch(std::exception const&) { return -1; } 
} 

Zastosowanie:

int foo() { return 42; } 
void bar() {} 

int main() 
{ 
    WrapException(foo); 
    WrapException(bar); 
} 

Można oczywiście wdrożyć krótszy wysyłkowy:

namespace detail 
{ 
    template<typename Func> 
    auto WrapException_dispatch(Func&& f, int) 
    -> typename std::enable_if< 
      std::is_convertible<decltype(f()), int>::value, int 
     >::type 
    { 
     return f(); 
    } 

    template<typename Func> 
    int WrapException_dispatch(Func&& f, ...) 
    { 
     f(); 
     return 0; 
    } 
} 

template<typename Func> 
int WrapException(Func&& f) 
{ 
    try 
    { 
     return detail::WrapException_dispatch(std::forward<Func>(f), 0); 
    } 
    catch(std::exception const&) { return -1; } 
}