कहें कि आपके पास कक्षा foo
है जो किसी प्रकार की कॉल करने योग्य वस्तुओं का संग्रह लपेटती है। foo
में एक सदस्य फ़ंक्शन run()
है जो संग्रह पर पुन: प्रयास करता है और प्रत्येक फ़ंक्शन ऑब्जेक्ट को कॉल करता है। foo
में एक सदस्य remove(...)
है जो संग्रह से कॉल करने योग्य ऑब्जेक्ट को हटा देगा।सी ++ में ऐसी स्थिति के खिलाफ सुरक्षा करने के लिए एक बेवकूफ तरीका है जिसमें क्रियाओं का संग्रह चलाने से संग्रह को परिवर्तित किया जा सकता है?
वहाँ एक मुहावरेदार, आरए II शैली गार्ड आप foo.run()
में डाल दिया और कर सकता है foo.remove(...)
ऐसी है कि हटा देगा कि foo.run()
के लिए एक कॉल के द्वारा संचालित किया गया गार्ड नाशक आग जब तक स्थगित कर दिया जाएगा? क्या यह मानक पुस्तकालय में कुछ के साथ किया जा सकता है? क्या इस पैटर्न का नाम है?
मेरा वर्तमान कोड सुरुचिपूर्ण लगता है, इसलिए मैं सबसे अच्छा अभ्यास प्रकार समाधान ढूंढ रहा हूं।
नोट: यह समरूपता के बारे में नहीं है। एक गैर थ्रेड-सुरक्षित समाधान ठीक है। मुद्दा पुन: प्रवेश और आत्म-संदर्भ के साथ है।
यहां समस्या का एक उदाहरण है, सुरुचिपूर्ण "डिफर हटाने" गार्ड को सैन्स करता है।
class ActionPlayer
{
private:
std::vector<std::pair<int, std::function<void()>>> actions_;
public:
void addAction(int id, const std::function<void()>& action)
{
actions_.push_back({ id, action });
}
void removeAction(int id)
{
actions_.erase(
std::remove_if(
actions_.begin(),
actions_.end(),
[id](auto& p) { return p.first == id; }
),
actions_.end()
);
}
void run()
{
for (auto& item : actions_) {
item.second();
}
}
};
तो कहीं:
...
ActionPlayer player;
player.addAction(1, []() {
std::cout << "Hello there" << std::endl;
});
player.addAction(42, [&player]() {
std::cout << "foobar" << std::endl;
player.removeAction(1);
});
player.run(); // boom
संपादित करें ... ठीक है कि यह कैसे मैं एक आरए II ताला वस्तु के माध्यम से यह करने के लिए देख सकते हैं। निम्नांकित कार्रवाई को फेंकने और फिर से प्रवेश करने के लिए कॉल को चलाने के लिए चलाना चाहिए, यह मानते हुए कि रिकर्सन अंततः समाप्त हो जाता है (यदि उपयोगकर्ता की गलती नहीं है)। मैंने कैश किए गए std :: फ़ंक्शंस का उपयोग किया क्योंकि इस कोड के वास्तविक संस्करण में addAction और removeAction के समतुल्य टेम्पलेट फ़ंक्शंस हैं जिन्हें केवल वेनिला समरूप टाइप किए गए कंटेनर में संग्रहीत नहीं किया जा सकता है।
class ActionPlayer
{
private:
std::vector<std::pair<int, std::function<void()>>> actions_;
int run_lock_count_;
std::vector<std::function<void()>> deferred_ops_;
class RunLock
{
private:
ActionPlayer* parent_;
public:
RunLock(ActionPlayer* parent) : parent_(parent) { (parent_->run_lock_count_)++; }
~RunLock()
{
if (--parent_->run_lock_count_ == 0) {
while (!parent_->deferred_ops_.empty()) {
auto do_deferred_op = parent_->deferred_ops_.back();
parent_->deferred_ops_.pop_back();
do_deferred_op();
}
}
}
};
bool isFiring() const
{
return run_lock_count_ > 0;
}
public:
ActionPlayer() : run_lock_count_(0)
{
}
void addAction(int id, const std::function<void()>& action)
{
if (!isFiring()) {
actions_.push_back({ id, action });
} else {
deferred_ops_.push_back(
[&]() {
addAction(id, action);
}
);
}
}
void removeAction(int id)
{
if (!isFiring()) {
actions_.erase(
std::remove_if(
actions_.begin(),
actions_.end(),
[id](auto& p) { return p.first == id; }
),
actions_.end()
);
} else {
deferred_ops_.push_back(
[&]() {
removeAction(id);
}
);
}
}
void run()
{
RunLock lock(this);
for (auto& item : actions_) {
item.second();
}
}
};
आप अपने "असजीला 'स्थगित निकालें' गार्ड" क्यों नहीं दिखाते? ऐसा लगता है कि जिस तरह से मैं इसे संपर्क करूंगा। – 1201ProgramAlarm
क्योंकि मेरे पास यह वास्तविक कोड है, यह खिलौना संस्करण नहीं है और यह वास्तविक संरचनाओं से जुड़ा हुआ है जो विविध टेम्पलेट आदि का उपयोग करते हैं। यह एक समस्या में है। मैं एक और सामान्य समाधान खोजने की कोशिश नहीं कर रहा था, जिसे एक्शनप्लेयर-जैसी कक्षाओं के आंतरिक लोगों के बारे में जानने की आवश्यकता नहीं है। वैसे भी, जब मुझे कुछ समय मिलता है तो बीमार उदाहरण अपडेट करें। – jwezorek