2013-02-20 36 views
8

Tôi muốn tạo một đối tượng giống như đối tượng có thể xử lý lưu trữ nhiều hơn một quá tải.Độ phân giải quá tải chữ ký "Thủ công"

Loại cú pháp như sau: my_function< int(double, int), double(double, double), char(int, int) >.

Hoặc, một cách rõ ràng hơn:

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)...); 
    } 
}; 

Câu hỏi của tôi: làm thế nào tôi nên viết pick_overload_signatures?

Đây là công việc tôi đã thực hiện trên nó:

nghiêng của tôi sẽ được để viết một trật tự phần trên chữ ký chức năng liên quan đến một tập hợp các đối số, sau đó sắp xếp các loại danh sách các chữ ký chức năng, sau đó lấy tốt nhất (có thể một thời gian biên dịch khẳng định rằng cái tốt nhất là duy nhất). Để kéo nó ra, tôi phải có một trật tự một phần rắn (đối với một tập hợp các đối số được truyền vào) trên các chữ ký chức năng ...

13.3.3.1 cho tôi biết cách xác định xem có chuyển đổi hợp lệ hay không . Tôi có thể gian lận cho điều này bằng cách sử dụng trình biên dịch để thực hiện chuyển đổi cho tôi và sử dụng SFINAE để phát hiện xem nó có xảy ra đối với một đối số đã cho hay không và chữ ký của một trong các "quá tải".

13.3.3.2 cho tôi biết cách đặt hàng các chuyển đổi này. Ở đây tôi phải phát hiện xem chuỗi chuyển đổi là do người dùng xác định hay một chuỗi chuẩn. Tôi không chắc chắn làm thế nào để phân biệt giữa hai người.

Có lẽ tôi có thể sử dụng các đặc điểm để phát hiện sự tồn tại của chuỗi chuyển đổi do người dùng xác định. Kiểm tra sự tồn tại của &S::operator D()&D::D(S const&)&D::D(S)&D::D(S&&) hoặc điều gì đó tương tự.

has_user_defined_conversion<S,D>::value, has_standard_conversion<S,D>::value, v.v ...?

Cách tiếp cận này có hiệu quả không, có ai đó đã thực hiện hoặc có ai đó đã thực hiện các phần này không?

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(); 
} 

không được thực hiện, nhưng là có thể sử dụng.

Cảm ơn tất cả!

+0

Câu hỏi rất thú vị này, và tôi có một số ý tưởng trong tâm trí về cách trả lời nó, nhưng tôi sẽ phải chơi đùa với điều đó khi tôi đi làm. – Xeo

+0

Bạn có muốn lưu trữ các tình trạng quá tải khác nhau của cùng một chức năng hoặc các chức năng khác nhau được gọi tùy thuộc vào các đối số không?Bạn có thể đưa ra một ví dụ về cách bạn dự định sử dụng mẫu lớp của bạn không? –

+0

@AndyProwl Tôi sẽ hài lòng (cho bây giờ) với các chức năng khác nhau được gọi tùy thuộc vào các đối số, "as-if" chúng khi tham gia vào quá trình phân giải quá tải. Sau đó bạn có thể lặp lại chức năng đó nhiều lần như "các chức năng khác nhau". Loại bỏ lặp lại đó có thể là tốt (và doable với một chút vĩ mô + hoàn hảo chuyển tiếp mẫu lambda tomfoolery), nhưng không cần thiết. – Yakk

Trả lời

4

tôi chắc chắn rằng nó là doable theo cách của bạn, nhưng có thể bạn sẽ hài lòng với một 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

Điều đó có làm cùng một lựa chọn "công văn" như quá tải chức năng không? – Yakk

+1

@Yakk: Nó nhất thiết phải làm, bởi vì nó có hiệu quả tạo ra một tập quá tải cho 'operator()' với các chữ ký thay thế. –

+0

À vâng, toán tử '()' không liên quan theo chiều dọc, do đó, không có sự ưu tiên cho cái kia. Nice trick - thích ứng với giao diện kiểu 'std :: function' sẽ dễ dàng! Nhược điểm duy nhất là bởi vì chúng tôi sử dụng cơ chế độ phân giải quá tải của trình biên dịch trực tiếp, chúng tôi không thể chơi với nó (hoặc thay đổi nó). Tôi cũng có thể rút ra chuyển tiếp hoàn hảo bằng cách sử dụng ở trên trong một bối cảnh không được đánh giá để có được chỉ số của tình trạng quá tải phải. – Yakk

1

Tôi nghĩ rằng bạn có thể sử dụng một cái gì đó giống như những đặc điểm ... Nhưng nếu bạn muốn làm cho quá tải độ phân giải đầy đủ như trong tiêu chuẩn - bạn cần mã hơn 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> 
{ 
}; 

và thực hiện những gì bạn muốn như: kiểm tra đầu tiên , chữ ký đó là standard_convertible nếu không kiểm tra chữ ký đó là user_convertible.

Các vấn đề liên quan