5

我想用符合類型'void(ClassType :: Function)(ArgType)'的模板類來包裝成員函數。後來,我想類類別的一個實例傳遞給該模板的一個實例,並讓它調用包裝方法:在包裝<的實例C++ - 是否可以從模板中的成員函數類型中提取類和參數類型?

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; 
} 

通知>說,「foo」被指定了兩次 - 它這裏看起來多餘。

所以我想知道的是是否有可能避免模板參數ClassType。例如,如果可以從成員函數指針參數中隱含或提取它,則它不需要在包裝器的實例化中明確指定>。因爲(可能)它可以從Foo :: set中確定,所以避免明確指定ArgType也是有用的。

這是可能的C++?也許東西沿着這些(完全幻想)線:

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); 

我有興趣知道有什麼機制可用,如果有的話。

我使用C++ 03作爲需求,而不是C++ 11,但也有興趣知道C++ 11可能提供什麼。我打算使用這種機制來包裝300個成員函數(全部屬於ClassType或者一組非常相似的類),但是隻有大約6個左右的簽名需要考慮:

(編輯:更多信息)
  • 空隙(類類別::功能)(ArgType) - ,其中ArgType是 '浮動'
  • 空隙(類類別::功能)(ArgType) - ,其中ArgType是 '積分'
  • 空隙(類類別::函數)(bool)
  • void(ClassType :: Function)(IndexType,ArgType) - 上面三個wi第一個額外的「索引」參數

成員函數是用於我所謂在大型結構「屬性」「集合」類,例如(而非上述簡單的Foo)「設置器」功能:

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個模板類型。噗,提取。我將立即在gcc中嘗試。 – 2015-03-06 05:03:08

回答

0

這是一個很差的重新實現::std::mem_fn + ::std::bind,它們是C++ 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解決方案。使用Boost可以在C++ 03中獲得這個。我也相信在TR1的C++ 03中,這樣的事情是可能的。你可以通過查看#include <tr1/functional>是否有效來判斷你是否有這種情況。如果你有TR1那些出現在::std::tr1命名空間中。

現在,有一種方法不是。你已經將函數指針本身作爲類的類型簽名的一部分。這是一件很奇怪的事情,但你可能已經知道了。能夠在編譯時確定ClassTypeArgType值是棘手的。你可以使用模板函數參數匹配來實現,但不是很有用,因爲C++ 03沒有auto

+0

謝謝,我會看看TR1和/或Boost的變體。我將類型簽名的函數指針部分作爲原因的原因是我想擴展它來匹配其他類型的函數,並自動匹配正確的特殊匹配。然而,現在我想到了這一點,我想知道這是否有可能與函數類型的參數匹配 - 但我仍然需要指定ClassType和ArgType作爲模板參數,以便可以正確指定參數類型。 – meowsqueak 2013-02-09 21:36:39

+0

@meowsqueak:我已經想了一下,只要你有指針值作爲模板參數,你不能以任何方式用函數參數匹配來解決你的問題。問題是模板參數必須是一個常量表達式。而函數參數不是常量表達式。因此,雖然可以使用模板函數參數推導將參數和類類型從隨機指針值中拉出,但您不能將該指針值用作返回類型的模板參數之一。如果這是有道理的。 – Omnifarious 2013-02-10 20:56:25

+0

我認爲這是有道理的。我現在改變了我的方法 - 而不是使用成員函數指針作爲模板參數,我將其作爲普通參數傳遞。然後我有一小組模板來處理我需要包裝的每個簽名。這意味着包裝函數的簽名(包括名稱)不再是該類型的一部分。到目前爲止,它運作良好。 – meowsqueak 2013-02-11 22:20:16

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) 
    { 

    } 
}; 

這樣有更復雜的邏輯看的回拉,但你沒有定義一個新的包裝器每一次,只是一個新的包裝爲每一個簽名:

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

3)線保持與模板的想法,你可以用包裝:

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

    } 
}; 

從這個邏輯代碼看起來好一點,但你被卡住不得不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

親愛的未來人士,我似乎無法得到格式在其中一個代碼段正確工作,我的歉意。 – 2013-02-09 02:45:36

+0

感謝您的回覆。關於第1點,我需要擴展它來覆蓋300多個成員函數(來自第三方的配置接口的一部分),知道只有大約六個不同的方法簽名。每個人的繼承包裝將涉及寫300個單獨的類,除非我模板他們(最終沒有得到任何東西)。 – meowsqueak 2013-02-09 21:38:57

+0

@meowsqueak,既然如此,方法2應該給你你正在尋找的東西。你只需要定義其中的6個。 – 2013-02-09 21:49:03

2

在C++ 11你可以使用lambda表達式,如:

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

隨着VISUALC++(至少最近的VS2013),在捕獲成員函數指針(或遇到崩潰)時使用值[=]捕獲。

遊樂場:

#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"); 

它出現在我的版本的GCC 4.8的工作。
它像我在評論中提到的那樣工作,它是編譯器在專門化檢查過程中執行的「後臺模式匹配」。這非常強大。所以你看,我們指定了某種模式,如果類型T尊重,它將被編譯器分解成三個子類型組成它們:R,C,A。這是返回類型,類類型和參數。

但是,您可以看到它可以與一個參數一起使用。如果我們有未定義數量的參數,該怎麼辦?
也許檢查器類的列表,或使用可變模板?

坦誠地說,我甚至不確定這將與void一起工作。我認爲無效放置在模板中總是不可能的,因此它會導致ExtractType類的許多版本支持所有可能聲明的組合。或者在我看來。

編輯:

好了,所以我給這個完全消失隨機,但似乎在C++ 11它的效果要好得多,比我預料,這是對GCC 4.8 OK:

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"); 

瘋狂的部分是它不介意在返回類型void。當然,它的C++ 11然而。

相關問題