C++ - 是否可以从模板中的成员函数类型中提取类和参数类型? [英] C++ - is it possible to extract class and argument types from a member function type in a template?

查看:39
本文介绍了C++ - 是否可以从模板中的成员函数类型中提取类和参数类型?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我想用模板化类包装符合类型void (ClassType::Function)(ArgType)"的成员函数.后来,我想将 ClassType 的一个实例传递给这个模板的一个实例,并让它调用包装的方法:

class Foo {民众:Foo() : f_(0.0) {}void set(double v) { f_ = v * 2.1;}double get() { 返回 f_;}私人的:双 f_;};模板 类包装{民众:显式包装器(ClassType *cls):cls_(cls){}void do_something(ArgType 值) {(cls_->*Method)(value);}私人的:类类型 *cls_;};#include int main(int argc, char ** argv) {福福;包装器<double, Foo, &Foo::set>包装器(&foo);wrapper.do_something(1.0);std::cout <<foo.get() <<std::endl;//输出2.1"返回0;}

注意 Wrapper 的实例化<>Foo"被指定了两次 - 这里看起来是多余的.

所以我想知道是否可以避免模板参数 ClassType.例如,如果可以从成员函数指针参数中暗示或提取它,那么就不需要在 Wrapper<> 的实例化中明确指定.

以类似的方式,避免显式指定 ArgType 也是有用的,因为(也许)它可以从 Foo::set 中确定?

这在 C++ 中可行吗?也许沿着这些(完全幻想)的路线:

template 类 Wrapper2 {民众:显式包装器(Method::ClassType *cls) : cls_(cls) {}void do_something(Method::ArgType 值) {(cls_->*Method)(value);}私人的:方法::类类型 *cls_;};//...int main() {福福;包装器<&Foo::set>包装器(&foo);//...}

或者,也许可以调用另一个级别的模板魔术来执行以下操作:

Wrapper>包装器(&foo);

我很想知道可能有哪些可用的机制(如果有).

我要求使用 C++03,而不是 C++11,但也想知道 C++11 可能提供什么.

更多信息 - 我打算使用这种机制来包装约 300 个成员函数(都属于 ClassType,或一组非常相似的类),但只需要考虑大约六个左右的签名:

  • void (ClassType::Function)(ArgType) - 其中 ArgType 为浮动"
  • void (ClassType::Function)(ArgType) - 其中 ArgType 是整数"
  • void (ClassType::Function)(bool)
  • void (ClassType::Function)(IndexType, ArgType) - 以上三个带有额外的索引"参数

例如,成员函数是我在大型配置集合"类中称为属性"的setter"函数(而不是上面的简单 Foo):

class MyPropertyCollection {民众:void set_oink(double value) { oink_ = value;}void set_bar(int value) { bar_ = value;}void set_squee(bool value) { squee_ = value;}私人的:双oink_;国际酒吧_;布尔squee_;};//别处WrapperCollection wrapper_collection;//一组简单的包装对象,通过 id 访问MyPropertyCollection property_collection;wrapper_collection.add(PROPERTY_OINK_ID, new Wrapper(&property_collection);wrapper_collection.add(PROPERTY_BAR_ID, new Wrapper(&property_collection);wrapper_collection.add(PROPERTY_SQUEE_ID, new Wrapper(&property_collection);//+300 更多

解决方案

struct MyClass{我的课堂&Move(MyClass& m) { return *this;}};typedef MyClass&(MyClass::*MethodT) (MyClass&);模板<类型名称 T >结构提取类型:std::false_type{};模板<类型名称 R,类型名称 C,类型名称 A >结构体提取类型{typedef C 类型;};static_assert( std::is_same< ExtractType< MethodT >::type, MyClass >::value, "oops" );

它似乎适用于我的 gcc 4.8 版本.
它的工作原理就像我在评论中提到的那样,它是编译器在专业化检查期间所做的反向模式匹配".这非常强大.
所以你看,我们指定了某种模式,如果类型 T 尊重,它将被编译器分解为组成它的三个子类型:RCA.分别是返回类型、类类型和参数.

但是您可以看到它仅适用于一个参数.当我们有未定义数量的参数时怎么办?
也许是一个检查器类列表,或者使用可变参数模板?

坦率地说,我什至不确定这是否适用于 void.我认为 void 总是不可能放在模板中,因此它会导致这个 ExtractType 类的许多版本支持可能声明的所有组合.或者在我看来是这样.

好的,所以我完全随机地给出它,但在 C++11 中它似乎比我预期的要好得多,这在 gcc 4.8 上没问题:

struct MyClass{};typedef int (MyClass::*MethodT) (bool);typedef void (MyClass::*VV) ();typedef void (MyClass::*IL) (int, long);模板<类型名称 T >结构提取类型:std::false_type{};模板<类型名称 R,类型名称 C,类...A >结构体提取类型{typedef C 类型;typedef R 返回类型;};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.

I would like to wrap member functions that conform to the type 'void (ClassType::Function)(ArgType)' with a templated class. Later, I want to pass an instance of ClassType to an instance of this template and have it invoke the wrapped method:

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

Notice in the instantiation of Wrapper<> that "Foo" is specified twice - it looks redundant here.

So what I'd like to know is whether it's possible to avoid the template parameter ClassType. For instance, if it is possible to imply or extract it from the member function pointer parameter, then it wouldn't need to be explicitly specified in the instantiation of Wrapper<>.

In a similar manner, it would be useful to avoid explicitly specifying ArgType also, as (perhaps) it can be determined from Foo::set?

Is this possible in C++? Perhaps something along these (entirely fantastical) lines:

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);
  // ...
}

Or, perhaps there's another level of template magic that can be invoked that would do something along these lines:

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

I'm interested to know what mechanisms might be available, if any.

I'm using C++03 as a requirement, not C++11, but also interested to know what C++11 might offer.

EDIT: more info - I intend to use this mechanism to wrap ~300 member functions (all belonging to ClassType, or a set of very similar classes), but there will only be around six or so signatures to consider:

  • void (ClassType::Function)(ArgType) - where ArgType is 'floating'
  • void (ClassType::Function)(ArgType) - where ArgType is 'integral'
  • void (ClassType::Function)(bool)
  • void (ClassType::Function)(IndexType, ArgType) - the above three with an extra 'index' argument

The member functions are 'setter' functions for what I call "properties" in a large configuration 'collection' class, for example (rather than the simple Foo above):

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

解决方案

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

It appears to work in my version of gcc 4.8.
It works like I mentioned in the comment, its a "back pattern matching" that the compiler does during specialization checks. This is very powerful.
So you see, we specified some kind of pattern that if the type T respects, it will be decomposed by the compiler into the three subtypes that composes it: R, C, A. Which is return type, class type and argument.

However you can see that it works with one argument. How to do when we have an undefined number of arguments ?
Maybe a list of checker classes, or use variadic templates ?

And frankly in all honesty, I am not even sure this will work with void. I think void is always impossible to place in template, therefore it will result in many versions of this ExtractType class to support all combinations of possible declarations. Or so it seems to me.

EDIT:

Ok so I'm giving this away completely randomly, but it seems in C++11 it works much better than I have expected, this is ok on gcc 4.8:

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

The crazy part being that it doesn't mind void in the return type. Of course its C++11 however.

这篇关于C++ - 是否可以从模板中的成员函数类型中提取类和参数类型?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆