5

मैं सदस्य कार्यों को लपेटना चाहता हूं जो एक टेम्पलेट श्रेणी के साथ 'शून्य (क्लासटाइप :: फ़ंक्शन) (ArgType)' प्रकार के अनुरूप हैं। बाद में, मैं इस टेम्पलेट का एक उदाहरण के लिए ClassType का एक उदाहरण पारित करने के लिए चाहते हैं और यह आह्वान लिपटे विधि है: आवरण < की इन्स्टेन्शियशन मेंसी ++ - क्या एक टेम्पलेट में सदस्य फ़ंक्शन प्रकार से वर्ग और तर्क प्रकार निकालना संभव है?

class Foo { 
public: 
    Foo() : f_(0.0) {} 
    void set(double v) { f_ = v * 2.1; } 
    double get() { return f_; } 
private: 
    double f_; 
}; 

template <typename ArgType, typename ClassType, void (ClassType::*Method)(ArgType)> 
class Wrapper { 
public: 
    explicit Wrapper(ClassType *cls) : cls_(cls) {} 

    void do_something(ArgType value) { 
    (cls_->*Method)(value); 
    } 

private: 
    ClassType *cls_; 
}; 

#include <iostream> 
int main(int argc, char ** argv) { 
    Foo foo; 
    Wrapper<double, Foo, &Foo::set> wrapper(&foo); 

    wrapper.do_something(1.0); 
    std::cout << foo.get() << std::endl; 
    // outputs "2.1" 
    return 0; 
} 

सूचना> कि "फू" दो बार निर्दिष्ट किया जाता है - यह यहाँ अनावश्यक लग रहा है।

तो मैं क्या जानना चाहता हूं कि टेम्पलेट पैरामीटर क्लासटाइप से बचना संभव है या नहीं। उदाहरण के लिए, यदि सदस्य फ़ंक्शन पॉइंटर पैरामीटर से इसे लागू या निकालना संभव है, तो उसे रैपर <> के तत्काल में स्पष्ट रूप से निर्दिष्ट करने की आवश्यकता नहीं होगी।

इसी प्रकार, यह ArgType को स्पष्ट रूप से निर्दिष्ट करने से बचने के लिए उपयोगी होगा, जैसा कि (शायद) इसे Foo :: set से निर्धारित किया जा सकता है?

क्या यह सी ++ में संभव है? शायद ये (पूरी तरह विलक्षण) पंक्तियों के साथ कुछ:

template <void (ClassType::*Method)(ArgType)> 
class Wrapper2 { 
public: 
    explicit Wrapper(Method::ClassType *cls) : cls_(cls) {} 

    void do_something(Method::ArgType value) { 
    (cls_->*Method)(value); 
    } 

private: 
    Method::ClassType *cls_; 
}; 

// ... 

int main() { 
    Foo foo; 
    Wrapper<&Foo::set> wrapper(&foo); 
    // ... 
} 

या, शायद लागू किया जा सकता है कि यह है कि इन पंक्तियों के साथ कुछ करना चाहते हैं टेम्पलेट जादू का एक और स्तर नहीं है:

Wrapper<Magic<&Foo::set> > wrapper(&foo); 

मैं करने के लिए इच्छुक हूँ पता है कि कौन सी तंत्र उपलब्ध हो सकती है, यदि कोई हो।

मैं सी ++ 03 को आवश्यकता के रूप में उपयोग कर रहा हूं, सी ++ 11 नहीं, बल्कि यह जानने में भी दिलचस्पी है कि सी ++ 11 क्या पेशकश कर सकता है।

संपादित करें: अधिक जानकारी - मैं ~ 300 सदस्य फ़ंक्शंस (क्लासटाइप से संबंधित सभी, या बहुत समान वर्गों के सेट) को लपेटने के लिए इस तंत्र का उपयोग करना चाहता हूं, लेकिन विचार करने के लिए केवल छह या तो हस्ताक्षर होंगे:

  • शून्य (ClassType :: समारोह) (ArgType) - जहां ArgType 'चल' है
  • शून्य (ClassType :: समारोह) (ArgType) - जहां ArgType 'अभिन्न'
  • शून्य है (ClassType :: फंक्शन) (बूल)
  • शून्य (क्लासटाइप :: फ़ंक्शन) (इंडेक्स टाइप, ArgType) - उपर्युक्त तीन वाई वें एक अतिरिक्त 'सूचकांक' तर्क

सदस्य कार्यों मैं क्या "properties" एक बड़े विन्यास में कॉल 'संग्रह' वर्ग, उदाहरण के लिए (न कि ऊपर सरल फू से) के लिए 'सेटर' कार्य हैं:

class MyPropertyCollection { 
public: 
    void set_oink(double value) { oink_ = value; } 
    void set_bar(int value) { bar_ = value; } 
    void set_squee(bool value) { squee_ = value; } 
private: 
    double oink_; 
    int bar_; 
    bool squee_; 
}; 

// elsewhere 
WrapperCollection wrapper_collection; // a simple set of wrapper objects, accessed by id 
MyPropertyCollection property_collection; 
wrapper_collection.add(PROPERTY_OINK_ID, new Wrapper<double, MyPropertySet, &MyPropertySet::set_oink>(&property_collection); 
wrapper_collection.add(PROPERTY_BAR_ID, new Wrapper<int, MyPropertySet, &MyPropertySet::set_bar>(&property_collection); 
wrapper_collection.add(PROPERTY_SQUEE_ID, new Wrapper<bool, MyPropertySet, &MyPropertySet::set_squee>(&property_collection); 
// +300 more 
+0

मुझे पूरा यकीन है कि कक्षा वर्गीकरण का उपयोग करना संभव होगा। क्योंकि कटौती रिवर्स में काम करती है, आप आधार टेम्पलेट घोषणा से, विधि टेम्पलेट प्रकार को 3 टेम्पलेट प्रकारों में विस्फोट कर सकते हैं, जिसमें केवल एक ही है। और पोफ, निकाला गया। मैं तुरंत जीसीसी में कोशिश करने जा रहा हूँ। –

उत्तर

0

यह ::std::mem_fn + ::std::bind का खराब पुन: कार्यान्वयन है, जो सी ++ 11 संरचनाएं हैं। यहां बताया गया है कि आप इसका उपयोग कैसे कर सकते हैं:

#include <functional> 

int main() { 
    Foo foo; 
    auto wrapper = ::std::bind(::std::mem_fn(&Foo::set), ::std::ref(foo), _1); 
    wrapper(5); // Calls foo.set(5) 
} 

लेकिन, निश्चित रूप से, आप एक C++ 03 समाधान चाहते हैं। बूस्ट का उपयोग करके आप इसे C++ 03 में प्राप्त कर सकते हैं। मुझे यह भी विश्वास है कि इस तरह कुछ ऐसा सी ++ 03 में TR1 के साथ संभव है। #include <tr1/functional> काम करता है या नहीं, तो आप यह बता सकते हैं कि क्या आपके पास यह है। यदि आपके पास TR1 हैं जो ::std::tr1 नामस्थान में दिखाई देते हैं।

अब, एक तरीका है जिसमें यह नहीं है। आपने फ़ंक्शन पॉइंटर को कक्षा के प्रकार हस्ताक्षर का हिस्सा बना दिया है। यह करने के लिए एक अजीब बात है, लेकिन निश्चित रूप से संभव है कि आप पहले से ही जानते हैं। संकलन समय पर ClassType और ArgType मान निर्धारित करने में सक्षम होने के बावजूद मुश्किल है। आप टेम्पलेट फ़ंक्शन तर्क मिलान के साथ ऐसा कर सकते हैं, लेकिन उपयोगी नहीं है क्योंकि C++ 03 में auto नहीं है।

+0

धन्यवाद, मैं TR1 और/या बूस्ट की विविधताओं पर एक नज़र डालेगा। जिस प्रकार मैंने टाइप हस्ताक्षर का फ़ंक्शन पॉइंटर भाग बनाया है, क्योंकि मैं इसे अन्य प्रकार के कार्यों से मेल खाने के लिए विस्तारित करना चाहता हूं, और सही विशेषज्ञता स्वचालित रूप से मिलती है। हालांकि अब मैं इसके बारे में सोचता हूं, मुझे आश्चर्य है कि फ़ंक्शन प्रकार पर तर्क मिलान के साथ यह संभव होगा - लेकिन मुझे अभी भी क्लास टाइप और ArgType को टेम्पलेट पैरामीटर के रूप में निर्दिष्ट करने की आवश्यकता होगी ताकि तर्क प्रकार सही तरीके से निर्दिष्ट किया जा सके। – meowsqueak

+0

@meowsqueak: मैंने इसके बारे में कुछ सोचा है, और जब तक आपके पास टेम्पलेट तर्क के रूप में सूचक मूल्य है, तो आप फ़ंक्शन तर्क मिलान के साथ किसी भी तरह से अपनी समस्या का समाधान नहीं कर सकते हैं। समस्या यह है कि टेम्पलेट तर्क निरंतर अभिव्यक्ति होना चाहिए। और फ़ंक्शन तर्क निरंतर अभिव्यक्ति नहीं होते हैं। इसलिए जब आप तर्क और क्लास प्रकार को यादृच्छिक सूचक मान से बाहर खींचने के लिए टेम्पलेट फ़ंक्शन तर्क कटौती का उपयोग कर सकते हैं, तो आप उस पॉइंटर मान को रिटर्न प्रकार के लिए टेम्पलेट तर्कों में से एक के रूप में उपयोग नहीं कर सकते हैं। क्या उसके कोई भी मायने हैं। – Omnifarious

+0

मुझे लगता है कि यह समझ में आता है। मैंने अब अपना दृष्टिकोण बदल दिया है - टेम्पलेट पैरामीटर के रूप में सदस्य-फ़ंक्शन पॉइंटर का उपयोग करने के बजाय, मैं इसे सामान्य तर्क के रूप में पास करता हूं। तब मेरे पास लपेटने के लिए आवश्यक प्रत्येक हस्ताक्षर से निपटने के लिए टेम्पलेट का एक छोटा सा सेट है। इसका मतलब है कि लपेटा हुआ फ़ंक्शन का हस्ताक्षर (नाम सहित) अब और प्रकार का हिस्सा नहीं है। अब तक यह अच्छी तरह से काम कर रहा है। – meowsqueak

0

तुम क्या मुझे कुछ ही विकल्प के बारे में सोच कर दिया था से अधिक पढ़ना:

1) विरासत में इन्स्टेन्शियशन लपेटें। यह डरावनी चीजें आपकी परिभाषा में ले जाता है।

class FooWrapper : public Wrapper< double, Foo, &Foo::set >, public Foo 
{ 
public: 
    FooWrapper() : Wrapper(this){} 
}; 

आपका तर्क कोड इस तरह दिखता होगा:

FooWrapper fooWrapper; 
    fooWrapper.do_something(1.0); 
    std::cout << fooWrapper.get() << std::endl; 

आप डबल टेम्पलेट तर्क को खत्म नहीं किया था जिसका मतलब है, तो आप सिर्फ उन्हें ले जाया गया।

2), यह रैप करने के लिए एक स्तर पर एक अधिक सामान्य तरीका नहीं है:

template<typename argType1, class classType1> 
class FooWrapper2 : public Wrapper<argType1, classType1, &classType1::set>, public classType1 
{ 
public: 
    FooWrapper2() 
     : classType1(), 
      Wrapper<argType1, classType1, &classType1::set>(this) 
    { 

    } 
}; 

इस तरह और अधिक जटिल की तलाश में तर्क का ड्रॉ-बैक है, लेकिन आप एक नया परिभाषित करने के लिए की जरूरत नहीं है हर हस्ताक्षर के लिए हर बार एक नया आवरण आवरण,:

template<typename argType1> 
class FooWrapper3 : public FooWrapper2<argType1, Foo> 
{ 
public: 
    FooWrapper3() 
    { 

    } 
}; 
01:

FooWrapper2<double, Foo> fooWrapper2; 
    fooWrapper2.do_something(1.0); 
    std::cout << fooWrapper2.get() << std::endl; 

3) टेम्पलेट विचार के साथ लाइन में रखते हुए, आप आवरण लपेट कर सकते हैं

इस से तर्क कोड थोड़ा बेहतर लग रहा है, लेकिन आप (सिर्फ टेम्पलेट का उपयोग कर विशिष्ट कोड के साथ, के बजाय) प्रत्येक प्रकार आप लपेटकर रहे हैं के लिए resubclass होने फंस रहे हैं:

FooWrapper3<double> fooWrapper3; 
    fooWrapper3.do_something(1.0); 
    std::cout << fooWrapper3.get() << std::endl; 

4) यह विकल्प स्क्रैप आधार रैपर वर्ग और एक इंटरफ़ेस का उपयोग करता है। बस इंटरफेस को पास करें जैसे आप रैपर करेंगे और आप अधिकतर क्रियाएं कर सकते हैं।

template <typename ArgType> 
class Do_something { 
public: 

    virtual void do_something(ArgType value) = 0; 

}; 

template<typename ArgType> 
class FooWrapper4 : public Foo, public Do_something<ArgType> 
{ 
public: 
    virtual void do_something(ArgType value) 
    { 
     set(1.0); 
    } 
}; 

परीक्षण कार्यक्रम के साथ मैं खेला:

class Foo { 
public: 
    Foo() : f_(0.0) {} 
    void set(double v) { f_ = v * 2.1; } 
    double get() { return f_; } 
private: 
    double f_; 
}; 


template <typename ArgType, typename ClassType, void (ClassType::*Method)(ArgType)> 
class Wrapper { 
public: 
    explicit Wrapper(ClassType *cls) : cls_(cls) {} 

    void do_something(ArgType value) { 
    (cls_->*Method)(value); 
    } 

private: 
    ClassType *cls_; 
}; 


class FooWrapper : public Wrapper< double, Foo, &Foo::set >, public Foo 
{ 
public: 
    FooWrapper() : Wrapper(this){} 
}; 


template<typename argType1, class classType1> 
class FooWrapper2 : public Wrapper<argType1, classType1, &classType1::set>, public classType1 
{ 
public: 
    FooWrapper2() 
     : classType1(), 
      Wrapper<argType1, classType1, &classType1::set>(this) 
    { 

    } 
}; 

template<typename argType1> 
class FooWrapper3 : public FooWrapper2<argType1, Foo> 
{ 
public: 
    FooWrapper3() 
    { 

    } 
}; 

template <typename ArgType> 
class Do_something { 
public: 

    virtual void do_something(ArgType value) = 0; 

}; 

template<typename ArgType> 
class FooWrapper4 : public Foo, public Do_something<ArgType> 
{ 
public: 
    virtual void do_something(ArgType value) 
    { 
     set(1.0); 
    } 
}; 

#include <iostream> 
int main(int argc, char ** argv) { 
    Foo foo; 
    Wrapper<double, Foo, &Foo::set> wrapper(&foo); 

    wrapper.do_something(1.0); 
    std::cout << foo.get() << std::endl; 

    FooWrapper fooWrapper; 
    fooWrapper.do_something(1.0); 
    std::cout << fooWrapper.get() << std::endl; 
    // outputs "2.1" 

    FooWrapper2<double, Foo> fooWrapper2; 
    fooWrapper2.do_something(1.0); 
    std::cout << fooWrapper2.get() << std::endl; 

    FooWrapper3<double> fooWrapper3; 
    fooWrapper3.do_something(1.0); 
    std::cout << fooWrapper3.get() << std::endl; 

    FooWrapper4<double> fooWrapper4; 
    fooWrapper4.do_something(1.0); 
    std::cout << fooWrapper4.get() << std::endl; 

    return 0; 
} 
+0

भविष्य के प्रिय लोग, मुझे कोड खंडों में से किसी एक पर सही तरीके से काम करने के लिए स्वरूपण नहीं मिल रहा है, माफी माँगता हूं। –

+0

उत्तर देने के लिए धन्यवाद। बिंदु 1 के संबंध में, मुझे इसे 300 से अधिक सदस्य फ़ंक्शन (किसी तृतीय पक्ष से कॉन्फ़िगरेशन इंटरफ़ेस का हिस्सा) लपेटने के लिए विस्तारित करने की आवश्यकता है, यह जानकर कि केवल छह अलग-अलग विधि हस्ताक्षर हैं। प्रत्येक के लिए विरासत में लपेटने में 300 अलग-अलग वर्गों को लिखना शामिल होगा, जब तक कि मैं उन्हें टेम्पलेट नहीं करता (जो मुझे अंत में कुछ भी नहीं प्राप्त करता)। – meowsqueak

+0

@meowsqueak, क्योंकि यह मामला है, विधि 2 आपको वह चीज़ देनी चाहिए जो आप खोज रहे हैं। आपको केवल उन में से 6 को परिभाषित करना होगा। –

2

सी ++ 11 आप lambdas, की तरह इस्तेमाल कर सकते हैं में:

template <typename X, typename ARG> 
std::function<void(X*, ARG)> wrapper(void (X::*mfp)(ARG)) 
{ 
    return [=](X *x, ARG arg) { 
     (x->*mfp)(arg); 
    }; 
} 
VisualC साथ

++ (कम से कम के रूप में हाल ही में वीएस2013 के रूप में), सदस्य फ़ंक्शन पॉइंटर्स (या क्रैश अनुभव) कैप्चर करते समय मूल्य [=] द्वारा कैप्चर का उपयोग करें।

खेल का मैदान:

#include <iostream> 
#include <functional> 

struct A { 
    virtual void a(int i) { std::cout << "A: " << i << std::endl; } 
}; 

struct B { 
    virtual void b(int i) { std::cout << "B: " << i << std::endl; } 
}; 

template <typename X, typename ARG> 
std::function<void(X*, ARG)> wrapper(void (X::*mfp)(ARG)) { 
    return [=](X *x, ARG arg) { (x->*mfp)(arg); }; 
} 

int main() 
{ 
    auto g = wrapper(&B::b); 
    B b; 
    g(&b, 3); 
    auto h = wrapper(&A::a); 
    A a; 
    h(&a, 4); 
    return 0; 
} 
2
struct MyClass 
{ 
    MyClass& Move(MyClass& m) { return *this; } 
}; 

typedef MyClass& (MyClass::*MethodT) (MyClass&); 

template< typename T > 
struct ExtractType : std::false_type 
{ 
}; 

template< typename R, typename C, typename A > 
struct ExtractType< R (C::*)(A) > 
{ 
    typedef C type; 
}; 

static_assert(std::is_same< ExtractType<MethodT>::type, MyClass >::value, "oops"); 

यह जीसीसी 4.8 के अपने संस्करण में काम करने के लिए प्रकट होता है।
यह टिप्पणी करता है जैसे मैंने टिप्पणी में उल्लेख किया है, यह एक "बैक पैटर्न मिलान" है जिसे संकलक विशेषज्ञता जांच के दौरान करता है। यह बहुत शक्तिशाली है।
तो आप देखते हैं, हमने कुछ प्रकार के पैटर्न निर्दिष्ट किए हैं कि यदि T प्रकार का सम्मान करता है, तो यह संकलक द्वारा तीन उपप्रकारों में विघटित हो जाएगा जो इसे लिखता है: R, C, A। रिटर्न प्रकार, कक्षा प्रकार और तर्क कौन सा है।

हालांकि आप देख सकते हैं कि यह एक तर्क के साथ काम करता है। जब हमारे पास अनिश्चित संख्या में तर्क होते हैं तो कैसे करें?
शायद चेकर कक्षाओं की एक सूची, या variadic टेम्पलेट्स का उपयोग करें?

और स्पष्ट रूप से सभी ईमानदारी में, मुझे यह भी यकीन नहीं है कि यह void के साथ काम करेगा। मुझे लगता है कि टेम्पलेट में शून्य रखना हमेशा असंभव है, इसलिए संभावित घोषणाओं के सभी संयोजनों का समर्थन करने के लिए इस ExtractType वर्ग के कई संस्करणों का परिणाम होगा। या तो मुझे ऐसा लगता है।

संपादित करें:

struct MyClass 
{ 
}; 

typedef int (MyClass::*MethodT) (bool); 
typedef void (MyClass::*VV)(); 
typedef void (MyClass::*IL) (int, long); 

template< typename T > 
struct ExtractType : std::false_type 
{ 
}; 

template< typename R, typename C, class...A > 
struct ExtractType< R (C::*)(A...) > 
{ 
    typedef C type; 
    typedef R returntype; 
}; 

static_assert(std::is_same< ExtractType<MethodT>::type, MyClass >::value, "oops"); 
static_assert(std::is_same< ExtractType<VV>::type, MyClass >::value, "oops"); 
static_assert(std::is_same< ExtractType<IL>::type, MyClass >::value, "oops"); 

static_assert(std::is_same< ExtractType<MethodT>::returntype, int >::value, "oops"); 
static_assert(std::is_same< ExtractType<VV>::returntype, void >::value, "oops"); 
static_assert(std::is_same< ExtractType<IL>::returntype, void >::value, "oops"); 
:

ठीक है तो मैं यह पूरी तरह से बेतरतीब ढंग से दे रहा हूँ, लेकिन उस में सी ++ 11 यह ज्यादा बेहतर की तुलना में मैं उम्मीद काम करता है लगता है, इस जीसीसी 4.8 पर ठीक है

पागल हिस्सा यह है कि यह रिटर्न प्रकार में void पर ध्यान नहीं देता है। बेशक इसकी सी ++ 11 हालांकि।

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