从SmartPay dll学到的内容 宏定义 单件模式 迭代 日志记录函数进入与出来

news2024/11/18 5:45:41

日志记录函数进入与出来:利用C++的反初始化来记录退出

函数运行记时、调用次数统计等

宏定义

配置里的宏

在这里插入图片描述

WIN32;NDEBUG;_WINDOWS;_USRDLL;SMARTPAY_PGLDLL_192787_EXPORTS;ESLOG_RELEASE;HAVE_STRUCT_TIMESPEC;%(PreprocessorDefinitions)

减少代码耦合 关闭日志等

在这里插入图片描述

多线程

支持多平台

#if defined(WIN32)
			//			tcout << szTime;
			//std::cout << strLog << std::endl;
			//			tcout << szLog << std::endl;
#elif defined(linux) || defined(macintosh)
			if (ESLOG_LEVEL_ERROR == m_ulLevel)
			{
				// 红色背景,黄色前景
				std::cerr << '\033' << "[1;33;41m";
				std::cerr << szTime;
				std::cerr << szLog;
				std::cerr << '\033' << "[0m";
				std::cerr << std::endl;
			}
			else
			{
				std::cout << szTime;
				//std::cout << strLog << std::endl;
				std::cout << szLog << std::endl;
			}
#endif

多编译配置 结合宏定义输出不同版本。

在这里插入图片描述

多途径输出

文件、DebugView、控制台等。

OutputDebugStringA(szLog);
std::cout << strLog << std::endl;

https://blog.csdn.net/chenhao0568/article/details/132870969?spm=1001.2014.3001.5501

其它

说了半天,还是没明白!!!

在C++中,volatile是一个关键字,用于告诉编译器不要对带有volatile修饰符的变量进行优化。volatile通常用于标记那些在程序的执行过程中可能会被意外修改的变量,以防止编译器对其进行优化,确保每次都从内存中读取或写入变量的值。

主要用途包括:

  1. 硬件寄存器访问: volatile常常用于访问硬件寄存器,因为这些寄存器的值可以在程序执行过程中被外部设备更改。通过将变量标记为volatile,可以确保编译器不会将读写这些寄存器的操作优化掉。

    volatile int *hwRegister = (volatile int *)0x1234; // 访问硬件寄存器
    int value = *hwRegister; // 从寄存器中读取值
    
  2. 多线程环境: 在多线程编程中,volatile可以用来确保线程之间对共享变量的访问不会被编译器优化掉,从而避免潜在的并发问题。

    volatile int sharedVariable = 0; // 共享变量
    
    // 线程1
    sharedVariable = 1;
    
    // 线程2
    int value = sharedVariable;
    

需要注意的是,volatile只提供了有关变量在内存中的读写行为的信息,但它并不提供线程安全性。在多线程环境中,通常需要使用更强大的同步机制,如互斥锁或原子操作,来确保正确的并发访问。

另外,从C++11开始,C++引入了std::atomic类型,它提供了更强大的原子操作和内存模型,用于处理多线程编程中的共享数据。在许多情况下,std::atomic更适合用来替代volatile来处理多线程问题。

错误消息

您提供的代码片段使用了Windows API中的FormatMessageW函数,该函数用于将系统错误代码转换为相应的错误消息文本。下面是对您提供的代码片段的解释:

FormatMessageW(
    FORMAT_MESSAGE_ALLOCATE_BUFFER |      // 分配缓冲区以存储消息
    FORMAT_MESSAGE_FROM_SYSTEM |          // 从系统中检索错误消息
    FORMAT_MESSAGE_IGNORE_INSERTS,        // 忽略消息中的插入字符串
    NULL,                                 // 所报告的错误代码
    dwErrCode,                            // 错误代码,通常使用GetLastError()获取
    MAKELANGID(LANG_CHINESE, SUBLANG_CHINESE_SIMPLIFIED), // 语言标识,这里是简体中文
    (LPTSTR) &lpMsgBuf,                    // 用于存储错误消息的缓冲区指针
    0,                                    // 缓冲区大小,0表示自动分配
    NULL                                 // 指向插入字符串的指针(在这里不使用)
);

这段代码的目的是将Windows系统中的错误代码(dwErrCode)转换为相应的错误消息文本,并将结果存储在lpMsgBuf所指向的缓冲区中。在这里,您指定了语言标识为简体中文,以获取简体中文的错误消息。

一旦FormatMessageW函数成功执行,lpMsgBuf将包含错误消息的文本,您可以使用它来显示错误信息或进行其他处理。请注意,在使用完lpMsgBuf后,您需要使用LocalFree函数来释放分配的内存,以避免内存泄漏。

这是一种常见的用法,用于在Windows应用程序中将系统错误代码转换为人类可读的错误消息,以便用户或开发人员能够更好地理解发生的问题。

void ShowSystemError(IN u4 dwErrCode) 
{
#if defined(WIN32)
	//Show error message of GetLastError() 
	if (dwErrCode==-1)
	{
		dwErrCode = GetLastError();
	}
	LPVOID lpMsgBuf = NULL; 
	FormatMessageW( 
		FORMAT_MESSAGE_ALLOCATE_BUFFER |
		FORMAT_MESSAGE_FROM_SYSTEM | 
		FORMAT_MESSAGE_IGNORE_INSERTS, 
		NULL, 
		dwErrCode/*GetLastError()*/, 
		MAKELANGID(LANG_CHINESE, SUBLANG_CHINESE_SIMPLIFIED), // Default language 
		(LPTSTR) &lpMsgBuf, 
		0, 
		NULL 
		); 
	WCHAR pbuffer[1024*2] = {0};
	wsprintf(pbuffer,_T("Err[0x%x]:%s"),dwErrCode,(WCHAR*)lpMsgBuf);
	LocalFree( lpMsgBuf ); 
#elif defined(linux)|| defined(macintosh)
	WCHAR pbuffer[1024*2] = {0};
	swprintf(pbuffer, sizeof(pbuffer), L"Err[0x%x]:%s",errno, L"/n");
#endif
	ESLOG_INF(2,_T("%s"),pbuffer);
}

单件模式

MessageLogger& get_msg_logger(void)
{
	static MessageLogger message_logger;
	return message_logger;
}

迭代

这行代码定义了一个名为 iter 的迭代器,该迭代器用于遍历和操作一个名为 std::map 的关联容器,其中键的类型是整数 int,值的类型是 FGLCallbackstd::map 是C++标准库中的关联容器,用于存储键-值对,并根据键进行排序。

以下是代码的解释:

std::map<int, FGLCallback>::iterator iter;
  • std::map<int, FGLCallback>:这部分定义了一个std::map 容器,其中键的类型是整数 int,值的类型是 FGLCallback。这表示你可以使用整数作为键,并将 FGLCallback 对象与每个整数相关联。

  • ::iterator:: 操作符用于访问 std::map 的内部类型 iterator,它是一个迭代器类型,用于遍历 std::map 中的元素。

  • iter:这是迭代器的名称,你可以使用它来遍历 std::map 中的元素,例如使用 iter 来访问、修改或删除 std::map 中的键-值对。

要使用这个迭代器,你可以使用循环来遍历 std::map 中的元素,例如:

std::map<int, FGLCallback> myMap;
// 填充 myMap

for (std::map<int, FGLCallback>::iterator iter = myMap.begin(); iter != myMap.end(); ++iter) {
    int key = iter->first;            // 获取键
    FGLCallback value = iter->second; // 获取值

    // 在这里执行操作,如访问或修改键值对
}

这个迭代器用于遍历 std::map 中的所有键-值对,并允许你对它们进行操作。

智能指针

这行代码定义了一个名为 d 的智能指针,它指向类型为 FGLDevice 的对象。具体来说,它是一个 std::shared_ptr,这是C++标准库中的智能指针类型,用于管理动态分配的内存资源,确保资源在不再需要时被正确释放,以避免内存泄漏。

以下是代码的解释:

std::shared_ptr<FGLDevice> d;
  • std::shared_ptr<FGLDevice>:这部分定义了一个 std::shared_ptr,它可以管理指向 FGLDevice 类型对象的指针。std::shared_ptr 是一种共享所有权的智能指针,允许多个 shared_ptr 共享同一个对象,当最后一个 shared_ptr 不再需要对象时,对象的内存将自动释放。

  • d:这是智能指针的名称,你可以使用它来访问 FGLDevice 对象,以及对智能指针的其他操作,如复制、释放等。

通常,你可以使用 std::make_shared 函数来创建一个 std::shared_ptr,并初始化它指向的对象。例如:

#include <memory>

// 创建一个 std::shared_ptr 指向 FGLDevice 对象
std::shared_ptr<FGLDevice> d = std::make_shared<FGLDevice>();

这将创建一个 std::shared_ptr,指向一个新创建的 FGLDevice 对象,并自动管理对象的生命周期。当不再需要 d 时,它将负责释放关联的 FGLDevice 对象的内存。

分离线程

这段代码是使用 POSIX 线程库(通常称为 pthreads)在多线程环境中创建一个分离线程。下面是对这段代码的解释:

pthread_attr_t attr; // 创建线程属性对象
pthread_attr_init(&attr); // 初始化线程属性对象
pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED); // 设置线程为分离状态
pthread_t pid; // 创建线程的标识符
pthread_create(&pid, &attr, TransThread::thread_close, (void *) this); // 创建线程
  • pthread_attr_t attr;:这行代码定义了一个线程属性对象 attr,用于指定线程的属性。线程属性包括线程的状态、调度策略等信息。

  • pthread_attr_init(&attr);:这行代码初始化线程属性对象 attr,以确保它处于一个可用的初始状态。

  • pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED);:这行代码设置线程属性,将线程状态设置为分离状态,这意味着线程结束后将自动释放其资源,而不需要显式调用 pthread_join 函数来等待线程的结束。

  • pthread_t pid;:这行代码声明一个线程标识符 pid,它将用于标识新创建的线程。

  • pthread_create(&pid, &attr, TransThread::thread_close, (void *) this);:这行代码创建一个新的线程,并将其设置为分离状态。具体解释如下:

    • &pid:传入指向 pid 变量的指针,以便 pthread_create 函数将新线程的标识符存储在 pid 变量中。
    • &attr:传入指向线程属性对象 attr 的指针,以指定新线程的属性。
    • TransThread::thread_close:这是新线程的入口点(线程函数)。在这个例子中,它似乎是一个静态成员函数或类方法,用于执行线程的实际工作。
    • (void *) this:这是传递给线程函数的参数,通常用于传递线程所需的数据。在这里,它似乎将当前对象的指针传递给线程函数。

这段代码的效果是创建一个新线程,该线程在后台运行(分离状态),执行 TransThread::thread_close 函数,并将当前对象的指针传递给该函数。线程属性的设置确保了线程在结束后会自动释放资源。

分离线程(Detached Thread)是一种线程状态,它具有以下主要特点和作用:

  1. 自动资源回收:分离线程在执行结束后,线程库会自动回收其相关资源,包括线程的内存和其他资源,而不需要显式调用 pthread_join 或类似的函数等待线程的结束。这可以减少代码的复杂性,特别是在创建许多临时线程时。

  2. 不阻塞主线程:在主线程中创建分离线程后,主线程不需要等待分离线程的完成。这允许主线程继续执行其他任务,而不必等待分离线程的结果或等待其完成。

  3. 线程资源管理:分离线程对于创建临时线程或线程池中的线程非常有用,因为它们可以在不增加主线程的负担的情况下并发执行任务。这可以提高程序的性能和效率。

  4. 减少资源泄漏风险:当线程是分离状态时,程序员不需要关心线程的资源释放问题,这有助于减少资源泄漏的风险。如果线程是非分离状态(joinable),则需要确保在主线程中正确地调用 pthread_join 函数来等待线程的结束和释放资源。

需要注意的是,分离线程不适合所有情况。在某些情况下,你可能需要创建非分离线程,以等待线程的结果或协调线程之间的操作。选择线程状态(分离或非分离)取决于你的具体需求和应用程序的设计。在需要时,你可以使用线程库提供的函数来设置线程的状态。在POSIX线程库中,使用 pthread_attr_setdetachstate 函数来设置线程的分离状态。

互斥锁

这行代码使用了C++标准库中的std::lock_guard,它是一种用于管理互斥锁(std::mutex)的RAII(资源获取即初始化)类。下面是对这行代码的解释:

std::lock_guard<std::mutex> lk(mutex_);
  • std::lock_guard:这是一个模板类,用于在其作用域内锁定互斥锁,并在作用域结束时自动释放锁。它是一种C++中管理锁的推荐方式,因为它确保在离开作用域时始终释放锁,无论作用域是正常退出还是异常退出。

  • <std::mutex>std::lock_guard是一个模板类,需要指定互斥锁的类型作为模板参数。在这里,它指定了std::mutex,表示将使用std::mutex类型的互斥锁。

  • lk:这是std::lock_guard对象的名称,它是一个局部对象,其生命周期与当前作用域相对应。当lk离开其所在的作用域时,它会自动释放关联的互斥锁(mutex_)。

  • mutex_:这是一个名为mutex_的互斥锁,std::lock_guard将会锁定这个互斥锁。

这段代码的作用是在当前作用域中锁定mutex_互斥锁,以确保在锁定期间只有一个线程可以访问受保护的共享资源。一旦lk对象超出其作用域,互斥锁将自动释放,允许其他线程获得锁。这有助于避免多线程中的竞态条件和数据竞争问题。

分析过程

点连接后不断的获取列表 和接收串口数据

在调用每个业务接口时,先把回调等设好,数据放进TransEntity,进到trans里
find_device 设备还在不在 不在就调用demo中的回调callback (response码和response信息)

create_pipe   identity就是串口设备名  MAX_PIPE_SIZE最大使用10个通道

insert_trans_thread  数量不限

find_trans_thread  

thread->run(); 
				thread_run  又创新线程
				find_device
				find_pipe
				pipe->start() 发送开始
				mEntity.getJsonData()
				des3_encrypt 选授权时需要加解密
				pipe->send 发送数据
				ThreadLock::wait(&thread->mMutex, &thread->mCond, timeout * 1000) 等待响应
				
				
				回调if (!d->create_pipe(identity, TransThread::PRO_RECEIVED_CALLBACK))
				    mPipeList.insert(mPipeList.end(), std::make_shared<FGLPipe>(identity, received));
				     	mReceived = received;
				    		PROTOCOL_RECEIVED mReceived; //pipe received data callback
				    			void FGLPipe::receivedData(FGLMessage *message)
				    				        mReceived(mIdentity, buff, buffLen);  实际的回调
				    				        
				    				        
				    				        void *FGLDevice::thread_received(void *data)调用receivedData
				    				        
				    				        		void FGLDevice::receivedData(const unsigned char *data, unsigned int dataLen)调用了thread_received
				    				        				void FGLProtocol::COMM_RECEIVED_CALLBACK(const char *device,const unsigned char *data, int unsigned dataLen) 调用了        d->receivedData(data, dataLen);
																				void FGLProtocol::COMM_INIT_CALLBACK(int code, const char *data) 调用了        IComm::getInstance().setReceivedListener(FGLProtocol::COMM_RECEIVED_CALLBACK);  看下面
																						void FGLProtocol::initialize(int type, PROTOCOL_RESULT callback)调用了COMM_INIT_CALLBACK
																								void FGLPos::initialize(int type, RESULT_CALLBACK callback)调用了上面
																										void impl_initialize(int comm, RESULT_CALLBACK callback)调用了上面
																											接口void  ConnectDevice(const char *deviceName, FGLCallback callback)调用了上面  
				    	
				   
														setReceivedListener 转到	mCommReceivedCallBack
															unsigned __stdcall Fun1Proc(void* p1)
																	mCommReceivedCallBack(deviceCom.c_str(), pbRecv, nRecvLen);  实际在这里
																			m_Thread.Run(Fun1Proc, 0, mCommReceivedCallBack);  unsigned __stdcall Fun1Proc(void* p1)未使用
				
				void TransThread::PRO_RECEIVED_CALLBACK(const char *identity, const unsigned char *data,unsigned int dataLen) 正常回调
					void TransThread::close(int code, const char *data)
						thread_close 
							result.callback(result.transNo, entity.error_code, entity.error_info.c_str()); 有回调

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1039897.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

国内音视频开发的前景怎么样?

国内音视频开发的前景怎么样? 本人就是音视频开发&#xff0c;谈一下我的观点。 目前干我们这一行的年纪都比较大&#xff0c;我自己工作五年就是很年轻的了。年会上老板说除了音视频中心的大家都是比较年轻的。。。 有些也是过了35岁了&#xff0c;四十的都有。是不是觉得这…

Unity(四) 基于关键帧的动画与骨骼动画

Unity中有两种类型的动画&#xff1a;基于关键帧的动画和骨骼动画 基于关键帧的动画是最常见的动画形式&#xff0c;也称为帧动画。它将每一帧的动画存储为一个离散的关键帧&#xff0c;然后通过计算每一帧之间的差异来创建动画。这种类型的动画适用于不需要太多交互或程序控制…

【network】丢包网络情况记录

目录 2023-09-25 09:20 2023-09-25 11:23 接口流量 2023-09-25 09:20 2023-09-25 11:23 接口流量

使用自功率谱、互功率谱估计滤波器幅频特性

这段时间终于对工程中的随机信号的一般处理方式有点头绪了&#xff0c;功率谱密度估计是十分重要的方式之一&#xff0c;仍需继续深入细化相关内容。 示例&#xff1a;使用自功率谱、互功率谱估计滤波器幅频特性&#xff0c;自己实现 & Matlab自带函数实现。 clc;clear;cl…

BUUCTF [BJDCTF2020]EasySearch 1

“.swp” 后缀通常用于表示 Vim&#xff08;一种文本编辑器&#xff09;的交换文件。Vim 是一个强大的文本编辑器&#xff0c;它在编辑文件时会创建交换文件以确保文件内容的安全性。 审阅 访问 index.php.swp 得到源码 <?phpob_start();function get_hash(){$chars ABC…

【PDF】pdf 学习之路

PDF 文件格式解析 https://www.cnblogs.com/theyangfan/p/17074647.html 权威的文档&#xff1a; 推荐第一个连接&#xff1a; PDF Explained &#xff08;译作《PDF 解析》&#xff09; | PDF-Explained《PDF 解析》https://zxyle.github.io/PDF-Explained/ https://zxyle…

缓冲区溢出漏洞预防

什么是缓冲区溢出 组成所有应用程序的程序由缓冲区组成&#xff0c;缓冲区是在内存中分配的临时空间&#xff0c;用于保存数据&#xff0c;直到它们移动到程序的其他部分&#xff0c;缓冲区可以包含的数据字节数最初将在代码开发期间指定&#xff0c;由于没有任何类型的边界检…

UNet网络训练

UNet网络训练 训练资源 构建好UNet网络模型后&#xff0c;需要进行训练。但是训练需要特别多的原始图像和标签图像&#xff0c;对于一般而言这一步特别繁琐&#xff0c;不过在网上有一些免费的数据集可以让我们省略这一步&#xff0c;直接进行训练测试。 VOC&#xff08;Visu…

Centos环境使用Docker安装Kafka

1 Kafka简介 1、kafka是什么&#xff1f; Kafka是一种高吞吐量的分布式发布订阅消息系统&#xff0c;它可以处理消费者规模的网站中的所有动作流数据&#xff0c;具有高性能、持久化、多副本备份、横向扩展能力。 2、kafka的工作原理[去耦合] Kafka采用的是订阅-发布的模式&am…

Android应用线上闪退问题解决

解决Android应用线上闪退问题需要仔细的监控、调试和分析。以下是一些解决Android线上闪退问题的工具和方法&#xff0c;希望对大家有所帮助。北京木奇移动技术有限公司&#xff0c;专业的软件外包开发公司&#xff0c;欢迎交流合作。 工具&#xff1a; 1.Google Play 控制台&…

anaconda navigator启动时一直卡在 loading applications 页面

anaconda navigator启动时一直卡在 loading applications 页面 方法1 在安装目录找到D:\anaconda\Lib\site-packages\anaconda_navigator\api 然后打开conda_api.py&#xff0c; 在1358行找到data yaml.load(f)&#xff0c;将其改为data yaml.safeload(f) 猜测为保证代码…

精准对接促合作:飞讯受邀参加市工信局举办的企业供需对接会

2023年9月21日&#xff0c;由惠州市工业和信息化局主办的惠州市工业软件企业与制造业企业供需对接会成功举办&#xff0c;对接会旨在促进本地工业软件企业与制造业企业的紧密合作&#xff0c;推动数字化转型的深入发展。此次会议在市工业和信息化局16楼会议室举行&#xff0c;会…

【校招VIP】产品行测之逻辑计算题

考点介绍&#xff1a; 数理逻辑包括对于统计学有基础的了解&#xff0c;有基础的数据敏感性&#xff0c;拥有从数据层层深挖定位到问题的能力。知道先验概率&#xff0c;置信度&#xff0c;归因方法等基础的统计学概念。作为产品经理都应该去理解这些逻辑&#xff0c;并且思考如…

DirectX12学习笔记-创建窗口

创建窗口就是纯的Win API&#xff0c;我设想的窗口是这样的&#xff1a; 我们调用WinMain启动窗口&#xff0c;然后在WinMain初始化和启动消息循环。 消息会传入OnEvent, WndProc是窗口过程函数&#xff08;每个窗口都有一个WndProc函数&#xff0c;用于接收和处理窗口相关的…

yolov8训练自己的数据集(标注到训练)

yolov8可以用作目标检测&#xff0c;分割&#xff0c;姿态&#xff0c;跟踪。这里举例目标检测从标注到训练的过程。 官网连接 先把代码下载下来&#xff0c;这个不用说了。 然后准备数据集&#xff0c;创建一个文件夹dataset&#xff08;自己命名&#xff09;&#xff0c;下面…

m1芯片-centos安装mysql

在m1芯片中&#xff0c;虚拟机centos7使用mysql官方的yum源安装mysql没问题&#xff0c;但是在启动mysql的时候会报错&#xff0c;从日志上看是硬件问题&#xff0c;报错信息为 Most likely, you have hit a bug, but this error can also be caused by malfunctioning hardwar…

OpenCV项目开发实战--主成分分析(PCA)的特征脸应用(附C++/Python实现源码)

什么是主成分分析? 这是理解这篇文章的先决条件。 图 1:使用蓝线和绿线显示 2D 数据的主要组成部分(红点)。 快速回顾一下,我们了解到第一个主成分是数据中最大方差的方向。第二主成分是空间中与第一主成分垂直(正交)的最大方差方向,依此类推。第一和第二主成分红点(2…

【周赛364-数组】美丽塔 I-力扣 2865

&#x1f49d;&#x1f49d;&#x1f49d;欢迎来到我的博客&#xff0c;很高兴能够在这里和您见面&#xff01;希望您在这里可以感受到一份轻松愉快的氛围&#xff0c;不仅可以获得有趣的内容和知识&#xff0c;也可以畅所欲言、分享您的想法和见解。 推荐:kuan 的首页,持续学…

SpringBoot统一返回处理和全局异常处理

统一接口返回 前后端分离项目&#xff0c;通常后端会返回给前端统一的数据格式&#xff0c;一般包括code,msg,data信息。 创建返回统一实体类 package com.example.exceptionspring.domain;import lombok.Data;Data public class Result {private Integer code;private Strin…

基于微信小程序的校园二手交易平台设计与实现(源码+lw+部署文档+讲解等)

文章目录 前言学生的主要功能有&#xff1a;管理员的主要功能有&#xff1a;具体实现截图论文参考详细视频演示为什么选择我自己的网站自己的小程序&#xff08;小蔡coding&#xff09;有保障的售后福利 代码参考源码获取 前言 &#x1f497;博主介绍&#xff1a;✌全网粉丝10W…