2011-10-12 20 views
32

"हुकिंग" के साथ मेरा मतलब है कि किसी फ़ंक्शन के व्यवहार को गैर-घुसपैठ से ओवरराइड करने की क्षमता। कुछ उदाहरण:सी ++ में फंक्शन हुकिंग?

  • फ़ंक्शन बॉडी से पहले और/या बाद में एक लॉग संदेश प्रिंट करें।
  • फ़ंक्शन बॉडी को एक कोशिश पकड़ने वाले शरीर में लपेटें।
  • एक समारोह
  • आदि के उपाय अवधि ...

मैं विभिन्न प्रोग्रामिंग भाषाओं और पुस्तकालयों में विभिन्न कार्यान्वयन देखा है:

  • पहलू उन्मुख प्रोग्रामिंग
  • जावास्क्रिप्ट की पहली कक्षा कार्यों
  • ओओपी सजावट पैटर्न
  • WinAPI उप (Ab)

मेरे सवालों का hooking के उद्देश्य के लिए इस्तेमाल classing

  • रूबी के method_missing
  • SWIG के %exception कीवर्ड जो एक आज़माएं/कैच ब्लॉक में सभी कार्यों रैप करने के लिए किया जा सकता है मतलब है कर रहे हैं:

    • आईएमओ यह एक अविश्वसनीय रूप से उपयोगी सुविधा है जो मुझे आश्चर्य है कि इसे कभी भी सी ++ भाषा सुविधा के रूप में क्यों लागू नहीं किया गया है। क्या ऐसे कोई कारण हैं जो इसे संभव होने से रोकते हैं?
    • सी ++ प्रोग्राम में इसे लागू करने के लिए कुछ अनुशंसित तकनीक या पुस्तकालय क्या हैं?
  • +6

    हाल ही में रेमंड चेन ने लिखा [http://blogs.msdn.com/b/oldnewthing/archive/2011/09/21/10214405.aspx) विंडोज इस – Praetorian

    +1

    को कैसे पूरा करता है तो आपका मतलब एओपी है? –

    +0

    @ आर। मार्टिन्होफर्नैंड्स हाँ, सबसे प्रासंगिक एक का उल्लेख करना भूल गए। – StackedCrooked

    उत्तर

    13

    आप एक नई पद्धति के कारण के बारे में बात कर रहे हैं इससे पहले कि नाम से जाना/एक समारोह शरीर के बाद, समारोह शरीर को बदले बिना, आप इसे this है, जो एक कस्टम shared_ptr Deleter के बाद शरीर समारोह को गति प्रदान करने का उपयोग करता है पर आधार कर सकते हैं । इसका उपयोग try/catch के लिए नहीं किया जा सकता है, क्योंकि इस तकनीक का उपयोग करके पहले और बाद में अलग-अलग कार्यों की आवश्यकता होती है।

    इसके अलावा, संस्करण नीचे shared_ptr का उपयोग करता है, लेकिन साथ सी ++ 11 आप unique_ptr उपयोग करने के लिए बनाने और एक साझा सूचक हर बार जब आप इसका इस्तेमाल को नष्ट करने की लागत के बिना एक ही प्रभाव प्राप्त करने के लिए सक्षम होना चाहिए।

    #include <iostream> 
    #include <boost/chrono/chrono.hpp> 
    #include <boost/chrono/system_clocks.hpp> 
    #include <boost/shared_ptr.hpp> 
    
    template <typename T, typename Derived> 
    class base_wrapper 
    { 
    protected: 
        typedef T wrapped_type; 
    
        Derived* self() { 
        return static_cast<Derived*>(this); 
        } 
    
        wrapped_type* p; 
    
        struct suffix_wrapper 
        { 
        Derived* d; 
        suffix_wrapper(Derived* d): d(d) {}; 
        void operator()(wrapped_type* p) 
        { 
         d->suffix(p); 
        } 
        }; 
    public: 
        explicit base_wrapper(wrapped_type* p) : p(p) {}; 
    
    
        void prefix(wrapped_type* p) { 
        // Default does nothing 
        }; 
    
        void suffix(wrapped_type* p) { 
        // Default does nothing 
        } 
    
        boost::shared_ptr<wrapped_type> operator->() 
        { 
        self()->prefix(p); 
        return boost::shared_ptr<wrapped_type>(p,suffix_wrapper(self())); 
        } 
    }; 
    
    
    
    
    template<typename T> 
    class timing_wrapper : public base_wrapper< T, timing_wrapper<T> > 
    { 
        typedef base_wrapper< T, timing_wrapper<T> > base; 
        typedef boost::chrono::time_point<boost::chrono::system_clock, boost::chrono::duration<double> > time_point; 
    
        time_point begin; 
    public: 
        timing_wrapper(T* p): base(p) {} 
    
    
        void prefix(T* p) 
        { 
        begin = boost::chrono::system_clock::now(); 
        } 
    
        void suffix(T* p) 
        { 
        time_point end = boost::chrono::system_clock::now(); 
    
        std::cout << "Time: " << (end-begin).count() << std::endl; 
        } 
    }; 
    
    template <typename T> 
    class logging_wrapper : public base_wrapper< T, logging_wrapper<T> > 
    { 
        typedef base_wrapper< T, logging_wrapper<T> > base; 
    public: 
        logging_wrapper(T* p): base(p) {} 
    
        void prefix(T* p) 
        { 
        std::cout << "entering" << std::endl; 
        } 
    
        void suffix(T* p) 
        { 
        std::cout << "exiting" << std::endl; 
        } 
    
    }; 
    
    
    template <template <typename> class wrapper, typename T> 
    wrapper<T> make_wrapper(T* p) 
    { 
        return wrapper<T>(p); 
    } 
    
    
    class X 
    { 
    public: 
        void f() const 
        { 
        sleep(1); 
        } 
    
        void g() const 
        { 
        std::cout << __PRETTY_FUNCTION__ << std::endl; 
        } 
    
    }; 
    
    
    
    int main() { 
    
        X x1; 
    
    
        make_wrapper<timing_wrapper>(&x1)->f(); 
    
        make_wrapper<logging_wrapper>(&x1)->g(); 
        return 0; 
    } 
    
    +0

    यह बहुत दिलचस्प लग रहा है। विशेष रूप से बजरने के पेपर का लिंक। – StackedCrooked

    4

    अपने पहले सवाल का जवाब करने के लिए:

    • सबसे गतिशील भाषाओं उनके method_missing निर्माणों है, पीएचपी एक magic methods (__call और __callStatic) है और अजगर __getattr__ है। मुझे लगता है कि यह सी ++ में उपलब्ध नहीं है क्योंकि यह सी ++ की टाइप की गई प्रकृति के खिलाफ है। कक्षा में इसे कार्यान्वित करने का अर्थ है कि कोई भी टाइपो इस फ़ंक्शन को कॉल करने के लिए समाप्त हो जाएगा (रनटाइम पर!), जो संकलन समय पर इन समस्याओं को पकड़ने से रोकता है। बतख टाइपिंग के साथ मिक्सिंग सी ++ एक अच्छा विचार प्रतीत नहीं होता है।
    • सी ++ जितनी जल्दी हो सके कोशिश करता है, इसलिए प्रथम श्रेणी के कार्य प्रश्न से बाहर हैं।
    • एओपी। अब यह और अधिक रोचक है, तकनीकी रूप से ऐसा कुछ भी नहीं है जो इसे C++ मानक में जोड़ा जा सके (इस तथ्य के अलावा कि जटिलता की एक और परत पहले से ही अत्यधिक जटिल मानक में जोड़ना एक अच्छा विचार नहीं हो सकता है)। वास्तव में ऐसे कंपाइलर हैं जो कोड को लहर करने में सक्षम हैं, AspectC++ उनमें से एक है। एक साल पहले या तो यह स्थिर नहीं था, लेकिन ऐसा लगता है कि तब से वे 1.0 को एक सुंदर सभ्य परीक्षण सूट के साथ रिलीज़ करने में कामयाब रहे, इसलिए यह अब नौकरी कर सकता है।

      Emulating CLOS :before, :after, and :around in C++:

    तकनीक के एक जोड़े हैं, यहाँ एक संबंधित सवाल है।

    +1

    धन्यवाद, AspectC++ जो मैं पूछता हूं उसे प्रदान करता है। इस भाषा विस्तार के लिए एक कस्टम कंपाइलर की आवश्यकता है हालांकि थोड़ा डरावना है :) – StackedCrooked

    2

    आईएमओ यह एक अविश्वसनीय रूप से उपयोगी सुविधा है, तो यह सी ++ भाषा सुविधा क्यों नहीं है? क्या ऐसे कोई कारण हैं जो इसे संभव होने से रोकते हैं?

    सी ++ भाषा सीधे ऐसा करने का कोई साधन प्रदान नहीं करती है। हालांकि, यह इस (AFAIK) के खिलाफ कोई प्रत्यक्ष बाधा उत्पन्न नहीं करता है। इस प्रकार की सुविधा देशी कोड की तुलना में एक दुभाषिया में लागू करना आसान है, क्योंकि व्याख्या सॉफ्टवेयर का एक टुकड़ा है, सीपीयू स्ट्रीमिंग मशीन निर्देश नहीं। यदि आप चाहते थे तो आप हुक के लिए समर्थन के साथ एक सी ++ दुभाषिया प्रदान कर सकते हैं।

    समस्या है लोग सी ++ का उपयोग करते हैं। बहुत से लोग सी ++ का उपयोग कर रहे हैं क्योंकि वे बेहद निष्पादन गति चाहते हैं। उस लक्ष्य को प्राप्त करने के लिए, कंपाइलर ऑपरेटिंग सिस्टम के पसंदीदा प्रारूप में देशी कोड आउटपुट करते हैं और संकलित निष्पादन योग्य फ़ाइल में जितनी अधिक सामग्री को हार्ड कोड करने का प्रयास करते हैं। अंतिम भाग अक्सर संकलन/लिंक समय पर कंप्यूटिंग पते का मतलब है। यदि आप उस समय किसी फ़ंक्शन का पता ठीक करते हैं (या इससे भी बदतर, फ़ंक्शन बॉडी इनलाइन करें) तो हुक के लिए कोई और समर्थन नहीं है।

    कहा जा रहा है कि हैंडिंग सस्ते बनाने के तरीके हैं, लेकिन इसे कंपाइलर एक्सटेंशन की आवश्यकता है और पूरी तरह से पोर्टेबल नहीं है। रेमंड चेन ने इस बारे में ब्लॉग किया कि कैसे हॉट पैचिंग विंडोज एपीआई में लागू किया गया है। वह नियमित कोड में इसके उपयोग के खिलाफ भी सिफारिश करता है।

    0

    यह एक सी ++ चीज नहीं है, लेकिन कुछ चीजों को पूरा करने के लिए, मैंने * nix सिस्टम में एलडी_PRELOAD पर्यावरण चर का उपयोग किया है। कार्रवाई में इस तकनीक का एक अच्छा उदाहरण faketime लाइब्रेरी है जो समय कार्यों में हुक करता है।

    5

    ऐसे जीसीसी के -finstrument-functions के रूप में इस तरह के रूप संकलक विशिष्ट सुविधाओं आप लाभ उठा सकते हैं, कर रहे हैं। अन्य कंपाइलरों में समान सुविधाएं होंगी। अतिरिक्त विवरण के लिए यह SO question देखें।

    एक और तरीका Bjarne Stroustrup's function wrapping तकनीक जैसे कुछ का उपयोग करना है।

    +2

    और एमएसवीसी ++ के लिए संबंधित स्विच '/ Gh' है: http://msdn.microsoft.com/en-us/library/c63a9b7h.aspx –

    0
    1. कार्यक्षमता का उपयोग न करने वाले कोड के प्रदर्शन को प्रभावित किए बिना कार्यक्षमता को कार्यान्वित करने का एक तरीका होना चाहिए। सी ++ सिद्धांत पर डिज़ाइन किया गया है कि आप केवल उपयोग की जाने वाली सुविधाओं के लिए प्रदर्शन लागत का भुगतान करते हैं। अगर प्रत्येक फंक्शन में चेक किया गया है तो यह जांचने के लिए कि क्या यह ओवरराइड किया गया है, कई सी ++ परियोजनाओं के लिए अस्वीकार्य रूप से धीमा होगा। विशेष रूप से, इसे काम करने के लिए ताकि कोई प्रदर्शन लागत न हो, जबकि अभी भी ओवरराइड और ओवरराइडिंग फ़ंक्शंस के स्वतंत्र संकलन की अनुमति देना मुश्किल होगा। यदि आप केवल ओवरराइड करने के लिए संकलित समय की अनुमति देते हैं, तो प्रदर्शन करना आसान है (लिंकर ओवरराइटिंग पते का ख्याल रख सकता है), लेकिन आप रूबी और जावास्क्रिप्ट की तुलना कर रहे हैं जो आपको इन चीजों को रनटाइम पर बदलने देता है।

    2. क्योंकि यह टाइप सिस्टम को विचलित कर देगा। किसी फ़ंक्शन के लिए निजी या गैर-आभासी होने का क्या अर्थ है यदि कोई भी अपने व्यवहार को ओवरराइड कर सकता है?

    3. पठनीयता बहुत पीड़ित होगी। किसी भी समारोह में कोड में कहीं और व्यवहार हो सकता है! एक समारोह को समझने के लिए आपको जितना अधिक संदर्भ चाहिए, उतना ही कठिन कोड आधार को समझना मुश्किल है। हुकिंग एक बग है, फीचर नहीं। कम से कम अगर आपने जो कुछ महीनों बाद लिखा था उसे पढ़ने में सक्षम होना एक आवश्यकता है।

    +0

    * हुकिंग एक बग है, फीचर नहीं * - एओपी आपके साथ असहमत है, गैर-इंस्ट्रुसेव इंस्ट्रूमेंटेशन वाले प्रोफाइलर्स जैसे टूल करें। –

    +0

    बग का अस्तित्व इस बात का खंडन नहीं करता है कि बग एक बग है;) इसलिए मुझे एओपी का अस्तित्व विशेष रूप से विश्वास नहीं है। प्रोफाइलर फ़ंक्शन के व्यवहार को बदलने वाले मनमानी कोड को सम्मिलित नहीं करते हैं। अगर कोई एओपी प्रकार प्रणाली के साथ आया जो साबित कर सकता है कि ओवरराइडिंग साइड इफेक्ट्स नहीं बनाएगा जो कोड के उपयोगकर्ताओं द्वारा धारणाओं का उल्लंघन करती है, तो मैं इसके साथ बोर्ड पर जा सकता हूं। –

    +0

    मैं (1) और (2) से सहमत हूं, लेकिन फिर (3), मुझे नहीं लगता कि हुकिंग खुले नेमस्पेस और ओवरराइडिंग की अनुमति देने से बग का कोई और है। उनके पास दोनों कार्य-दर-दूरी की गुणवत्ता है। बेशक आप तर्क दे सकते हैं कि वे दोनों बग हैं :) –

    1

    कम से कम C++ रूपरेखा है कि मैं का उपयोग शुद्ध आभासी कक्षाओं

    class RunManager; 
    class PhysicsManager; 
    // ... 
    

    जिनमें से प्रत्येक कार्यों

    void PreRunAction(); 
    void RunStartAction() 
    void RunStopAction(); 
    void PostRunAction(); 
    

    जो NOPs का एक सेट परिभाषित का एक सेट प्रदान करता है, लेकिन जिस पर उपयोगकर्ता माता-पिता वर्ग से प्राप्त होने पर ओवरराइड कर सकता है।

    सशर्त संकलन के साथ संयोजन करें (हाँ, मुझे "युक!") और आप जो चाहते हैं उसे प्राप्त कर सकते हैं।

    +0

    क्या आप उस ढांचे का नाम प्रदान कर सकते हैं? –

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