2017-10-16 9 views
7

मैं इस तरह एक समारोह मिल गया है करने के लिए पहली एन आर्ग पारित करने के लिए:कैसे एक सी ++ समारोह

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

समस्या मैं जरूरी कर रहा हूँ मेरी कॉलबैक फ़ंक्शन में subject और header उपयोग करने की आवश्यकता नहीं है। अब मैं इसे से निपटने कर रहा हूँ इस तरह से:

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

मैं

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

से बदलने के लिए और स्वचालित रूप से स्वीकार करने में सक्षम यह के रूप में कार्य के रूप में कई तर्क कालबैक में पारित करना चाहते हैं। मैं सभी 3 संभावित तर्क गणनाओं के लिए loadData फ़ंक्शन मैन्युअल रूप से अधिभारित नहीं करना चाहता हूं। मैं कॉलिंग साइट पर किसी भी जटिल लैम्ब्डा वाक्यविन्यास का उपयोग नहीं करना चाहता क्योंकि मेरी लाइब्रेरी दूसरों के उपयोग के लिए स्पष्ट होना चाहिए। क्या यह सी ++ एसटीएल और बूस्ट का उपयोग कर संभव है?

+1

सिर्फ क्यों ओवरलोड नहीं की जा सकता है? या बस अन्य दो पैरामीटर – Mgetz

+1

@Mgetz को अनदेखा करें क्योंकि मैं एक बार फिर से इस मुद्दे का सामना कर सकता हूं जब मेरे पास 3 से अधिक तर्क होंगे, और मेरे पास अधिक अधिभार है, यूग्लियर और कम पठनीय मेरा कोड बन जाता है। –

+0

मुझे लगता है कि कुछ कारण हैं कि आप तर्क को 'स्ट्रक्चर' में पास नहीं करना चाहते हैं? –

उत्तर

1

मैं अन्य उत्तर है, जो एक आवरण है कि functor के लिए मानकों की सही संख्या गुजरता बनाने के लिए प्रस्ताव में से एक ने प्रेरित हुआ:

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

फिर, उस तरह अपने आवरण का उपयोग करें। मुझे यह समाधान बहुत अच्छा लगता है, और सोचा कि मैं एक सामान्य टेम्पलेटेड रैपर बनाने की कोशिश करूंगा, जहां तर्कों की संख्या हार्डकोड नहीं की जाती है।

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

यह "वैकल्पिक" मानकों के किसी भी संख्या के साथ, किसी भी कार्य के लिए काम करना चाहिए, और पैरामीटर के किसी भी प्रकार के साथ: यहाँ है कि मैं क्या के साथ आया है। यह सबसे सुंदर कोड नहीं है, लेकिन मुझे उम्मीद है कि विचार स्पष्ट है और कुछ का उपयोग :)

Live example

+0

हां, यह वही है जो मैंने देखा है। बहुत बहुत धन्यवाद। –

+1

@ डेनिसशेरम कोई समस्या नहीं, सहायता के लिए खुश :) – Banan

6

... का उपयोग करके निम्नलिखित तर्कों को अनदेखा करने के बारे में क्या?

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


कथाकार द्वारा बताया के रूप में (धन्यवाद!) अंडाकार के उपयोग गैर तुच्छ प्रकार (अधिक जानकारी के लिए [expr.call]p9 देखें) का समर्थन नहीं किया जा सकता है।

इस समस्या से बचने के लिए, यदि आप सी ++ 14 का उपयोग कर सकते हैं, तो आप अनावश्यक प्रतियों से बचने के लिए auto ... (बेहतर auto && ... का उपयोग कर सकते हैं; धन्यवाद याक)।

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

कुछ भी गुजरने के अलावा कुछ मामूली प्रकार केवल सशर्त रूप से समर्थित है। – StoryTeller

+0

@StoryTeller - समझने के लिए निश्चित नहीं है; क्या आपका मतलब है कि '...' केवल तुच्छ प्रकारों का समर्थन करने की गारंटी है? – max66

+0

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

1

आप लैम्ब्डा के चारों ओर एक रैपर बना सकते हैं।

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))}); 
} 
संबंधित मुद्दे