2010-03-20 14 views
5

Extendsजेनेरिक कॉलबैक

Related

तो, मैं टेम्पलेट metaprogramming बेहतर जानने की कोशिश कर रहा हूँ और मैं समझ यह इसके लिए एक अच्छा व्यायाम है।

मैं कोड लिखने की कोशिश कर रहा हूं जो किसी भी प्रकार के तर्कों के साथ किसी फ़ंक्शन को कॉलबैक कर सकता है जिसे मैं पास करता हूं।

 
// First function to call 
int add(int x, int y) ; 

// Second function to call 
double square(double x) ; 

// Third func to call 
void go() ; 

कॉलबैक निर्माण कोड दिखना चाहिए:

 
// Write a callback object that 
// will be executed after 42ms for "add" 
Callback<int, int, int> c1 ; 
c1.func = add ; 
c1.args.push_back(2); // these are the 2 args 
c1.args.push_back(5); // to pass to the "add" function 
         // when it is called 

Callback<double, double> c2 ; 
c2.func = square ; 
c2.args.push_back(52.2) ; 

मैं क्या सोच रहा हूँ है, टेम्पलेट metaprogramming का उपयोग कर रहा है, जैसे कॉलबैक की घोषणा इस तरह एक struct लिखने में सक्षम होना चाहता हूँ (कृपया ध्यान रखें यह बहुत स्यूडोकोड) जब

साथ कहा जाता है के लिए तो

 
<TEMPLATING ACTION <<ANY NUMBER OF TYPES GO HERE>> > 
struct Callback 
{ 
    double execTime ; // when to execute 
    TYPE1 (*func)(TYPE2 a, TYPE3 b) ; 

    void* argList ; // a stored list of arguments 
         // to plug in when it is time to call __func__ 
} ; 

है

 
Callback<int, int, int> c1 ; 

आप स्वचालित रूप से < हार्डकोर templating कार्रवाई >

 
struct Callback 
{ 
    double execTime ; // when to execute 
    int (*func)(int a, int b) ; 

    void* argList ; // this would still be void*, 
         // but I somehow need to remember 
         // the types of the args.. 
} ; 

सही दिशा में किसी भी संकेत दिए गए की तरह एक struct द्वारा आप के लिए निर्माण किया जायेगा इस लिखने पर आरंभ करने के लिए?

उत्तर

1

boost::bind पर देखें। मेरे पास कहने के लिए और कुछ नहीं है ... यदि आप वास्तव में आंतरिक को समझना चाहते हैं तो समय शायद उनके स्रोत पर पोरिंग करने और इसे फिर से लागू करने का प्रयास कर रहा है। लेकिन यह देखते हुए कि उन्होंने इसे कितनी अच्छी तरह से पॉलिश किया है, पुनर्मूल्यांकन केवल एक अकादमिक पीछा है।

2

आप इसे variadic templates के साथ कर सकते हैं, जो आपका कंपाइलर समर्थन नहीं कर सकता है। मैंने कभी उन्हें स्वयं का उपयोग नहीं किया है और इस प्रकार कुछ विवरण गलत हो सकते हैं, लेकिन मैं उनका वर्णन करने की कोशिश करूंगा।

वैराडिक टेम्पलेट्स "..." ऑपरेटर का उपयोग करते हैं। एक टेम्पलेट घोषणा (या अन्य प्रकार के भाव) के भीतर, लंबवृत्त इंगित करता है कि औपचारिक पैरामीटर किसी भी तर्क ले सकता है।

template <typename ... Args> 
class Variadic { 
public: 
    operator()(Args&& ... args); 
}; 

फ़ंक्शन कॉल अभिव्यक्ति के भीतर, लंबवृत्त उनके बाएं तर्क को अनपैक करते हैं।

Variadic<Args>::operator(Args&& ... args) { 
    func(args...); 
} 

आगे बढ़ने के लिए, आपको std::forward का उपयोग करने की आवश्यकता हो सकती है; यह एक ऐसा क्षेत्र है जहां मेरा ज्ञान अस्पष्ट हो जाता है। इस एकत्र किया गया, और हम पाते हैं:

template <typename ReturnValue, typename ... Args> 
class Callback { 
    typedef ReturnValue (*Func)(Args ... args); 

    double execTime; 
    Func func; 
    Args... args; 

public: 
    Callback(double et, Func f) : execTime(et), func(f) {} 
    ReturnValue operator()(Args&& ... a); 
    ReturnValue operator()(); 
}; 

template <typename ReturnValue, typename ... Args> 
ReturnValue Callback<ReturnValue, Args>::operator()(Args&& ... a) { 
    return (*func)(std::forward(a)...); 
} 
template <typename ReturnValue, typename ... Args> 
ReturnValue Callback<ReturnValue, Args>::operator()() { 
    return operator(*func)(args...); 
} 
+1

वैराडिक टेम्पलेट्स सी ++ 0x का हिस्सा हैं। उनका समर्थन करने वाला एक कंपाइलर (या यहां तक ​​कि कम C++ 0x समर्थन वाले कंपाइलर) में सी ++ 0x फ़ंक्शन ऑब्जेक्ट्स और 'std :: bind' भी शामिल होंगे, जो' boost :: bind' के समान है और जो भी हो के बारे में बातें कर रहे हैं। – Potatoswatter

0

C++ 0x variadic टेम्पलेट्स, जो सीधे कि मापदंडों के एक मनमाना संख्या लेता है एक टेम्पलेट का समर्थन कहते हैं। इसके बिना, आप इसे अनुकरण करने के लिए आंशिक विशेषज्ञता का उपयोग कर सकते हैं, हालांकि इसे प्रत्येक पैरामीटर के लिए एक अलग विशेषज्ञता की आवश्यकता है। उदाहरण के लिए, अगर आप इस तरह 1 से 3 मापदंडों से समर्थन कर सकते हैं:

class null_class {}; 

template <class func, class arg1, class arg2, class arg3> 
class callback_t { 
    func f; 
    arg1 a; 
    arg2 b; 
    arg3 c; 
public: 
    callback_t(func f, arg1 a, arg2 b, arg3 c) : f(f), a(a), b(b), c(c) {} 
    double operator()() const { return f(a, b, c); } 
}; 

template <class func, class arg1, class arg2> 
class callback_t<func, arg1, arg2, null_class> { 
    func f; 
    arg1 a; 
    arg2 b; 
public: 
    callback_t(func f, arg1 a, arg2 b) : f(f), a(a), b(b) {} 
    double operator()() const { return f(a, b); } 
}; 

template <class func, class arg1> 
class callback_t<func, arg1, null_class, null_class> { 
    func f; 
    arg1 a; 
public: 
    callback_t(func f, arg1 a) : f(f), a(a) {} 
    double operator()() const { return f(a); } 
}; 

template <class func, class arg1, class arg2, class arg3> 
callback_t<func, arg1, arg2, arg3> 
callback(func f, arg1 a, arg2 b, arg3 c) { 
    return callback_t<func, arg1, arg2, arg3>(f, a, b, c); 
} 

template <class func, class arg1, class arg2> 
callback_t<func, arg1, arg2, null_class> 
callback(func f, arg1 a, arg2 b) { 
    return callback_t<func, arg1, arg2, null_class>(f, a, b); 
} 

template <class func, class arg> 
callback_t<func, arg, null_class, null_class> 
callback(func f, arg a) { 
    return callback_t<func, arg, null_class, null_class>(f, a); 
} 

#ifdef TEST 
#include <iostream> 

double square(double d) { 
    return d * d; 
} 

double add(double a, double b) { 
    return a + b; 
} 

double sum(double a, double b, double c) { 
    return a + b + c; 
} 

int main() { 
    double a = 2.0, b = 3.0, c=4.0; 

    double d = callback(square, a)(); 
    double e = callback(add, b, c)(); 
    double f = callback(sum, a, b, c)(); 

    std::cout << "2.0 squared = " << d << "\n"; 
    std::cout << "3.0 + 4.0 = " << e << "\n"; 
    std::cout << "Sum = " << f << "\n"; 
    return 0; 
} 

#endif 

वापसी प्रकार के रूप में अच्छी टेम्प्लेट की जा सकती है, लेकिन मैं सादगी की खातिर कि बाहर छोड़ दिया है (या कम से कम जटिलता कम)।

0

के साथ शुरू करने के लिए, आप Boost.Function की जांच करनी चाहिए, क्योंकि यह लपेटकर कार्यों के बारे में स्वचालित रूप से है, यह आप विचारों मुझे लगता है कि दे देंगे;)

दूसरा, अपने वाक्य रचना थोड़ा अजीब है। आप पूरी तरह से टेम्प्लेट पैरामीटर है, जो अच्छी तरह से variadic टेम्पलेट्स की समस्या के बाद से यह आप प्रकार के एक मनमाना संख्या पारित करने के लिए अनुमति देता है के साथ सौदों के रूप में भाग के रूप में कार्य करता है हस्ताक्षर का उपयोग कर सकते हैं;)

Callback< int(int,int) > callback; 

संकेत मिलता है कि अपने कॉलबैक एक ले जाएगा आपके add: int add(int, int) के समान हस्ताक्षर वाले फ़ंक्शन पर पॉइंटर। मैं वास्तव में इस वाक्यविन्यास को पसंद करता हूं क्योंकि यह स्पष्ट करता है कि हम क्या कर रहे हैं।

हालांकि शुरू करने से पहले मेरे पास कोई सवाल है: आप रिटर्न प्रकार के बारे में क्या करना चाहते हैं?

1. संदर्भ

तो फिर वहाँ Boost.Fusion पुस्तकालय है जो आप एक बहुत (मूल रूप से, tuples) मदद कर सकता है की तरह बातें हैं।

इसके अलावा, Boost.FunctionTypes देखें जो फ़ंक्शन हस्ताक्षर का विश्लेषण करने के लिए सुविधाएं प्रदान करता है।

2. सड़क फिर से

// It is nice to have a base class 
// Generally callbacks do not return anything though... 
struct CallbackBase 
{ 
    virtual ~CallbackBase(); 
    virtual void execute() const = 0; 
}; 

namespace func_ = boost::function_types; 

template < 
    class Parameters, 
    class N = typename mpl_::minus< 
    typename mpl_::size<Parameters>::type, 
    mpl_::size_t<1> 
    >::type 
> 
class Streamer 
{ 
public: 
    typedef Streamer< Parameters, typename mpl_::minus<N,1>::type > next_type; 
    typedef typename mpl_::size<Parameters>::type size_type; 
    typedef typename mpl_::minus< size_type, N >::type index_type; 
    typedef typename mpl_::at<Parameters, index_type>::type arg_type; 

    Streamer(Parameters& p): mParameters(p) {} 

    next_type operator<<(arg_type arg) 
    { 
    boost::fusion::at_c<index_type>(mParameters) = arg; 
    return next_type(mParameters); 
    } 

private: 
    Parameters& mParameters; 
}; 

template <class Parameters> 
struct Streamer<Paramaters,0> 
{ 
    Streamer(Parameters&) {} 
}; 


template <class Function> 
class Callback: public CallbackBase 
{ 
public: 
    typedef typename func_::result_type<Function>::type result_type; 
    typedef typename func_::parameters_type<Function>::type parameters_type; 
    typedef typename func_::function_pointer< 
    typename func_::components<Function>::type 
    >::type function_pointer; 

    Callback(function_pointer f): mFunction(f) {} 

    virtual void execute() const 
    { 
    mReturn = Invoke<function_pointer>::Do(f,mParameters); 
    } 

    Streamer<parameters_type> operator<<(typename mpl_::at<parameters_type, 0>::type arg) 
    { 
    boost::fusion::at_c<0>(mParameters) = arg; 
    return Streamer<parameters_type>(mParameters); 
    } 

private: 
    function_pointer f; 
    result_type mResult; 
    parameters_type mParameters; 
}; 

खैर पर, कि कितनी दूर मैं चला गया है। मैंने वास्तविक आमंत्रण से निपटाया नहीं है जिसके लिए कार्य में तर्कों को पारित करने के लिए टुपल को अनपॅक करने की आवश्यकता है।

अब तक उपयोग होगा:

int add(int,int); 

void pass(const CallbackBase& b); 

int main(int argc, char* argv[]) 
{ 
    Callback<int(int,int)> c(&add); 
    c << 2 << 4; 
    pass(c); 
} 

मैं दृढ़ता से Boost.Fusion में तल्लीन करने के लिए प्रोत्साहित करते हैं, अगर आप इस क्षेत्र में अपनी पढ़ाई को आगे बढ़ाने की इच्छा, शुद्ध टेम्पलेट metaprogramming अक्सर निरर्थक है के रूप में यदि आप नहीं कर सकते परिणाम को रनटाइम दुनिया में लाएं :)