一个化解C++互相包含头文件问题的方法——回调函数

最近写了两个通讯协议程序,都是电力系统中问答式传输规约,一个是基于TCP传输文件的102规约服务端程序,一个是基于串口采集数据的102规约客户端程序。之前还有别的通讯规约,最近更浓烈的期望能够抽象出这些通讯协议程序的一些通用的部分,以便于新的协议来时,我可以少一些工作量。大致上,我将这些通讯程序分成以下几类:

1、通讯协议类,主要负责按照一定传输协议去组装报文,拆解报文,范围包括全部角色的报文(主站、子站)
2、通讯控制类,包括各种通讯控制的子类:TCP、串口等,主要负责建立通讯、关闭通讯、发送报文、接收报文
3、服务端逻辑类,主要是站在服务端的角色处理逻辑,具有1、2两种类型的成员变量
4、客户端逻辑类,主要是站在客户端的角色处理逻辑,具有1、2两种类型的成员变量

5、界面类,负责与用户、逻辑类交互

听起来是很美,做到逻辑类的时候也还算顺畅,到了做界面类与逻辑类的交互时就纠结啦~我是有强迫症的,如果结构太累赘就心塞~之前写别的协议时,因为刚接触也不太了解业务,时间又紧,只好忍着把逻辑类和界面类揉在一起,这次说什么也要把他们分开!不幸的是,界面类可以方便的输出日志到界面,却不可以直接发送和接收报文,而逻辑类恰恰相反,虽然把控着报文,对于输出报文到界面却是短腿。。。而我又不希望这两个类互相包含头文件。。。好吧,强迫症就是作==||。。。

经过小师傅的提点,我用回调函数解决了这个问题。大致如下:

首先在界面类中添加一个静态的回调函数:

static void CALLBACK PrintLogCallback(void* pInstance, const char* str_log, int len);

千万别小看第一个参数,他可是可以动用界面类中的非静态兵力的制胜法宝啊!实现此函数的时候可以这样写:

void CCOM102Dlg::PrintLogCallback(void* pInstance, const char* str_log, int len)
{
    CCOM102Dlg* pCOM102Dlg = (CCOM102Dlg*) pInstance;
    //然后拿着pCOM102Dlg 这个兵符,你想干啥干啥去吧
    //比如你可以像下面这样调用自定义的打印日志方法:
    //pCOM102Dlg->AppendLog(str_log, len);
}

这样完成第一步,第二步就是要在逻辑类中添加调用回调函数的接口,如下:

typedef void (CALLBACK *PrintLogCallback)(void* pInstance, const char* str_log, int len); 
//设置与界面类交互需要的回调函数,保存函数指针
void SetInterfaceEngine(void *pInstance, PrintLogCallback printLog);

//保存与回调函数有关信息的结构体
struct InterfaceEngine
{
    void *pInstance;
    PrintLogCallback printLog;
};
InterfaceEngine m_engine;

//逻辑类的打印日志函数
void PrintLog(const char* buf, int len);

然后在需要用到界面类的打印日志功能时,找你保存的函数指针替你办事就可以啦!

void CClassLogic::SetInterfaceEngine(void *pInstance, PrintLogCallback printLog)
{
    m_engine.pInstance= pInstance;
    m_engine.printLog = printLog;
}
void CClassLogic::PrintLog(const char* buf, int len)
{
    if (m_engine.pInstance!= NULL)
    {
        m_engine.printLog(m_engine.pInstance, buf, len);
    }
}

所以现在只需要在界面类包含逻辑类的头文件就可以了~声明一个CClassLogic的成员变量m_cLogic,然后调用设置回调函数的接口就可以看到效果了:

    m_cLogic.SetInterfaceEngine(this, COM102Dlg::PrintLogCallback);
时间: 2025-01-09 18:53:57

一个化解C++互相包含头文件问题的方法——回调函数的相关文章

C++中类的前置声明和包含头文件的区别

一.类嵌套的疑问 C++头文件重复包含实在是一个令人头痛的问题,假设我们有两个类A和B,分别定义在各自的头文件A.h和B.h中,但是在A中要用到B,B中也要用到A,但是这样的写法当然是错误的: class B; class A{ public: B b;}; class B{ public: A a;}; 因为在A对象中要开辟一块属于B的空间,而B中又有A的空间,是一个逻辑错误,无法实现的,在这里我们只需要把其中的一个A类中的B类型成员改成指针形式就可以避免这个无限延伸的怪圈了,为什么要更改A而

include包含头文件的语句中,双引号和尖括号的区别

include包含头文件的语句中,双引号和尖括号的区别 #include <>格式:引用标准库头文件,编译器从标准库目录开始搜索 #incluce ""格式:引用非标准库的头文件,编译器从用户的工作目录开始搜索 预处理器发现 #include 指令后,就会寻找后跟的文件名并把这个文件的内容包含到当前文件中.被包含文件中的文本将替换源代码文件中的#include指令,就像你把被包含文件中的全部内容键入到源文件中的这个位置一样. #include 指令有两种使用形式 #incl

C++包含头文件时尖括号和双引号区别

原文链接:http://c.biancheng.net/cpp/biancheng/view/66.html 如果你还看一些别的C++教程,那么你可能很早就发现了,有些书上的#include命令写作#include <文件名>,但有时候又会出现#include "文件名".你会很疑惑,到底哪个是对的呢?为什么要有这两种不同的写法呢? 这两种写法都是正确的写法,但是它们却是有区别的.我们知道C++已经有一些编写好的头文件(比如标准函数库等等),它们存放在VC++的Includ

c++包含头文件好还是重新定义好

A.h struct A { int a; int b; }; B.cpp 在B.cpp里面用到这个结构体 有两种方法 1.自己定义一个一模一样的结构体 struct A { }; 2.包含A.h头文件 第一种感觉有点蛋疼同样的结构体定义两次,是不是重复了 第二种包含别人的头文件,会带来编译的小麻烦,而且这样模块之间的关联性变大了,感觉也不好 大家觉得哪种方法好?为什么呢? 不要重复发明轮子 , 除非这个轮子满足不了你的需求 先问一个问题,如果别人改了struct A,比如删掉了int b或者加

C++中#include包含头文件带 .h 和不带 .h 的区别

C++中#include包含头文件带 .h 和不带 .h 的区别? 如 #include <iostream> 和 #include <iostream.h> 包含的东西有哪些不同? 之前在写C++程序的时候只知道使用 #include <iostream> 的时候,使用函数前要用 using namespace std; 导入命名空间,而 #include <iostream.h> 则不用,这个得看C+ +标准化过程为C++开发者做了哪些有意义的工作. (

C语言宏定义技巧——多次包含头文件内容不同

1.  头文件定义如下: /* declears in "funcs.h" */ FUNC_1(ID_FUN1_001) FUNC_1(ID_FUN1_002) FUNC_2(ID_FUN2_001) FUNC_2(ID_FUN2_002) 2.  多次包含头文件 #define FUNC_1(opt) opt, #define FUNC_2(opt) enum aaa { #include "funcs.h" ID_FUN1_END }; #undef FUNC_

C++包含头文件中&lt;&gt;和&quot;&quot;的区别

#include "book.h" #include<iostream.h> 在刚开始学习都会有这种迷惑,有的程序用<>,有的却用"",那么二者到底什么区别呢,什么情况下使用呢? <>和""表示编译器在搜索头文件时的顺序不同,<>表示从系统目录下开始搜索,然后再搜索PATH环境变量所列出的目录,不搜索当前目录,""是表示从当前目录开始搜索,然后是系统目录和PATH环境变量所列出的

include包含头文件的语句中,双引号和尖括号的区别是什么?

include包含头文件的语句中,双引号和尖括号的区别是什么?  #include <> 格式:引用标准库头文件,编译器从标准库目录开始搜索 尖括号表示只在系统默认目录或者括号内的路径查找,通常用于包含系统中自带的头文件; 尖括号:在包含文件目录中去查找(包含目录是由用户在设置环境时设置的),而不在源文件目录去查找: #incluce "" 格式:引用非标准库的头文件,编译器从用户的工作目录开始搜索 双引号表示先在程序源文件所在目录查找,如果未找到则去系统默认目录查找,通常

关于在Clion中创建解决方案、项目与源/头文件的解决方法

关于在Clion中创建解决方案.项目与源/头文件的解决方法 在windows端我们经常使用的集成编译器是Microsoft Visual Studio(以下简称VS),一些用户使用mac端时,由于VS不支持macOS,很多人会选择使用虚拟机或者安装双系统来进行工作,但个人感觉这样做会对系统的性能造成一定程度的降低,这里就浅讲一下如何在Clion中创建与VS类似的解决方案 如何在Clion中创建解决方案 Clion由于使用的是波兰的C++编译器,这就导致一些文件内容编码类型的不同.在个人理解中,C