简体   繁体   English

从非模板基 class 指针调用模板派生 class 方法的方法

[英]Invoke a method of templated derived class method from non-template base class pointer

I'm trying to implement a Property system in my project similar to Property system in Qt.我正在尝试在我的项目中实现一个属性系统,类似于 Qt 中的属性系统。 We just started with some ideas and are in prototyping stage.我们刚开始有一些想法,正处于原型设计阶段。 Basically, what I understood from Qt is, client should be able to pass the get function, set function and property type through some macro in the.h file.基本上,我从 Qt 了解到的是,客户端应该能够通过 .h 文件中的一些宏来获取 function,设置 function 和属性类型。 So I tried to mimic the same.所以我试着模仿同样的。

Following is my sample code:以下是我的示例代码:

Abstract getter class.抽象吸气剂 class。 This type of getter class is a member in Property Class这种类型的吸气剂 class 是属性 Class 中的成员

class AbstractFunc
{
public:
  template < typename R > 
  R Invoke ()
  {
    return (this)->Invoke ();
  }
};

Get Function template: Return values can be T, T&, const T&, T* etc..获取 Function 模板:返回值可以是 T、T&、const T&、T* 等。

template < typename R, class T > class GetterFunction : public AbstractFunc
{
  typedef R (T::*GetterFunc) ();

public:
  GetterFunction (T * obj, GetterFunc func):m_Obj (obj), m_Func (func)
  {
  }

  R Invoke ()
  {
    return m_Obj->*(m_Func) ();
  }

public:
  T * m_Obj;
  GetterFunc m_Func;
};

Property Class:属性 Class:

class Property
{
public:
  Property (string name, AbstractFunc* getter):m_name (name), m_getter (getter)
  {

  }

  template < typename R > R GetValue ()
  {
    return m_getter->Invoke < R > ();
  }

private:
  string m_name;
  AbstractFunc* m_getter;
}; 

Some Window Class:一些 Window Class:

class Window
{
public:

};

Example window class示例 window class

class CheckBox :public Window
{
public:
  int GetChecked ()
  {
    return m_checked;
  }
  void SetChecked (int nChecked)
  {
    m_checked = nChecked;
  }

  void AddProperty (string name)
  {
    m_prop = new Property (name, new GetterFunction< int, Checked >(this, &Checked::GetChecked));
  }

  int m_checked;
  Property *m_prop;
};

main function:主要 function:

int main ()
{

  CheckBox cc;
  cc.AddProperty ("Hello");

  cout<<"value:"<< cc.m_prop->GetValue<int>();

  return 0;
}

PROBLEM: Getter function is remembered as AbstractFunc in Property Class.问题:Getter function 在属性 Class 中被记住为 AbstractFunc。 I want to call 'Invoke' on AbstractFunc* instance and it should invoke the member function and return correct return type .我想在 AbstractFunc* 实例上调用“调用”,它应该调用成员 function 并返回正确的返回类型 The above code throws error at AbstractFunc::Invoke.上面的代码在 AbstractFunc::Invoke 处抛出错误。

see live看直播

Your AbstractFunc isn't abstract at all: its Invoke isn't virtual.您的AbstractFunc根本不是抽象的:它的Invoke不是虚拟的。 So even though GetterFunction also has a method named Invoke , that method doesn't actually override AbstractFunc::Invoke ;因此,即使GetterFunction也有一个名为Invoke的方法,该方法实际上并没有覆盖AbstractFunc::Invoke it just hides it.它只是隐藏它。 When you try to call Invoke through the AbstractFunc* , it calls AbstractFunc::Invoke , which goes into infinite recursion and thus produces UB.当您尝试通过AbstractFunc*调用Invoke时,它会调用AbstractFunc::Invoke ,它会进入无限递归并因此产生 UB。

I would follow @nm's suggestion to make a class hierarchy like so:我会按照@nm 的建议制作一个 class 层次结构,如下所示:

class AbstractFunc {
  // lock down construction
  AbstractFunc() = default;
public:
  template<typename R>
  R Invoke();
  template<typename R>
  bool HasType() const noexcept;
  virtual ~AbstractFunc() = default; // need to have SOME virtual method so that we have runtime type info; also a virtual destructor is required anyway

  template<typename R>
  friend class TypedFunc;
};
template<typename R>
struct TypedFunc : AbstractFunc { // the ONLY instances of AbstractFunc are also instances of specializations of TypedFunc
  virtual R InvokeTyped() = 0;
};
// one kind of TypedFunc applies a getter on an object
template<typename R, typename T>
struct GetterFunc : TypedFunc<R> {
  // you never see a GetterFunc in the interface anyway... don't see a need to hide these
  T *obj; // have you considered std::shared_ptr?
  R (T::*getter)();
  GetterFunc(T *obj, R (T::*getter)()) : obj(obj), getter(getter) { }
  R InvokeTyped() override { return (obj->*getter)(); }
};
template<typename R, typename T>
std::unique_ptr<GetterFunc<R, T>> MakeGetterFunc(T *obj, R (T::*getter)()) {
  return std::make_unique<GetterFunc<R, T>>(obj, getter);
}
// another kind applies a functor, etc.
template<typename R, typename F>
struct FunctorFunc : TypedFunc<R> {
  F func;
  template<typename... G>
  FunctorFunc(G&&... args) : func(std::forward<G>(args)...) { }
  R InvokeTyped() override { return func(); }
};

This is already usable: if you have an AbstractFunc* or an AbstractFunc& , you can dynamic_cast it down to a TypedFunc of the expected type (eg TypedFunc<int> ).这已经可用:如果您有AbstractFunc*AbstractFunc& ,您可以将其TypedFunc dynamic_cast例如TypedFunc<int> )。 If that succeeds (you get a nonnull pointer or there is no std::bad_cast exception), then you just call InvokeTyped without having to know what kind of GetterFunc / FunctorFunc /whatever you are actually dealing with.如果成功(您得到一个非空指针或没有std::bad_cast异常),那么您只需调用InvokeTyped而无需知道您实际处理的是GetterFunc / FunctorFunc /whatever。 The functions Invoke and HasType declared in AbstractFunc are just sugar to help do this. AbstractFunc中声明的函数InvokeHasType只是帮助完成此任务的糖。

template<typename R>
bool AbstractFunc::HasType() const noexcept {
  return dynamic_cast<TypedFunc<R> const*>(this);
}
template<typename R>
R AbstractFunc::Invoke() {
  return dynamic_cast<TypedFunc<R>&>(*this).InvokeTyped();
  // throws std::bad_cast if cast fails
}

Done.完毕。

class Property {
  std::string name;
  std::unique_ptr<AbstractFunc> getter;
public:
  Property(std::string name, std::unique_ptr<AbstractFunc> getter) : name(std::move(name)), getter(std::move(getter)) { }
  template<typename R>
  bool HasType() const noexcept { return getter->HasType<R>(); }
  template<typename R>
  R GetValue() const { return getter->Invoke<R>(); }
  std::string const &GetName() const noexcept { return name; }
};
struct Window {
  virtual ~Window() = default;
  // doesn't really make sense to add/remove these from outside...
  virtual std::vector<Property> GetProperties() = 0;
};
class CheckBox : public Window {
  int checked = 0;
public:
  int GetChecked() /*const*/ noexcept { return checked; }
  void SetChecked(int checked) noexcept { this->checked = checked; }
  std::vector<Property> GetProperties() override {
    std::vector<Property> ret;
    ret.emplace_back("Boxes Checked", MakeGetterFunc(this, &CheckBox::GetChecked));
    return ret;
  }
};

int main() {
  CheckBox cb;
  cb.SetChecked(5);
  for(auto const &prop : cb.GetProperties()) std::cout << prop.GetName() << ": " << prop.GetValue<int>() << "\n";
}

You could then add eg a virtual std::type_info const& GetType() const or similar to AbstractFunc if you want to be able to directly get at the type, etc.然后,您可以添加例如virtual std::type_info const& GetType() const或类似于AbstractFunc如果您希望能够直接获取类型等。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM