2014-09-11 13 views
5

Ho una funzione wrapper in C++ 11, progettato per essere utilizzato con lambda, in questo modo:In C++ 11, come si specializza un modello di funzione che accetta un oggetto funzione in base al tipo restituito?

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

E mi può chiamare in questo modo:

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

E la vita è OK. Quello che voglio fare, però, è sovraccarico o specializzarsi la funzione wrapper in modo tale che quando la funzione interna restituisce void, la funzione esterna restituisce un valore di default pari a 0, ad esempio:

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

Posso effettivamente fare questo in C++ 11? Non posso per la vita di me pensare a come.

+2

Dai un'occhiata alla std :: result_of. http://en.cppreference.com/w/cpp/types/result_of –

risposta

7

È possibile utilizzare SFINAE:

  • con 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; } 
    } 
    
  • con 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

Forse un po 'sovra-ingegnerizzato, ma è possibile utilizzare tag spedizione: esempio

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

Usage:

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

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

È possibile, naturalmente, implementare un dispaccio più breve:

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; } 
} 
Problemi correlati