2012-06-07 18 views
6

का उपयोग करते समय std :: function से फ़ंक्शन पॉइंटर प्राप्त करें, मैं std::bind के साथ std::function का उपयोग करने की कोशिश कर रहा हूं, लेकिन मुझे कुछ समस्याएं आ रही हैं।std :: bind

यह काम करता है:

#include <functional> 
#include <iostream> 

void print() { 
    std::cout << 2; 
} 

int main() { 
    std::function<void()> foo = print; 
    (*foo.target<void (*)()>())(); //prints 3 
} 

यह main की दूसरी पंक्ति में दुर्घटनाओं:

#include <functional> 
#include <iostream> 

void print (int i) { 
    std::cout << i; 
} 

int main() { 
    std::function<void()> foo = std::bind (print, 2); 
    (*foo.target<void (*)()>())(); 
} 

मैं वास्तव में std::function<void()> पकड़े रहा हूँ और समारोह लौटने में सक्षम होने की जरूरत है; बस इसे बुलाओ नहीं। मुझे उम्मीद है कि उपयोग कुछ ऐसा होगा:

#include <functional> 
#include <iostream> 

void print (int i) { 
    std::cout << i; 
} 

int main() { 
    Container c (std::bind (print, 2)); 

    //I would expect the original 
    c.func() (3); //prints 3 

    if (c.func() == print) /* this is what I'm mostly getting at */ 
} 

क्या मूल कार्य इसे वापस करने के लिए कोई विकल्प है, या वैकल्पिक? यह रिटर्न प्रकार के साथ भी तरह का संघर्ष करता है, क्योंकि void (*)() बाध्य हस्ताक्षर से काफी अच्छी तरह मेल खाता है।

+0

यह संभव नहीं है: आपके कोड में 'शून्य()' हस्ताक्षर के साथ कोई फ़ंक्शन नहीं है। अगर यह संभव हो तो हमें 'std :: function' की आवश्यकता नहीं होगी। –

+0

@ आर। मार्टिन्हो फर्नांडीस, यह एक सामान्य कार्य को स्टोर करने के लिए एक अच्छी चाल थी, लेकिन ऐसा लगता है कि यह केवल कॉलिंग को काम करता है। – chris

+0

@chris: एक सामान्य कार्य को संग्रहीत करने का पूरा बिंदु है। सामान्य कार्यों की तुलना * बहुत * मुश्किल हो जाती है, और यह सिर्फ हिमशैल की नोक है। – Puppy

उत्तर

20

यह काफी असंभव है। पूरे कारण यह है कि std::function मौजूद है कि समारोह संकेत horrifically चूसना और कभी नहीं, कभी, किसी के द्वारा इस्तेमाल किया जाना चाहिए, फिर कभी, असर नरक सी अंतर्संचालन की जलन मानक, क्योंकि वे राज्य के साथ कार्यों को संभाल नहीं कर सकते हैं बर्बाद आत्माओं के अलावा है।

std::function<void()> सामान्य मामले में, void(*)() में परिवर्तित नहीं किया जा सकता है। पहले उदाहरण में यह एकमात्र कारण है क्योंकि यह होता है जो मूल रूप से void(*)() होता है।

+1

मुझे लगता है कि मुझे इसे तुलना करने के बारे में भूलना होगा, धन्यवाद। – chris

+15

* फ़ंक्शन पॉइंटर्स * चूसते हैं ... केवल वैसे ही 'int' बेकार, या कच्चे पॉइंटर्स ... वे निम्न स्तर वाले ब्लॉक होते हैं जिन पर उच्च स्तरीय संरचनाएं बनाई जाती हैं। कच्चे पॉइंटर्स और int के बिना आपके पास 'std :: string' नहीं होगा, फ़ंक्शन पॉइंटर्स के बिना आपके पास 'std :: function' नहीं होगा ... –

+3

@ डेविड: वास्तव में नहीं। आप फंक्शन पॉइंटर्स के किसी भी ज्ञान के बिना 'std :: function' को कार्यान्वित कर सकते हैं। – Puppy

6

आपको फ़ंक्शन पॉइंटरstd::function से बाहर नहीं मिल सकता है, क्योंकि यहां तक ​​कि कोई भी नहीं हो सकता है। यह एक सदस्य समारोह सूचक बजाय, या एक वस्तु कि operator() लागू करता है हो सकता है।

9

यह एक छोटे से टेम्पलेट मेटा प्रोग्रामिंग का उपयोग कर प्राप्त किया जा सकता। मैंने हाल ही में ओपनजीएल जीएलयूटी (जो कॉलबैक फ़ंक्शन पॉइंटर्स पर निर्भर करता है) के आसपास एक सामान्य सी ++ रैपर लिखते समय इसका उपयोग किया था। दृष्टिकोण:

  1. सिंगलटन टेम्पलेट प्रकार का उदाहरण इंस्टेंट करें।
  2. स्टोर अपने std :: सिंगलटन उदाहरण के लिए के एक सदस्य
  3. एक स्थिर सदस्य समारोह के माध्यम से अपने std :: समारोह आह्वान के रूप में समारोह (स्थिर सदस्य कार्य करता है और नि: शुल्क कार्यों में एक ही प्रकार है, तो "आह्वान" समारोह कर सकते हैं एक फ्री फ़ंक्शन पॉइंटर के रूप में उपयोग किया जा सकता है)

C++11 on GCC 4.8 के तहत परीक्षण किया गया।

#include <unistd.h> 
#include <thread> 
#include <chrono> 
#include <mutex> 
#include <functional> 
#include <iostream> 
#include <cmath> 

template <const size_t _UniqueId, typename _Res, typename... _ArgTypes> 
struct fun_ptr_helper 
{ 
public: 
    typedef std::function<_Res(_ArgTypes...)> function_type; 

    static void bind(function_type&& f) 
    { instance().fn_.swap(f); } 

    static void bind(const function_type& f) 
    { instance().fn_=f; } 

    static _Res invoke(_ArgTypes... args) 
    { return instance().fn_(args...); } 

    typedef decltype(&fun_ptr_helper::invoke) pointer_type; 
    static pointer_type ptr() 
    { return &invoke; } 

private: 
    static fun_ptr_helper& instance() 
    { 
     static fun_ptr_helper inst_; 
     return inst_; 
    } 

    fun_ptr_helper() {} 

    function_type fn_; 
}; 

template <const size_t _UniqueId, typename _Res, typename... _ArgTypes> 
typename fun_ptr_helper<_UniqueId, _Res, _ArgTypes...>::pointer_type 
get_fn_ptr(const std::function<_Res(_ArgTypes...)>& f) 
{ 
    fun_ptr_helper<_UniqueId, _Res, _ArgTypes...>::bind(f); 
    return fun_ptr_helper<_UniqueId, _Res, _ArgTypes...>::ptr(); 
} 

template<typename T> 
std::function<typename std::enable_if<std::is_function<T>::value, T>::type> 
make_function(T *t) 
{ 
    return {t}; 
} 

int main() 
{ 
    std::cout << (void*)get_fn_ptr<0>(make_function(::sin))<<std::endl; 
    return 0; 
} 
+1

मैंने इस समाधान में एक मामूली संशोधन किया है, जो अद्वितीय आईडी के लिए टाइपनाम जोड़ना है। 'Std :: enable_if' और' std :: is_literal 'के साथ जोड़ा गया यह मुझे अनन्य आईडी के रूप में enums या अन्य शाब्दिक प्रकार पहचानकर्ताओं का उपयोग करने देता है, विशिष्टता को आगे बढ़ाता है और टकराव को रोकता है (इस सहायक कोड को थोड़ा और अधिक बनाने की लागत पर शब्द)। –

+0

यह समाधान मल्टीथ्रेडिंग के साथ काम नहीं करता है जब एकाधिक थ्रेड सिंगलटन वर्ग के 'std :: function' को सेट करते हैं। –

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