我想用模板化的类包装符合'void(ClassType :: Function)(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;
}
请注意Wrapper&lt;&gt;的实例化“Foo”指定了两次 - 这里看起来多余。
所以我想知道的是,是否可以避免模板参数 ClassType 。例如,如果可以从成员函数指针参数中暗示或提取它,那么就不需要在Wrapper&lt;&gt;的实例化中明确指定它。
以类似的方式,避免明确指定 ArgType 会很有用,因为(也许)可以从Foo :: set确定?
这在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,或者一组非常相似的类),但是只有大约六个左右的签名要考虑:
例如,成员函数是我在大型配置'集合'类中称为“属性”的“setter”函数(而不是上面的简单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)
在C ++ 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 ++(至少与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; }
答案 1 :(得分:3)
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版本
它的作用就像我在评论中提到的那样,它的背面图案匹配&#34;编译器在专业化检查期间执行的操作。这非常强大。
所以你看,我们指定了一种模式,如果类型T
尊重,它将由编译器分解为组成它的三个子类型:R
,C
,{{ 1}}。哪个是返回类型,类类型和参数。
但是你可以看到它适用于一个参数。当我们有一个未定义的参数时怎么办?
可能是一个检查器类列表,或使用可变参数模板?
坦率地说,坦率地说,我甚至不确定这会与A
一起使用。我认为void总是不可能放在模板中,因此它会导致这个void
类的许多版本支持所有可能的声明组合。或者在我看来。
编辑:
好的,所以我完全随机地放弃了它,但是在C ++ 11中它看起来比我预期的要好得多,这在gcc 4.8上是可以的:
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" );
。当然是它的C ++ 11。
答案 2 :(得分:1)
这是::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
名称空间。
现在,有一种方法不是。您已将函数指针本身作为类的类型签名的一部分。这有点奇怪,但你可能已经知道了。能够在编译时确定ClassType
和ArgType
值是很棘手的。您可以使用模板函数参数匹配来完成它,但没有用,因为C ++ 03没有auto
。
答案 3 :(得分:1)
阅读你所做的事情让我想到了几个选择:
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()
{
}
};
这个逻辑代码看起来好一点,但是你不得不为你要包装的每个类型重新分组(使用特定的代码,而不是仅使用模板):
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;
}