C++培训:DirectX11Frame之函数实现

  这一讲我们来看如何实现这个函数,从参数中其实我们大概可以推断出Connect的实现应该和第三个参数有关,这里我们再来讲我们的声明式:

  template

  void Connect(

  const MString& funName, // 作为信号触发的函数名

  void(T::*fun1)(Args...), // funName对应的函数指针

  T* obj1, // 持有函数名为funName的对象

  std::function eventfun // 函数对象

  )

  其实从注释里面几乎可以肯定重点在于obj1本身,因为前面三个参数都是相关联的,而第四个是一个function(关于function到底是什么我没记错的话好像我们在前面的章节中有说过),而第四个参数所带来的灵活性就是无节操的无限制。说到这里,那么问题来了,Connect要如何做才能将这些杂乱的东西联系在一起呢?

  我们先设想一种模型:obj1存储一张表,这张表记录他自身函数和槽函数相关联的信息(可以想象一下虚函数的派发原理,当然我们这个可没那个复杂)。ok,现在剩下的就是如何实现这张表了,map似乎可以满足我们的需求,这个关系也很简单,只需要将函数名作为key,将槽函数作为value来储存。这实现起来还是相对简单的,但是缺点是不支持重载函数,如果是Qt的信号槽就不存在这个问题,但是前提是得声明各种各样的信号。

  怎么声明这个map呢???向下面这么干么?

  template

  using AnyFun = std::function;

  std::map mPropretyChangedFunMap;

  知道这为什么不行吗?因为AnyFun本身依然还是模板,依然还是需要进行实例化才能够使用,所以上面的声明是不对的,那么,既然不能这么干,那么是不是可以考虑另辟蹊径了呢?前提首先要明白一点关键的地方在AnyFun上面而不是map上面,使用map来储存这个关系是没问题(如果我们想要支持重载函数的话那么可能要考虑multmap了),嗯,std::map是不是可以呢?但是当回调的时候如何还原到函数类型呢?好吧,我们这里就不纠结,至少到这一步已经离成功不远了。

  在我的工具库里面有一个MAny的东西,他能够储存任意类型的对象同时还能够还原对象的原型,这就是我们想要的,boost里面也有一个any(话说当初写这个MAny的时候也参考了boost的any,之所以没有使用boost的any主要是想让自己的东西依赖更少),所以我们可以这样声明map:

  std::map mPerpertChangedFunMap;

  此处,距离成功80%。接下来要做的事就是将响应槽函数和函数名关联起来:

  void BindPropertChangedFun(const MString& funName,const MAny& fun){

  mPerpertChangedFunMap[funName] = fun;

  }

  该是时候实现Connect了。

  template

  static void Connect(

  const MString& funName,

  void(T::*fun1)(Args...),

  T* obj1,

  std::function eventfun

  )

  {

  (obj->BindPropertChangedFun)(funName,eventfun);

  }

  从这个实现我们看出来那个函数指针似乎没什么用啊,也不知道当初脑子是怎么想的,好吧,先不管,好吧,现在我们已经将这种关系联系在一起,但是任务仅仅完成了三分之一。接下来要做的事就是我们如何让被关联的槽对象被执行。让完成这个过程那么应该有一个通知的动作,而这个通知的动作应该要知道是什么样的类型函数被调用,同时还要知道当前调用的函数的函数名,还能够传递参数,从这些信息中可以简单的推断出来这个通知函数的原型:

  template

  void NotifyPropertyChanged(

  const MString& funName,

  void(T::*func)(Args...),

  Args...agrs

  );

  这个函数的调用也相当的复杂,声明也挺吓人,好吧,我们简化一下调用操作,比如如下:

  NotifyPropertyChanged(

  MPropertyNotifer(TestA, SetValue),

  value);

  这样一来不至于在参数里面输入一个字符串那么突兀的吓人,这样一来还能够在编译器对参数进行检测,那么MPropertyNotifer又是什么呢?应该想起来了,和MSIGNAL一样同样是一个宏定义:

  #ifndef MPropertyNotifer

  #define MPropertyNotifer(className,memFun) #memFun,&className::memFun

  #endif

  现在调用约定也ok了,剩下的也就是怎么实现这个调用过程了,可以想象一下,首先我们通过第一个参数查询map,从中找到对应的MAny对象,然后还原到原始类型,最后调用传递进来的参数执行任务。

  思路理清了,接下来就是怎么实现:

  //

  // 通过函数名查找MAny

  //

  MAny GetPropertChangedFun(const MString &funName) const{

  if(mPerpertChangedFunMap.count(funName)){

  MAny any = mPerpertChangedFunMap.at(funName);

  return any;

  }

  else{

  return MAny();

  }

  }

  //

  // 获取被执行的函数

  //

  template

  std::function MGetPropertChangedFun(

  const MString& funName)

  {

  try{

  typedef std::function funType;

  MAny Any = GetPropertChangedFun(funName);

  return mj::any_cast(Any);

  }

  catch(mj::bad_any_cast e){

  return nullptr;

  }

  }

  有了这两个辅助函数后我们就可以实现

  template

  void NotifyPropertyChanged(

  const MString& funName,

  void(T::*func)(Args...),

  Args...agrs

  )

  {

  //

  // 获取转发函数

  //

  std::function fun =

  MGetPropertChangedFun(funName);

  if(fun){

  fun(agrs...);

  }

  }

  到了这里就大功告成了,现在将这一部分封装到一个基类中——MProperty

  class MProperty

  {

  friend class MDataBind;

  template

  friend class MConnect;

  public:

  MProperty();

  virtual ~MProperty();

  protected:

  //

  // 将属性被修改的这件事通知出去

  // 第一参数为修改属性函数的名字,可以使用 MPropertyNotifer 来调用函数简化操作

  // MPropertyNotifer 该宏会对函数名和函数进行参数打包

  // eg:

  // NotifyPropertyChanged(MPropertyNotifer(className,memFunName),args...)

  //

  template

  void NotifyPropertyChanged(

  const MString& funName,

  void(T::*func)(Args...),

  Args...agrs

  )

  {

  //

  // 获取转发函数

  //

  std::function fun =

  MGetPropertChangedFun(funName,this);

  if(fun){

  fun(agrs...);

  }

  }

  //

  // 就算是子类也禁止访问

  //

  private:

  MAny GetPropertChangedFun(const MString &funName) const;

  template

  void BindPropertChangedFun(const MString& funName,const MAny& fun){

  mPerpertChangedFunMap[funName] = fun;

  }

  //

  // 获取多参数函数

  //

  template

  std::function MGetPropertChangedFun(

  const MString& funName,

  const MProperty* obj)

  {

  if(obj == nullptr)

  return nullptr;

  try{

  typedef std::function funType;

  MAny Any = obj->GetPropertChangedFun(funName);

  return mj::any_cast(Any);

  }

  catch(mj::bad_any_cast e){

  return nullptr;

  }

  }

  private:

  //

  // 触发事件的函数名,接收事件的函数

  //

  MMap mPerpertChangedFunMap;

  };

  Connect作为MDataBind的静态成员函数,MDataBind作为MProperty的友元类,所以他能够访问到MProperty的私有函数:

  class MDataBind

  {

  public:

  MDataBind(){}

  virtual ~MDataBind(){}

  //

  // 两个类之间的关联

  //

  template

  static std::shared_ptr> Connect(

  const MString& funName,

  void(T::*fun1)(Args...),

  T* obj1,

  std::function eventfun

  )

  {

  MAddPropertChangedFun(

  funName,

  fun1,

  obj1,

  eventfun);

  std::shared_ptr> connectObj(new MConnect(funName,obj1,eventfun));

  return connectObj;

  }

  //

  // 断开连接

  //

  template

  static void DisConnect(

  const MString& funName,

  void(T::*fun1)(Args...),

  T* obj1,

  std::function eventfun

  )

  {

  std::function fun{nullptr};

  (obj1->BindPropertChangedFun)(funName,fun);

  }

  private:

  //

  // 添加属性改变函数

  //

  template

  static void MAddPropertChangedFun(const MString& funName,

  void(T::*fun)(Args...),

  T* obj,

  std::function eventfun)

  {

  if(obj == nullptr)

  return;

  (obj->BindPropertChangedFun)(funName,eventfun);

  }

  };

  现在我们再来看看上一讲我们说到的两个类:

  class A : public MObject{

  public:

  void SetValue(const MString& value){

  if (mValue == value)

  return;

  mValue = value;

  NotifyPropertyChanged(

  MPropertyNotifer(A, SetValue),

  value);

  }

  private:

  MString mValue;

  };

  class B : public MObject{

  public:

  void setValue(const MString& value){

  if (mValue == value)

  return;

  mValue = value;

  NotifyPropertyChanged(

  MPropertyNotifer(B, setValue),

  value);

  }

  private:

  MString mValue;

  };

  void TestAFun(const MString& value){

  MString str("TestAFun ");

  str << value;

  box::QueBox(str);

  }

  现在当执行下面程序时首先objA中的值被修改,接着通知objB修改,最后通知自由函数TestAFun

  {

  A objA;

  B objB;

  MDataBind::Connect(MSIGNAL(A, SetValue, &objA),

  MSLOT(&B::setValue,&objB));

  MDataBind::Connect(MSIGNAL(B, setValue, &objB),

  MSLOT(TestAFun));

  objA.SetValue("Hello World");

  }

  现在来说一下这个属性的一些用处,他可以用于MVC模型,由于历史原因所以功能单一的他并没有从代码库中被移除,而是在当前版本上稍作修改。

  这个版本的不足之处想必大家都看到了,他不支持多播,只能一对一,至于双向绑定不是问题,只需要反向调一下就行,尽管一对一监听已经可以满足我们大部分的需求,但有时候多播还是需要的,另一个问题就是就是不安全性,也就是说当objB被销毁时,槽对象并没有被销魂,他依然能够得到执行,这时候就是未定义行为,新版本解决了这些问题,但是当前版本依然被使用。

  下面这个这个table就是以前使用这个属性完成的:

  

  

  

  MTableWindow pTableWindow{nullptr};// 视图

  MTableData mTableData; // 数据

  然后两者之间相互绑定:

  pTableWindow.BindTableData(&mTableData);

  mTableData.BindTableWindow(&pTableWindow);

  我们通过右键触发生成一些随机数来填充数据,然后界面会得到相应的更新,同时如果我们在街面上进行单元格修改,数据也会得到相应的更新。

  int row = gen(); // 生成随机的整数

  int col = gen(); // 生成随机的整数

  mTableData.reSize(row,col);// 修改数据大小,同时界面的表格也会相应的被修改

  HArray> data;

  for(int i=0;i

  HArray v;

  for(int j=0;j

  v<

  }

  data<

  }

  mTableData.setData(data);

时间: 2024-10-06 05:52:29

C++培训:DirectX11Frame之函数实现的相关文章

智普教育Python培训之Python开发视频教程网络爬虫实战项目

网络爬虫项目实训:看我如何下载韩寒博客文章Python视频 01.mp4 网络爬虫项目实训:看我如何下载韩寒博客文章Python视频 02.mp4 网络爬虫项目实训:看我如何下载韩寒博客文章Python视频 03.mp4 智普教育Python培训 01 Windows下Python开发环境的安装与基本使用.wmv 智普教育Python培训 02 python程序基本架构.mp4 智普教育Python培训 03 Python基本输入输出语句.mp4 智普教育Python培训 04 Python变量

Oracle SQL语言之常用函数_超越OCP精通Oracle视频教程培训30

Oracle SQL语言之常用函数_超越OCP精通Oracle视频教程培训30 本课程介绍: Oracle视频教程,风哥本套oracle教程培训是<<Oracle数据库SQL语言实战培训教程>>的第5/5套:Oracle SQL语言之常用函数.主要学习Oracle数据库SQL聚合函数,分组函数,字符函数,转换函数,日期字符数字转换,日期函数,集合函数,分析函数等. Oracle SQL语言之常用函数,课程内容详细如下: 聚合函数-数据统计 分组函数-使用group by与havin

PHP培训教程 PHP里10个鲜为人知但却非常有用的函数

PHP培训教程 PHP里10个鲜为人知但却非常有用的函数 php里有非常丰富的内置函数,很多我们都用过,但仍有很多的函数我们大部分人都不熟悉,可它们却十分的有用.这篇文章里,兄弟连小编列举了一些PHP培训鲜为人知但会让你眼睛一亮的PHP函数. levenshtein() 你有没有经历过需要知道两个单词有多大的不同的时候,这个函数就是来帮你解决这个问题的.它能比较出两个字符串的不同程度. 用法: <?php $str1 = "carrot"; $str2 = "carrr

入职培训笔记记录--day9(1、指针函数与函数指针、函数指针数组 2、malloc memset 3、递归函数 4、结构体 5、共用体---》大小端 6、枚举)

1.指针函数与函数指针.函数指针数组 指针函数:返回值为指针的函数 char *fun() { char str[] = "hello world"; return str; } int main() { char *p = fun(); puts(p); return 0; } 编译时,会出现警告,返回了一个已经被释放掉的内存空间的首地址解决方法:1.static 2.char *str = "hello world"; 3.malloc 注意:使用完后要free

SQL Server数据库培训(SQL篇)----集合运算及常用函数

1.             集合运算及常用函数 1.1          字符转换函数 1.1.1             ASCII () 返回字符表达式最左端字符的ASCII 码值.在ASCII()函数中,纯数字的字符串可不用''括起来,但含其它字符的字符串必须用''括起来使用,否则会出错. SELECT ASCII('iTalkbb') ---------- 105 1.1.2             CHAR () 将ASCII 码转换为字符.如果没有输入0 ~ 255 之间的ASCI

python培训~函数

2017年5月14日 开始涉及函数 练习题疑难解答: 1 #!/usr/bin/python 2 # -*- coding:utf-8 -*- 3 4 # 表格输出 5 6 # prettytable 7 8 # msg = "asdkfasfd\tasdfasdf\tasdfasdf\tasdfasdf\nasdkfasfd\tasdfasdf\tasdfasdf\tasdfasdf\n" 9 # print(msg.expandtabs(20)) 10 11 # x y z 12

python 培训第三章 ,函数,装饰器,模块,内置函数之一函数

目录: 函数示例装饰器模块内置函数一.函数示例: 1.定义函数: def fun(args): '描述信息' 函数体 return 返回值 定义函数的三种形式: 无参函数def foo():print('in the foo') foo() 有参函数: def bar(x,y): print('in the bar') bar(1,2) 空函数: def func(): pass 空函数的应用示例: def put():pass def get():pass def cd():pass def

[web建站] 极客WEB大前端专家级开发工程师培训视频教程

极客WEB大前端专家级开发工程师培训视频教程  教程下载地址: http://www.fu83.cn/thread-355-1-1.html 课程目录:1.走进前端工程师的世界HTML51.HTML5与HTML4的区别2.HTML5新增的主体结构元素3.HTML5新增的的非主体结构元素 4.HTML5表单新增元素与属性5.HTML5表单新增元素与属性(续)6.HTML5改良的input元素的种类 7.HTML5增强的页面元素8.HTML5编辑API之Range对象(一)9.HTML5编辑API之

【绝密外泄】风哥Oracle数据库DBA高级工程师培训视频教程与内部资料v0.1

由于是[绝密外泄]资料,防止被查,需要的小伙伴赶紧下载附件中的课件文档. 由于视频太大了,已放在百度网盘了,已经在附中说明,以免被和谐. ---------------------------------------------- 第一部分:Oracle视频压缩包目录列表 ---------------------------------------------- 01.[绝密外泄]风哥全套Oracle数据库DBA高级工程师培训教程-视频分章节(不断更新) 02.[绝密外泄]风哥全套Oracle