2017-07-11 18 views
6

मैंने पिछले कुछ दिनों में सी ++ में फ़ंक्शन पॉइंटर्स के लिए एक सामान्यीकृत रैपर बनाने की कोशिश की है और मैंने लगभग हर एक समस्या बार को हल करने में कामयाब रहा है। इसके साथ मेरा मुख्य लक्ष्य किसी ऑब्जेक्ट को आंतरिक रूप से संग्रहीत फ़ंक्शन पॉइंटर के साथ फ़ंक्शन के रूप में कॉल करने में सक्षम होना था। यदि सूचक कहीं कहीं इंगित कर रहा था तो यह इसे सामान्य के रूप में बुलाएगा, जबकि एक शून्य सूचक केवल फ़ंक्शन को कॉल नहीं करेगा और ऐसा कुछ भी नहीं होगा जैसा कि कुछ भी नहीं हुआ। मैं मुख्य रूप से कॉलबैक फ़ंक्शन प्रयोजनों के लिए इसका उपयोग करना चाहता हूं, जहां फ़ंक्शन को कॉल किया गया था या नहीं, और शायद कोई कार्रवाई करने के लिए मैं चाहता हूं कि मुझे परवाह नहीं है। यह निम्न के साथ लगभग पूरी तरह से काम करता है:सी के + 'टेम्पलेट मान के रूप में सी के' ... 'पैरामीटर पैक का उपयोग?

template<typename T> 
class Action; 

template<typename TReturn, typename ... TArgs> 
class Action<TReturn(TArgs...)> { 
public: 
    //! Define a type for the Action object 
    typedef TReturn(*signature)(TArgs...); 

    //! Constructors 
    inline Action(const signature& pFunc = nullptr) : mPointer(pFunc) {} 
    inline Action(const Action& pCopy) : mPointer(pCopy.mPointer) {} 

    //! Operator Call 
    inline bool operator() (TReturn& pReturn, TArgs ... pArgs) const { if (!mPointer) return false; pReturn = mPointer(pArgs...); return true; } 

    //! Operators 
    inline Action& operator=(const Action& pCopy) { mPointer = pCopy.mPointer; return *this; } 
    inline Action& operator=(const signature& pFunc) { mPointer = pFunc; return *this; } 
    inline operator bool() const { return (mPointer != nullptr); } 

private: 
    //! Store a pointer to the callback function 
    signature mPointer; 
}; 

template<typename ... TArgs> 
class Action<void(TArgs...)> { 
public: 
    //! Define a type for the Action object 
    typedef void(*signature)(TArgs...); 

    //! Constructors 
    inline Action(const signature& pFunc = nullptr) : mPointer(pFunc) {} 
    inline Action(const Action& pCopy) : mPointer(pCopy.mPointer) {} 

    //! Operator Call 
    inline bool operator() (TArgs ... pArgs) const { if (!mPointer) return false; mPointer(pArgs...); return true; } 

    //! Operators 
    inline Action& operator=(const Action& pCopy) { mPointer = pCopy.mPointer; return *this; } 
    inline Action& operator=(const signature& pFunc) { mPointer = pFunc; return *this; } 
    inline operator bool() const { return (mPointer != nullptr); } 

private: 
    //! Store a pointer to the callback function 
    signature mPointer; 
}; 

हालांकि, मैं स्थिति है कि सबसे अधिक संभावना होगा उपयोग इस आवरण डिबग जानकारी या स्वरूपित पाठ का उत्पादन होता है की तरह लग रहा है। यह उपयोगकर्ता परिभाषित कार्यों या printf जैसे अंतर्निहित कार्यों के माध्यम से हो सकता है। printf के हस्ताक्षर के साथ मिलान करने के लिए एक कार्य की तरह बनाया जा जाएगा:

Action<int(const char*, ...)> callback = printf; 

और यह एक ही तरीका है कि किसी भी अन्य कार्रवाई कैसा व्यवहार करेंगे काम करने में सक्षम हो जाएगा। मुझे जो समस्या मिल रही है वह है '...' टेम्पलेट हस्ताक्षर को किसी भी विशेषज्ञता के साथ संरेखित करने के लिए मजबूर नहीं करेगा, बल्कि पहले प्रोटोटाइप के साथ पहले जा रहा है।

मैं पूरी तरह से समझ सकता हूं कि यह क्यों काम नहीं करता है और क्यों संकलक आवश्यक वर्ग की पीढ़ी को संभालने में सक्षम नहीं होगा, लेकिन मैं उम्मीद कर रहा था कि यहां कोई व्यक्ति इसे या कुछ समान प्राप्त करने के लिए किसी भी चुस्त तरीके से जानता होगा। किसी भी मदद की बहुत सराहना की जाएगी, धन्यवाद :)

+7

'Std :: function' का उपयोग क्यों न करें? – StoryTeller

+0

'एक्शन' का उद्देश्य क्या है? आपके पास क्या आवश्यकता है कि आप ['std :: function'] (http://en.cppreference.com/w/cpp/utility/functional/function) का उपयोग नहीं कर सकते? 'एक्शन' को हल करने में क्या समस्या है कि 'std :: function' नहीं है? –

+1

https://stackoverflow.com/questions/18370396/why-cant-stdfunction-bind-to-c-style-variadic- कार्यों ने कुछ टिप्पणी दी, इस बारे में लगभग कैसे प्राप्त किया जाए। मुझे लगता है कि प्रत्येक वैरिएड फ़ंक्शन के लिए, आपको मदद करने के लिए एक ठोस वर्ग की आवश्यकता होगी, और उम्मीद है कि आप जिस फ़ंक्शन को कॉल कर सकते हैं उसका एक v * संस्करण था। – mksteve

उत्तर

1

निम्न उदाहरण सभी प्रकार के समारोह के लिए काम करता है, और कोई कब्जा करने के साथ lambdas:

#include <utility> 
#include <cstdio> 
#include <cmath> 

template<typename Fn> 
class Action { 
    Fn* function_ptr;   
public: 
    Action() noexcept : function_ptr(nullptr) {}  
    Action(std::nullptr_t) noexcept : function_ptr(nullptr) {}  
    Action(const Action& other) : function_ptr(other.function_ptr) {}  
    Action(Fn f) : function_ptr(f) {} 

    Action& operator=(const Action& other) { 
     return (function_ptr = other.function_ptr, *this); 
    }    
    Action& operator=(std::nullptr_t) { 
     return (function_ptr = nullptr, *this); 
    }  
    Action& operator=(Fn f) { 
     return (function_ptr = f, *this); 
    } 

    template<typename... Params> 
    auto operator()(Params&&... params) { 
     return function_ptr(std::forward<Params>(params)...); 
    } 
}; 

Live Demo

अपने प्रश्न उल्लेख के तहत टिप्पणी के रूप में , std::function का उपयोग फ़ंक्शन पॉइंटर्स के लिए एक रैपर लिखने से बेहतर है। std::function के साथ एकमात्र समस्या यह है कि इसका उपयोग फ़ंक्शन हस्ताक्षरों के साथ नहीं किया जा सकता है जिसमें इलिप्सिस होता है। यदि आप हस्ताक्षर निर्दिष्ट करते हैं तो आप स्टोर कर सकते हैं उदा। printf इस प्रकार है:

std::function<int(const char*, int, double, double)> fn = printf; 

आप सी ++ 17 या बूस्ट का उपयोग करते हैं, तो आप लागू कर सकते हैं, जिसके आप स्वामी printf की तरह समारोह, इस प्रकार std::any या Boost.Any जो std::function को सौंपा जा सकता का उपयोग कर:

#include <iostream> 
#include <string> 
#include <vector> 
#include <any> 
#include <functional> 

using namespace std; 

void any_printf(string&& format, vector<any>&& args) { 
    int arg_index = 0; enum { NORMAL, CONVERT } mode; 

    for(auto& chr : format) { 
     if(mode == CONVERT) { 
      switch(chr) { 
      case 'd': cout << any_cast<int>(args[arg_index++]); break; 
      case 'f': cout << any_cast<float>(args[arg_index++]); break; 
      case 's': cout << any_cast<string>(args[arg_index++]); break; 
      /* ... */ 
      default: cout << chr; 
      }; 
      mode = NORMAL; 
     } 
     else { 
      chr == '%' ? (mode = CONVERT, 0) : (cout << chr, 0); 
     } 
    } 
} 

int main() { 
    using namespace string_literals; 
    function<void(string&&, vector<any>&&)> f_ptr { any_printf }; 

    f_ptr("Cuboid: %smm x %dmm x %fmm.\n", { any("3"s), any(4), any(6.67f) }); 
    return 0; 
} 
+0

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

+1

@MitchellCroft, जब आप कोई फ़ंक्शन लिखते हैं जो कॉलबैक का आह्वान करेगा, तो आपको इसके हस्ताक्षर को परिभाषित करना होगा। यदि आप चाहते हैं कि उस कॉलबैक को डेटा की परिवर्तनीय संख्या और परिवर्तनीय प्रकारों के साथ ** (जैसे 'printf' को बुलाया जा सकता है) के साथ, आप उदाहरण का उपयोग कर सकते हैं ['std :: any'] का एक 'std :: vector' (http://en.cppreference.com/w/cpp/utility/any) प्रकार। यह एक सी ++ 17 फीचर है, लेकिन [बूस्ट.एनी] (http://www.boost.org/doc/libs/1_64_0/doc/html/any.html) को प्रतिस्थापन के रूप में भी इस्तेमाल किया जा सकता है। – Akira

+0

@MitchellCroft, मैंने पिछली टिप्पणी में उल्लिखित 'std :: any' दृष्टिकोण के साथ अपना जवाब संपादित किया। – Akira

संबंधित मुद्दे

 संबंधित मुद्दे