2013-02-20 28 views
8

Birden fazla aşırı yükü depolamayı başarabilen bir std::function benzeri nesne yapmak istiyorum."El ile" imzası aşırı yükleme çözünürlüğü

Sözdizimi şöyle: my_function< int(double, int), double(double, double), char(int, int) >.

Ya da daha açık bir şekilde:

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

Benim sorum: nasıl pick_overload_signatures yazmalıyım?

Benim eğim argümanlar kümesi verilen göre işlev imzalar üzerinde kısmi bir düzeni yazma olacağını, ardından, fonksiyon imza tipi listesini sıralamak: Burada

Bunun üzerine yaptığım iştir En iyi (muhtemelen bir derleme zamanı ile en iyi olanın benzersiz olduğunu iddia et) yakala. Bunu yapmak için, işlev imzaları üzerinde katı bir kısmi sıraya sahip olmalıyım. (

13.3.3.1, geçerli bir dönüşüm olup olmadığını nasıl belirleyeceğimi söyler. . Derleyiciyi, benim için bir dönüşüm yapmak için kullanarak aldatabilirim ve SFINAE’ı, belirli bir argüman için iletilmiş olup olmadığını ve "aşırı yüklenmelerden" birinin imzası olup olmadığını algılamak için kullanabilirim.

13.3.3.2, bu dönüşümlerin nasıl sipariş edileceğini anlatır. Burada bir dönüşüm sırasının kullanıcı tanımlı mı yoksa standart bir sıra mı olduğunu tespit etmeliyim. İkisini birbirinden nasıl ayırt edeceğimi bilmiyorum.

Kullanıcı tanımlı dönüşüm dizilerinin varlığını algılamak için özellik sınıfını kullanabilirim. &S::operator D() ve &D::D(S const&) ve &D::D(S) ve &D::D(S&&) veya bunun gibi bir şey olup olmadığını kontrol edin.

has_user_defined_conversion<S,D>::value, has_standard_conversion<S,D>::value, vb?

bu yaklaşım iş, birileri zaten yapacağını yaptı, yoksa birileri bu parçaları yaptı?

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

Result of Answers

yapılır, ama kullanışlı değil mi.

Herkese teşekkürler!

+0

Çok ilginç bir soru memnun olacaktır olabilir ve bunu yanıtlamak için nasıl akılda bazı fikirler var ama işe olsun 'bununla uğraşmak gerekecek. – Xeo

+0

Eğer argümanları bağlı çağırılacak aynı işleve veya farklı fonksiyonları farklı aşırı yükleri saklayabilecek istiyor musunuz?Sınıf şablonunuzu nasıl kullanacağınıza dair bir örnek verebilir misiniz? –

+0

@AndyProwl Aşırı yük çözünürlüğüne katılırken, "as-if" argümanlarına bağlı olarak çağrılacak farklı işlevlerle (şimdilik) tatmin olurdum. Daha sonra bu fonksiyonu "farklı işlevler" olarak tekrarlayabilirsiniz. Bu tekrarı ortadan kaldırmak iyi olabilir (ve biraz makro + mükemmel yönlendirme şablonu lambda tomfoolery ile yapılabilir), ancak gerekli değildir. – Yakk

cevap

4

ben senin yolun yapılabilir eminim ama bu bir 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

Bu, işlev aşırı yüklemesiyle aynı "gönderim" seçeneğini yapar mı? – Yakk

+1

@Yakk: Bu, zorunlu olarak, alternatif imzalarla "operator()" için bir aşırı yüklenme kümesi oluşturduğu için yapar. –

+0

Ah evet, operatör() 'dikey olarak ilişkili değildir, bu nedenle diğerinin üzerinde bir tercih yoktur. Güzel bir hile - bir 'std :: function' tarzı arayüzü için bunu kolay olmalı! Tek dezavantajı, derleyicinin aşırı yük çözme mekaniğini doğrudan kullandığımızdan, onunla oynamayız (ya da değiştiririz). Doğru aşırı yük endeksini elde etmek için, yukarıdakileri, değerlendirilmemiş bir bağlamda kullanarak da mükemmel iletmeyi kaldırabilirim. – Yakk

1

Sana bu özelliklerin gibi bir şey kullanabilirsiniz düşünüyorum ... Ama standardında olduğu gibi tamamen çözünürlüğü aşırı yapmak istiyorsanız - Hoşunuza istediğini uygulamak 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> 
{ 
}; 

ve daha fazla kod gerekir: ilk kontrolü o imzasını kontrol değilse imzalar standard_convertible olduğunu user_convertible bulunmaktadır.

İlgili konular