2011-01-29 13 views
9

मैं एक ऐसा फ़ंक्शन बनाने की कोशिश कर रहा हूं जो किसी विशिष्ट कार्य को निर्दिष्ट या दोहराए जाने वाले पैरामीटर के रूप में दिए गए किसी अन्य फ़ंक्शन को दोहराएगा और दोहराएगा। लेकिन जब आप पैरामीटर के रूप में फ़ंक्शन को पास करना चाहते हैं तो आपको हाथ से पहले अपने सभी पैरामीटर जानना होगा। अगर मैं फ़ंक्शन को एक पैरामीटर के रूप में पास करना चाहता था, और पैरामीटर दूसरे के रूप में कैसे करना चाहता था तो मैं कैसे करूँगा?सी ++: मैं किसी फ़ंक्शन को अपने फ़ंक्शन (बिना पैरामीटर जानने के) कैसे पास करूं?

void AddTimer(float time, int repeats, void (*func), params); // I know params has no type and that (*func) is missing parameters but it is just to show you what I mean 

अग्रिम धन्यवाद

उत्तर

3

dribeas 'जवाब जहाँ तक आधुनिक सी ++ का संबंध है सही है।

ब्याज के लिए, वहाँ भी सी दुनिया है कि जहाँ तक यह जाता है, सी में काम करता है से एक सरल लो तकनीक समाधान है ++। मनमाने ढंग से पैरामीटर की अनुमति देने के बजाय, फ़ंक्शन को void (*func)(void*) के रूप में परिभाषित करें, और "पैरा" void* बनाएं। इसके बाद कॉलर का काम कुछ संरचना को परिभाषित करने के लिए है जिसमें पैरामीटर होंगे, और इसके जीवन चक्र का प्रबंधन होगा। आमतौर पर फोन करने वाले भी समारोह है कि वास्तव में कहा जा करने के लिए आवश्यक है के लिए एक सरल आवरण लिखना होगा:

void myfunc(int, float); // defined elsewhere 

typedef struct { 
    int foo; 
    float bar; 
} myfunc_params; 

void myfunc_wrapper(void *p) { 
    myfunc_params *params = (myfunc_params *)p; 
    myfunc(params->foo, params->bar); 
} 

int main() { 
    myfunc_params x = {1, 2}; 
    AddTimer(23, 5, myfunc_wrapper, &x); 
    sleep(23*5 + 1); 
} 

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

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

15

सबसे अच्छा है कि आप कर सकते हैं एक साथ std::function या boost::function का उपयोग तर्क के रूप में, std::bind या boost::bind साथ करने के लिए, ठीक है, समारोह के साथ बहस के लिए बाध्य है:

void foo() { std::cout << "foo" << std::endl; } 
void bar(int x) { std::cout << "bar(" << x << ")" << std::endl; } 
struct test { 
    void foo() { std::cout << "test::foo" << std::endl; } 
}; 
void call(int times, boost::function< void() > f) 
{ 
    for (int i = 0; i < times; ++i) 
     f(); 
} 
int main() { 
    call(1, &foo);     // no need to bind any argument 
    call(2, boost::bind(&bar, 5)); 
    test t; 
    call(1, boost::bind(&test::foo, &t)); // note the &t 
} 

ध्यान दें कि पूरी तरह से जेनेरिक फ़ंक्शन पॉइंटर पास करने में स्वाभाविक रूप से कुछ गलत है: आप इसका उपयोग कैसे करते हैं? कॉलिंग फ़ंक्शन का शरीर अज्ञात प्रकार के तर्कों की एक अनिर्धारित संख्या को पारित करने में सक्षम होने जैसा दिखता है? (ध्यान दें उदाहरण में &t यही कारण है कि bind टेम्पलेट्स संकल्प, वे एक वर्ग functor कि तर्कों की प्रतियों के साथ एक साथ समारोह सूचक (ठोस समारोह सूचक) संग्रहीत करता है जब बुला उपयोग करने के लिए पैदा करते हैं ताकि सूचक और नहीं वस्तु नकल की जाती है)। bind का परिणाम एक मजेदार है जिसे ज्ञात इंटरफेस के माध्यम से बुलाया जा सकता है, इस मामले में इसे function< void() > के अंदर बाध्य किया जा सकता है और बिना किसी तर्क के बुलाया जा सकता है।

+0

कॉल केवल तभी कार्य करेगा? – Ninja

+0

@ निंजा हां बिल्कुल। कॉलर के रूप में, यदि आप यह भी नहीं जानते कि रिटर्न प्रकार क्या है, तो आप फ़ंक्शन का उपयोग कैसे कर सकते हैं? आप 'boost :: function ' का उपयोग कर सकते हैं और वापसी प्रकार को अपनी इच्छित चीज़ों में डाल सकते हैं। यदि आप आधुनिक सी ++ विकल्प चाहते हैं, तो आप कुछ प्रकार के एरर के साथ खेलने के लिए 'boost :: function ' का उपयोग कर सकते हैं। – kizzx2

1

यदि वाकई समारोह सूचक के बारे में कोई नियम नहीं है सब पर, बस * शून्य का उपयोग करें।

+5

'शून्य (*) (शून्य)' 'void * 'से बेहतर" जेनेरिक "फ़ंक्शन पॉइंटर प्रकार है क्योंकि यदि आप सही फ़ंक्शन पॉइंटर टाइप करने के लिए' void (*) (शून्य) 'प्रकार का मान डालते हैं, तो इसे टाइप करें गारंटी है कि मूल कार्य सूचक मूल्य संरक्षित किया जाएगा। यदि आप फ़ंक्शन पॉइंटर मान को 'शून्य *' और पीछे वापस डालते हैं तो इसकी गारंटी नहीं है। –

+0

@ चार्ल्स: आपने मानक में इसे कहां पढ़ा? –

+3

@DanielTrebbien: 5.2.10 [expr.reinterpret.cast]/6. –

2

यह सिर्फ एक उदाहरण है कि कैसे आप एक और कार्य करने के लिए समारोह सूचक गुजारें सकता है, और फिर इसे कहते है:

void AddTimer(float time, int repeats, void (*func)(int), int params) 
{ 
    //call the func 
    func(params); 
} 

void myfunction(int param) 
{ 
    //... 
} 

AddTimer(1000.0, 10, myfunction, 10); 

इसी तरह, आप अपने कोड को अपने समारोह विभिन्न प्रकार या/और मानकों की संख्या लेता है, तो लिख सकते हैं!

+0

+1, फिर से प्रश्न को फिर से पढ़ने के बाद, मुझे पूरा यकीन नहीं है कि तर्कों की संख्या और प्रकार अज्ञात हैं, और यदि ये ज्ञात हैं मेरा से एक बेहतर बेहतर समाधान। –

+0

यह वही नहीं है जो मैंने वास्तव में पूछा था। ऐसा लगता है कि डेविड का जवाब अधिक उपयोग है। – Ninja

0

सी ++ 11 में, चीज़ें वास्तव में सरल होती हैं - आपको जो कुछ चाहिए अपने टाइमर को लागू करने के लिए।

बाध्य फ़ंक्शन को कॉल गुजर के सबसे संक्षिप्त तरीके से एक functor लैम्ब्डा सिंटैक्स का उपयोग उत्पन्न, उदा .: []{ std::cout << "Hello, world!" << std::endl; } गुजर कर रहा है।इस प्रकार उत्पन्न एक वस्तु में केवल एक प्रकार का संकलक होता है, लेकिन प्रकार परिवर्तनीय है std::function<void()>

#include <functional> 
#include <list> 
#include <chrono> 
#include <thread> 
#include <iostream> 

template <typename Clock = std::chrono::high_resolution_clock> 
class Timers { 
public: 
    using clock = Clock; 
    using duration = typename clock::duration; 
    using time_point = typename clock::time_point; 
private: 
    struct Timer { 
     duration const period; 
     std::function<void()> const call; 
     int repeats; 
     time_point next; 
     Timer(duration $period, int $repeats, std::function<void()> && $call) : 
     period($period), call(std::move($call)), repeats($repeats) {} 
    }; 
    std::list<Timer> m_timers; 
public: 
    Timers() {} 
    Timers(const Timers &) = delete; 
    Timers & operator=(const Timers &) = delete; 
    template <typename C> void add(std::chrono::milliseconds period, 
            int repeats, C && callable) 
    { 
     if (repeats) m_timers.push_back(Timer(period, repeats, callable)); 
    } 
    enum class Missed { Skip, Emit }; 
    void run(Missed missed = Missed::Emit) { 
     for (auto & timer : m_timers) timer.next = clock::now() + timer.period; 
     while (! m_timers.empty()) { 
     auto next = time_point::max(); 
     auto ti = std::begin(m_timers); 
     while (ti != std::end(m_timers)) { 
      while (ti->next <= clock::now()) { 
       ti->call(); 
       if (--ti->repeats <= 0) { 
        ti = m_timers.erase(ti); 
        continue; 
       } 
       do { 
        ti->next += ti->period; 
       } while (missed == Missed::Skip && ti->next <= clock::now()); 
      } 
      next = std::min(next, ti->next); 
      ++ ti; 
     } 
     if (! m_timers.empty()) std::this_thread::sleep_until(next); 
     } 
    } 
}; 

int main(void) 
{ 
    Timers<> timers; 
    using ms = std::chrono::milliseconds; 
    timers.add(ms(1000), 2, []{ std::cout << "Hello, world!" << std::endl; }); 
    timers.add(ms(100), 20, []{ std::cout << "*" << std::endl; }); 
    timers.run(); 
    std::cout << std::endl; 
    return 0; 
} 
संबंधित मुद्दे

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