2013-02-20 63 views
8

मैं एक ऐसी स्थिति की तरह std::function बनाना चाहता हूं जो एक से अधिक अधिभार को संग्रहीत कर सकता है।"मैनुअल" हस्ताक्षर ओवरलोड रिज़ॉल्यूशन

सिंटेक्स इस तरह का: my_function< int(double, int), double(double, double), char(int, int) >

या, और अधिक स्पष्ट रूप:

template<typename... Ts> 
struct type_list {}; 

template<typename... Signatures > 
struct my_function { 
    std::tuple< std::function<Signatures>... > m_functions; 
    typedef type_list<Signatures...> sig_list; 
    template<typename... Args> 
    typename pick_overload_signature< sig_list, type_list<Args...> >::return_value 
    operator()(Args&&... args) 
    { 
    return get<pick_overload_signature< sig_list, type_list<Args...> >::index>(m_functions)(std::forward<Args>(args)...); 
    } 
}; 

मेरा प्रश्न: मैं कैसे pick_overload_signatures लिखना चाहिए?

मेरे झुकाव तर्कों की किसी दिए गए सेट के संबंध में समारोह हस्ताक्षर पर एक आंशिक आदेश लिखने के लिए हो सकता है, तो समारोह हस्ताक्षर के प्रकार सूची में, सॉर्ट तो:

यहाँ काम मैं इस पर किया है है सबसे अच्छा पकड़ो (संभावित रूप से एक संकलन-समय का दावा है कि सबसे अच्छा अद्वितीय है)। इसे खींचने के लिए, मुझे फ़ंक्शन हस्ताक्षर पर एक ठोस आंशिक क्रम (पास किए गए तर्कों के सेट के संबंध में) होना होगा ...

13.3.3.1 मुझे बताता है कि वैध रूपांतरण क्या है या नहीं । मैं इसके लिए रूपांतरण करने के लिए कंपाइलर का उपयोग करके धोखा दे सकता हूं, और यह पता लगाने के लिए SFINAE का उपयोग कर सकता हूं कि यह किसी दिए गए तर्क के लिए हुआ है और "अधिभार" में से एक के हस्ताक्षर।

13.3.3.2 मुझे बताता है कि इन रूपांतरणों को कैसे ऑर्डर करें। यहां मुझे यह पता लगाना है कि रूपांतरण अनुक्रम उपयोगकर्ता परिभाषित है या मानक अनुक्रम है या नहीं। मुझे यकीन नहीं है कि दोनों के बीच अंतर कैसे करें।

शायद मैं उपयोगकर्ता परिभाषित रूपांतरण अनुक्रमों के अस्तित्व का पता लगाने के लिए गुण वर्ग का उपयोग कर सकता हूं। &S::operator D() और &D::D(S const&) और &D::D(S) और &D::D(S&&) या इस तरह के कुछ के अस्तित्व की जांच करें।

has_user_defined_conversion<S,D>::value, has_standard_conversion<S,D>::value, आदि?

क्या यह दृष्टिकोण काम करेगा, क्या किसी ने पहले से ही यह किया है, या किसी ने पहले से ही इसका कुछ हिस्सा किया है?

Result of Answers

#include <type_traits> 
#include <cstddef> 
#include <utility> 
#include <functional> 
#include <tuple> 
#include <string> 

// Packaged list of types: 
template<typename... Ts> 
struct type_list { 
    template<template<typename...>class target> 
    struct apply { 
     typedef target<Ts...> type; 
    }; 
    template<typename T> 
    struct append { 
     typedef type_list< Ts..., T > type; 
    }; 
    template<typename T> 
    struct prepend { 
     typedef type_list< T, Ts... > type; 
    }; 
}; 
template<template<typename>class mapper, typename list> 
struct map_types { 
    typedef type_list<> type; 
}; 
template<template<typename>class mapper, typename T0, typename... Ts> 
struct map_types<mapper, type_list<T0, Ts...>> { 
    typedef typename map_types<mapper, type_list<Ts...>>::type tail; 
    typedef typename tail::template prepend< typename mapper<T0>::type >::type type; 
}; 
template<template<typename>class mapper, typename list> 
using MapTypes = typename map_types<mapper, list>::type; 
template<template<typename>class temp> 
struct apply_template_to { 
    template<typename T> 
    struct action { 
     typedef temp<T> type; 
    }; 
}; 
template<template<typename> class temp, typename list> 
struct apply_to_each:map_types< apply_template_to<temp>::template action, list > {}; 
template<template<typename> class temp, typename list> 
using ApplyToEach = typename apply_to_each<temp, list>::type; 

template<std::size_t n, typename list> 
struct nth_type {}; 
template<std::size_t n, typename first, typename... elements> 
struct nth_type<n, type_list<first, elements...>>:nth_type<n-1, type_list<elements...>> 
{}; 
template<typename first, typename... elements> 
struct nth_type<0, type_list<first, elements...>> 
{ 
    typedef first type; 
}; 
template<std::size_t n, typename list> 
using NthType = typename nth_type<n, list>::type; 

// func data 
template<typename R, typename... Args> 
struct unpacked_func { 
    typedef R result_type; 
    typedef type_list<Args...> args_type; 
    typedef unpacked_func< R, Args... > unpacked_type; 
    template<template<typename>class target> 
    struct apply { 
     typedef target<R(Args...)> type; 
    }; 
}; 

namespace unpack_details { 
    // Extracting basic function properties: 
    template<typename Func> 
    struct unpack_func {}; 
    template<typename R, typename... Args> 
    struct unpack_func< R(Args...) > { 
     typedef unpacked_func< R, Args... > type; 
    }; 
    template<typename R, typename... Args> 
    struct unpack_func< unpacked_func<R, Args...> >: 
     unpack_func< R(Args...) > 
    {}; 
} 

template<typename Func> 
using FuncUnpack = typename unpack_details::unpack_func<Func>::type; 

template<typename Func> 
struct func_props:func_props<FuncUnpack<Func>> {}; 
template<typename R, typename... Args> 
struct func_props<unpacked_func<R, Args...>>: 
    unpacked_func<R, Args...> 
{}; 

template<typename Func> 
using FuncResult = typename func_props<Func>::result_type; 
template<typename Func> 
using FuncArgs = typename func_props<Func>::args_type; 

template<typename Func> 
struct make_func_ptr:make_func_ptr<FuncUnpack<Func>> {}; 

template<typename R, typename... Args> 
struct make_func_ptr< unpacked_func< R, Args... > > { 
    typedef R(*type)(Args...); 
}; 
template<typename Func> 
using MakeFuncPtr = typename make_func_ptr<Func>::type; 

// Marking a type up with an index: 
template<typename R, std::size_t i> 
struct indexed_type { 
    typedef R type; 
    enum { value = i }; 
}; 

// Sequences of size_t: 
template<std::size_t... s> 
struct seq {}; 
template<std::size_t min, std::size_t max, std::size_t... s> 
struct make_seq: make_seq< min, max-1, max-1, s...> {}; 
template<std::size_t min, std::size_t... s> 
struct make_seq< min, min, s...> { 
    typedef seq<s...> type; 
}; 
template<std::size_t max, std::size_t min=0> 
using MakeSeq = typename make_seq<max, min>::type; 

namespace overload_details { 
    template<std::size_t n, typename... Overloads> 
    struct indexed_linear_signatures {}; 

    template<typename Overload> 
    struct signature_generator {}; 
    template<typename R, typename... Args> 
    struct signature_generator<unpacked_func<R, Args...>> { 
     R operator()(Args...); // no impl 
    }; 


    template<typename Func, std::size_t i> 
    struct indexed_retval {}; 

    template<typename R, typename... Args, std::size_t i> 
    struct indexed_retval< unpacked_func<R, Args...>, i > { 
     typedef unpacked_func<indexed_type<R,i>, Args...> type; 
    }; 

    template<typename Func, std::size_t i> 
    using IndexRetval = typename indexed_retval<Func,i>::type; 

    void test1() { 
     typedef overload_details::IndexRetval< FuncUnpack<void()>, 0 > indexed; 
     indexed::apply<std::function>::type test = []()->indexed_type<void,0> {return indexed_type<void,0>();}; 
    } 

    template<std::size_t n, typename Overload, typename... Overloads> 
    struct indexed_linear_signatures<n, Overload, Overloads...>: 
     signature_generator<IndexRetval<FuncUnpack<Overload>,n>>, 
     indexed_linear_signatures<n+1, Overloads...> 
    {}; 

    template<typename T> 
    struct extract_index {}; 
    template<typename T, std::size_t i> 
    struct extract_index<indexed_type<T,i>> { 
     enum {value = i}; 
    }; 

    template<typename T> 
    using Decay = typename std::decay<T>::type; 

    template<typename indexed_overloads, typename... Args> 
    struct get_overload_index { 
     enum{ value = extract_index< Decay<decltype(std::declval<indexed_overloads>()(std::declval<Args>()...))> >::value }; 
    }; 

    template<typename Overloads, typename Args> 
    struct get_overload {}; 
    template<typename... Overloads, typename... Args> 
    struct get_overload<type_list<Overloads...>, type_list<Args...>> { 
     typedef indexed_linear_signatures<0, Overloads...> sig_index; 
     enum { index = get_overload_index< sig_index, Args... >::value }; 
     typedef FuncUnpack< NthType<index, type_list<Overloads...> > > unpacked_sig; 
    }; 

    template<typename Overloads, typename Args> 
    using GetOverloadSig = typename get_overload< Overloads, Args >::unpacked_sig; 
} 

template<typename Overloads, typename Arguments> 
struct pick_overload_signature { 
    enum{ index = overload_details::get_overload<Overloads, Arguments>::index }; 
    typedef overload_details::GetOverloadSig<Overloads, Arguments> unpacked_sig; 
}; 
#include <iostream> 
void test1() { 
    typedef type_list< void(int), void(double) > overloads; 
    typedef type_list<int> args; 
    typedef pick_overload_signature< overloads, args > result; 
    std::cout << result::index << " should be 0\n"; 
    typedef type_list<double> args2; 
    typedef pick_overload_signature< overloads, args2 > result2; 
    std::cout << result2::index << " should be 1\n"; 

// ; 
    typedef ApplyToEach< std::function, overloads >::apply<std::tuple>::type functions; 
    typedef std::tuple< std::function<void(int)>, std::function<void(double)> > functions0; 
    std::cout << std::is_same<functions, functions0>() << " should be true\n"; 

    functions funcs{ 
     [](int) { std::cout << "int!" << "\n"; }, 
     [](double) { std::cout << "double!" << "\n"; } 
    }; 
    std::get<result::index>(funcs)(0); 
} 

template< typename... Signatures > 
struct my_function { 
    typedef type_list<Signatures...> signatures; 
    typedef std::tuple< std::function<Signatures>... > func_tuple; 
    func_tuple functions; 
    template<typename... Funcs> 
    explicit my_function(Funcs&&... funcs): 
     functions(std::forward<Funcs>(funcs)...) 
    {} 

    template<typename... Args> 
    auto 
    operator()(Args&&... args) const -> 
     typename overload_details::GetOverloadSig< signatures, type_list<Args...> >::result_type 
    { 
     return std::get< 
     pick_overload_signature< signatures, type_list<Args...> >::index 
     >(functions)(std::forward<Args>(args)...); 
    } 
    // copy/assign boilerplate 
    template<typename... OtherSignatures> 
    my_function(my_function<OtherSignatures...> const& o): 
     functions(o.functions) 
    {} 
    template<typename... OtherSignatures> 
    my_function(my_function<OtherSignatures...> && o): 
     functions(std::move(o.functions)) 
    {} 
    template<typename... OtherSignatures> 
    my_function& operator=(my_function<OtherSignatures...> const& o) 
    { 
     functions = o.functions; 
     return *this; 
    } 
    template<typename... OtherSignatures> 
    my_function& operator=(my_function<OtherSignatures...> && o) { 
     functions = std::move(o.functions); 
     return *this; 
    } 
}; 

struct printer { 
    template<typename T> 
    void operator()(T const& t) { 
     std::cout << t << "\n"; 
    } 
}; 

void print(int x) { 
    std::cout << "int is " << x << "\n"; 
} 
void print(std::string s) { 
    std::cout << "string is " << s << "\n"; 
} 
void test2() { 
    my_function< void(int), void(std::string) > funcs{ 
     [](int x){ std::cout << "int is " << x << "\n";}, 
     [](std::string s){ std::cout << "string is " << s << "\n";} 
    }; 
    std::cout << "test2\n"; 
    funcs("hello"); 
    funcs(0); 
    my_function< void(int), void(std::string) > funcs2{ 
     printer(), printer() 
    }; 
    funcs2("hello"); 
    funcs2(12.7); 
    // doesn't work: 
    /* 
    my_function< void(int), void(std::string) > funcs3{ 
     print, 
     print 
    }; 
    */ 
} 
void test3() { 

} 
int main() { 
    test1(); 
    test2(); 
    test3(); 
} 

नहीं किया है, लेकिन प्रयोग करने योग्य है।

धन्यवाद सब!

+0

बहुत दिलचस्प सवाल से संतुष्ट हो जाएगा, और मैं इसे कैसे जवाब देने के लिए पर मन में कुछ विचार है, लेकिन मैं जब मैं काम करता हूं तो उसके साथ खेलना होगा। – Xeo

+0

क्या आप तर्क के आधार पर उसी फ़ंक्शन के विभिन्न ओवरलोड, या विभिन्न कार्यों को शामिल करने में सक्षम होना चाहते हैं?क्या आप एक उदाहरण दे सकते हैं कि आप अपने वर्ग टेम्पलेट का उपयोग कैसे करना चाहते हैं? –

+0

@AndyProwl मैं तर्क के आधार पर विभिन्न कार्यों के साथ (अब के लिए) संतुष्ट होगा, "जैसे-अगर" वे अधिभार संकल्प में भाग लेते हैं। फिर आप उस समारोह को कई बार "अलग-अलग कार्यों" के रूप में दोहरा सकते हैं। दोहराया जा रहा है कि दोहराना अच्छा हो सकता है (और थोड़ा मैक्रो + सही अग्रेषण टेम्पलेट लैम्ब्डा टॉमफूलरी के साथ करने योग्य), लेकिन आवश्यक नहीं है। प्रयोग करने के लिए – Yakk

उत्तर

4

मैं यह मुमकिन अपना रास्ता है यकीन है, लेकिन हो सकता है कि आपको यह https://gist.github.com/dabrahams/3779345

template<class...Fs> struct overloaded; 

template<class F1, class...Fs> 
struct overloaded<F1, Fs...> : F1, overloaded<Fs...>::type 
{ 
typedef overloaded type; 

overloaded(F1 head, Fs...tail) 
: F1(head), 
overloaded<Fs...>::type(tail...) 
{} 
using F1::operator(); 
using overloaded<Fs...>::type::operator(); 
}; 

template<class F> 
struct overloaded<F> : F 
{ 
typedef F type; 
using F::operator(); 
}; 

template<class...Fs> 
typename overloaded<Fs...>::type overload(Fs...x) 
{ return overloaded<Fs...>(x...); } 

auto f = overload(
[](int x) { return x+1; }, 
[](char const* y) { return y + 1; }, 
[](int* y) { return y; }); 
+0

क्या यह वही "प्रेषण" विकल्प करता है जैसे फ़ंक्शन ओवरलोडिंग होगा? – Yakk

+1

@Yakk: यह जरूरी है, क्योंकि यह प्रभावी रूप से वैकल्पिक हस्ताक्षर के साथ 'ऑपरेटर()' के लिए अधिभार सेट बनाता है। –

+0

आह हाँ, 'ऑपरेटर() 'लंबवत से संबंधित नहीं हैं, इसलिए दूसरे के लिए एक प्राथमिकता नहीं है। अच्छी चाल - एक 'std :: function' शैली इंटरफ़ेस के लिए अनुकूल होना आसान होना चाहिए! केवल नकारात्मकता इसलिए है क्योंकि हम सीधे कंपाइलर के अधिभार रिज़ॉल्यूशन यांत्रिकी का उपयोग करते हैं, हम इसके साथ खेलना नहीं चाहते हैं (या इसे बदलते हैं)। मैं उपरोक्त उपयोग को सही अधिभार के सूचकांक प्राप्त करने के लिए उपरोक्त संदर्भ में उपरोक्त उपयोग करके भी सही अग्रेषण खींच सकता हूं। – Yakk

1

मुझे लगता है कि आप ये लक्षण की तरह कुछ का उपयोग कर सकते ... लेकिन आप पूरी तरह से संकल्प ओवरलोडिंग मानक के रूप में बनाने चाहते हैं - आप और अधिक कोड की जरूरत http://en.cppreference.com/w/cpp/language/implicit_cast

#include <type_traits> 

template<typename T, typename D> 
struct is_constructible 
{ 
    template<typename C, typename F> 
    static auto test(C*) -> decltype(C(std::declval<F>()), std::true_type()); 
    template<typename, typename> 
    static std::false_type test(...); 
    static const bool value = std::is_class<T>::value && 
     std::is_same<std::true_type, decltype(test<T, D>(0))>::value; 
}; 

template<typename T, typename D> 
struct has_conversion_operator 
{ 
    static std::true_type test(D d); 
    template<typename C, typename F> 
    static auto test(C* c) -> decltype(test(*c)); 
    template<typename, typename> 
    static std::false_type test(...); 

    static const bool value = std::is_class<T>::value && 
     !is_constructible<T, D>::value && 
     std::is_same<std::true_type, decltype(test<T, D>(0))>::value; 
}; 

template<typename T, typename D> 
struct is_standard_convertible : 
    std::integral_constant<bool, !has_conversion_operator<T, D>::value && 
    !is_constructible<T, D>::value && 
    std::is_convertible<T, D>::value> 
{ 
}; 

template<typename T, typename D> 
struct is_user_convertible : 
    std::integral_constant<bool, has_conversion_operator<T, D>::value || 
    is_constructible<T, D>::value> 
{ 
}; 

और लागू क्या आप की तरह हैं: पहला चेक , हस्ताक्षर मानक_convertible हैं यदि यह हस्ताक्षर नहीं है कि हस्ताक्षर उपयोगकर्ता_convertible हैं।