2017-10-16 63 views
7

ilk N args geçmek:nasıl böyle bir işlevi var bir C++ işlevi

void loadData(std::function<void (std::string, std::string, std::string)> callback) 
{ 
    // data loading stuff 
    callback(body, subject, header); 
} 

Sorun mutlaka benim geri arama işlevinde subject ve header kullanmaya gerek olmam. Şimdi ben bu şekilde hallediyorum:

loadData([](std::string body, std::string, std::string){ 
    std::cout << body; 
}) 

Ben

loadData([](std::string body){ 
    std::cout << body; 
}) 

ile değiştirin ve otomatik kabul edebilecek bunun yanı fonksiyonu gibi birçok argüman geri çağırma geçmek istiyorum. 3 olası argüman sayımı için loadData fonksiyonunu manuel olarak aşırı yüklemek istemiyorum. Ayrıca, arama sitemde daha karmaşık bir lambda sözdizimi kullanmak istemiyorum, çünkü kütüphanem başkalarının kullanması için net olmalı. C++ STL ve Boost kullanarak bu mümkün mü?

+1

Neden sadece yüklenmeyin olabilir umut? ya da sadece diğer iki parametreyi görmezden geliyorum – Mgetz

+1

@Mgetz Çünkü 3 taneden fazla hataya sahip olduğumda bu sorunu bir kez daha karşılayabiliyorum ve daha fazla aşırı yüküm var, daha çirkin ve daha az okunabilir kodum olur. –

+0

Sanırım argümanları geçmek için bir 'struct' içine koymak istememenizin bir nedeni var mı? –

cevap

1

Ben functor parametrelerin doğru sayıda geçen bir sarmalayıcı yapmak için önermektedir diğer cevaplar, biri tarafından ilham geldi:

template<typename L> 
struct OptionalWrapper { 
    OptionalWrapper(L l) : lambda{std::move(l)} {} 

    void operator()(std::string body, std::string subject, std::string header) const { 
     call(lambda, body, subject, header); 
    } 

private: 
    template<typename T> 
    auto call(T& l, std::string body, std::string subject, std::string header) const 
     -> decltype(l(body, subject, header)) 
    { 
     return l(body, subject, header); 
    } 

    template<typename T> 
    auto call(T& l, std::string body, std::string subject, std::string) const 
     -> decltype(l(body, subject)) 
    { 
     return l(body, subject); 
    } 

    template<typename T> 
    auto call(T& l, std::string body, std::string, std::string) const 
     -> decltype(l(body)) 
    { 
     return l(body); 
    } 

    L lambda; 
}; 

template<typename L> 
auto makeOptionalWrapper(L l) { return OptionalWrapper<L>{std::move(l)}; } 

Sonra o şekilde sargıyı kullanabilir. Bu çözümü çok güzel buluyorum ve argüman sayısının sabit kodlama yapılmadığı genel bir şablon yazmayı denediğimi düşündüm. ve parametrelerin herhangi türleri ile, "isteğe bağlı" parametrelerin herhangi bir sayı ile, herhangi bir işlev için çalışması gerekir

#include <string> 
#include <functional> 
#include <iostream> 

struct WrapperHelp 
{ 
    template 
     < typename L 
     , typename Tuple 
     , std::size_t... Is 
     , typename... Ts 
     > 
    static auto apply(L&& l, Tuple t, std::index_sequence<Is...>, Ts&&... ts) 
     -> decltype(l(std::get<Is>(t)...)) 
    { 
     return l(std::get<Is>(t)...); 
    } 

    template 
     < typename L 
     , typename Tuple 
     , std::size_t... Is 
     , typename T1 
     , typename... Ts 
     > 
    static auto apply(L&& l, Tuple t, std::index_sequence<Is...>, T1&& t1, Ts&&... ts) 
     -> decltype(WrapperHelp::apply(std::forward<L>(l), std::forward_as_tuple(std::get<Is>(t)..., t1), std::make_index_sequence<sizeof...(Is) +1 >(), ts...)) 
    { 
     return WrapperHelp::apply(std::forward<L>(l), std::forward_as_tuple(std::get<Is>(t)..., t1), std::make_index_sequence<sizeof...(Is) + 1>(), ts...); 
    } 
}; 

template<typename L> 
struct OptionalWrapper { 
    public: 
     OptionalWrapper(L l) : lambda{std::move(l)} {} 

     template<typename... Ts> 
     void operator()(Ts&&... ts) const 
     { 
     WrapperHelp::apply(lambda, std::tuple<>(), std::index_sequence<>(), std::forward<Ts>(ts)...); 
     } 

    private: 
     L lambda; 
}; 

template<typename L> 
auto makeOptionalWrapper(L l) { return OptionalWrapper<L>{std::move(l)}; } 

template<class F> 
void loadData(OptionalWrapper<F>&& callback) 
{ 
    std::string body = "body"; 
    std::string subject = "subject"; 
    std::string header = "header"; 
    double lol = 2.0; 
    callback(body, subject, header, lol); 
} 

template<typename L> 
void loadData(L callback) 
{ 
    loadData(makeOptionalWrapper(std::move(callback))); 
} 

int main() { 
    //apply(std::tuple<double>(2), std::tuple<double>(2)); 
    loadData([](auto&& body) { 
     std::cout << body << std::endl; 
    }); 
    loadData([](auto&& body, auto&& subject) { 
     std::cout << body << " " << subject << std::endl; 
    }); 
    loadData([](auto&& body, auto&& subject, auto&& header) { 
     std::cout << body << " " << subject << " " << header << std::endl; 
    }); 
    loadData([](auto&& body, auto&& subject, auto&& header, auto&& lol) { 
     std::cout << body << " " << subject << " " << header << " " << lol << std::endl; 
    }); 
    return 0; 
} 

Bu: İşte ile geldi budur. Bu güzel kodu değil, ama bir fikir açıktır ve bazı kullanım :)

Live example

+0

Evet, tam da aradığım şey bu. Çok teşekkürler. –

+1

@DenisSheremet Sorun değil, yardımcı olmaktan mutluluk duyuyorum :) – Banan

6

... kullanarak aşağıdaki bağımsız değişkenleri göz ardı etmek nedir? Masalcı tarafından belirttiği gibi

loadData([](std::string body, ...){ 
    std::cout << body; 
}) 


(teşekkürler!) Üç nokta kullanımı olmayan önemsiz türleri (ayrıntılı bilgi için [expr.call]p9 bakınız) için desteklenmeyen olabilir.

Bu sorunu önlemek için, C++ 14'ü kullanabiliyorsanız, gereksiz kopyalardan kaçınmak için auto ... (daha iyi auto && ... kullanabilirsiniz. Teşekkürler Yakk).

loadData([](std::string body, auto && ...){ 
    std::cout << body; 
}) 
+3

En önemsiz türlerden başka bir şeyi geçmek yalnızca koşullu olarak desteklenir. – StoryTeller

+0

@StoryTeller - Anladığınızdan emin değilsiniz; demek istediniz ki bu sadece ... ... önemsiz türlerini destekliyor mu? – max66

+0

http://eel.is/c++draft/expr.call#9 – StoryTeller

1

Lambda çevresinde bir sarıcı yapabilirsiniz.

void loadData(std::function<void (std::string, std::string, std::string)> callback) 
{ 
    callback(body, subject, header); 
} 

template<typename L> 
void loadData(L callback) 
{ 
    loadData({makeOptionalWrapper(std::move(callback))}); 
}