मेरा सिस्टम कई unordered_map जैसे डेटा को परिभाषित करता है, उदा.

      std::unordered_map< int,  int>  int_map;
      std::unordered_map< int,  double>  double_map;
      std::unordered_map< string,  string>  string_map;
      std::unordered_map< string,  string>  string_map_2;
      ...
      std::unordered_map< string, vector<string> >  string_string_map;
      std::unordered_map< string, vector<string> >  string_string_map_2;

प्रत्येक मानचित्र में विभिन्न डेटा प्रकार होते हैं। इनका उपयोग एक ढांचे (सी में लिखा गया) के साथ किया जाता है जो कॉलबैक फ़ंक्शन पंजीकृत करता है। पंजीकरण फ़ंक्शन (register_undo_handler) दो तर्क लेता है: कॉलबैक फ़ंक्शन के लिए एक सूचक और कॉलबैक को पास किया जाने वाला डेटा। हमारे मामले में, डेटा एक नक्शा सूचक है।

    //register our callback function to UNDO framework
    register_undo_handler(clear_data, &int_map);
    //or :
    register_undo_handler(clear_data, &string_map);

    // this is a sketch of the callback function, which frees the map resources
    void clear_data(void *data)
    {  
       data->clear();   
    }

यदि कोई UNDO होता है, तो UNDO फ्रेमवर्क प्रत्येक परिभाषित कॉलबैक फ़ंक्शन को कॉल करेगा, उदा। clear_data, और पंजीकृत डेटा पास करें, इस तरह के पैरामीटर के रूप में void* पर डालें

   // For each registered callback function, run with registered parameter
   (*registered_callback_func)(data); 

हम एक एकल कॉलबैक फ़ंक्शन चाहते हैं जो किसी भी मानचित्र को मुक्त कर सके। कार्यान्वयन पर कोई सुझाव?

0
zheng 17 सितंबर 2020, 18:11

3 जवाब

सबसे बढ़िया उत्तर

यदि आप कोड को धोखा नहीं देना चाहते हैं तो आपके पास ऐसा करने के लिए एक टेम्पलेट हो सकता है।

#include <functional>
#include <unordered_map>
#include <iostream>
using namespace std;

void register_undo_handler(void(*callback)(void*), void* data){
    callback(data); //I'd not gonna implement all undo in C so let me do this instead
}

template<typename clearable>
void Erase(void* data){
    ((clearable*)data)->clear();
}

int main(){
   std::unordered_map<int,  int>  int_map;
   std::unordered_map<int,  double>  double_map;
    
   double_map[0] = int_map[0] = 0;
   
   //you can wrap this into a function to avoid type types yourself
   register_undo_handler(&Erase<decltype(int_map)>, (void*)&int_map);
   register_undo_handler(&Erase<decltype(double_map)>, (void*)&double_map);
   
   std::cout << int_map.size() << ' ' << double_map.size();
}
3
apple apple 17 सितंबर 2020, 20:43

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

सबसे पहले, मैं दृढ़ता से एक c++ पूर्ववत ढांचे में जाने का सुझाव दूंगा


लेकिन आप वास्तव में इसे सिंगल हैंडलर (पहला पैरामीटर) के साथ संग्रहित कर सकते हैं।

बिंदु डेटा (दूसरा पैरामीटर) के रूप में पारित एक टाइप-मिटा हुआ हैंडलर (std::function नीचे दिए गए कोड) का उपयोग करता है, और इसे रैपर हैंडलर (पहला पैरामीटर) पर आमंत्रित करता है।


ध्यान दें: आप अभी भी register_undo_handler के हस्ताक्षर नहीं जोड़ रहे हैं, इसलिए मुझे लगता है कि यह है

void register_undo_handler(void(*)(void), void*);

कोड ( Wandbox )

#include <functional>
#include <list>
#include <stack>
#include <iostream>
using namespace std;

void register_undo_handler(void(*callback)(void*), void* data){
    callback(data); //I'd not gonna implement all undo in C so let me do this instead
}

void the_callback_wrapper(void* cb){
    (*static_cast<function<void()>*>(cb))();
}

int main(){
    list<function<void()>> undos;
    //and your maps, they should have same lifetime
    
    int a=2,b=3;
    
    //do something...
    undos.emplace_back([]{std::cout << "I'd like to undo this - 1\n";}); 
    register_undo_handler(the_callback_wrapper, (void*)(&undos.back()));
    
    //undo with local variables, no problem
    
    undos.emplace_back([&]{std::cout << "I'd like to undo this - " << a << '\n';});
    register_undo_handler(the_callback_wrapper, (void*)(&undos.back()));
    
    undos.emplace_back([&]{std::cout << "I'd like to undo this - " << b << '\n';});
    register_undo_handler(the_callback_wrapper, (void*)(&undos.back()));
    
    //modify local variable, no problem
    
    undos.emplace_back([&]{std::cout << "I'd like to modify a to 100\n"; a=100;});
    register_undo_handler(the_callback_wrapper, (void*)(&undos.back()));
    std::cout << "a should be 100 : a = " << a << '\n';

    //use multiple local variable, no problem
    
    undos.emplace_back([&]{std::cout << "I'd like to modify **both a and b** to 10000\n"; a=b=100000;});
    register_undo_handler(the_callback_wrapper, (void*)(&undos.back()));
    std::cout << a << ' ' << b;
}

सिडेनोट: यदि आप ग्लोबल वैरिएबल का उपयोग स्टोर करने के लिए कर सकते हैं जिनके पूर्ववत फ़ैक्टर हैं, तो आप उनकी अनुक्रमणिका को डेटा के रूप में भी पास कर सकते हैं और कॉलबैक को यह तय करने दें कि किस फ़ैक्टर को कॉल करना है।


0
apple apple 18 सितंबर 2020, 10:36

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

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

template <class T>
void call_clear(void * ptr)
{
    if ( ptr )
        static_cast<T*>(ptr)->clear();
}

इन कॉलबैक में से किसी एक को पंजीकृत करना एक वाक्यात्मक दर्द है (जैसे register_undo_handler( call_clear<decltype(int_map)>, &int_map);), इसलिए पंजीकरण को संभालने के लिए एक रैपर लिखना वांछनीय हो सकता है। यह रैपर दोहराए जाने वाले टाइपिंग की मात्रा को कम करते हुए टेम्पलेट पैरामीटर को घटा सकता है। कंपाइलर को इस फ़ंक्शन को अनुकूलन के किसी भी स्तर पर इनलाइन करना चाहिए, जिसके परिणामस्वरूप कोई अतिरिक्त रनटाइम ओवरहेड नहीं होगा।

template <class T>
void register_undo_call_clear(T & map)
{
    register_undo_handler(call_clear<T>, &map);
}

इस बिंदु पर, कॉलबैक का पंजीकरण सरलता से किया जाता है, उदाहरण के लिए, register_undo_call_clear(int_map);

0
JaMiT 17 सितंबर 2020, 21:26