写一个简单的 C++ 日志库 - cllogger(3)- CRT

news2024/12/26 0:08:44

通过上一篇 《写一个简单的 C++ 日志库 - cllogger(2)- 日期时间》我们已经掌握了如何通过 std::chrono 提供的日期时间工具转换时间参数为指定格式的字符串。

现在我们可以把各个参数信息拼装为 Entry 实例,交给 OutputMessage() 


void cllogger::LogInternal(Level level, std::chrono::system_clock::time_point timestamp, const WCHAR* source, const WCHAR* msg)
{
	auto milliseconds = std::chrono::duration_cast<std::chrono::milliseconds>(timestamp.time_since_epoch());
	auto seconds = std::chrono::duration_cast<std::chrono::seconds>(milliseconds).count();
	auto time = localtime(&seconds);

	WCHAR timestampSz[128];
	size_t len = _snwprintf_s(
		timestampSz,
		_TRUNCATE,
		L"%02i:%02i:%02i.%03llu",
		time->tm_hour,
		time->tm_min,
		time->tm_sec,
		milliseconds.count() % 1000);

    Entry entry = { level, std::wstring(timestampSz, len), source, msg };

	OutputMessage(entry);
}

 OutputMessage() 将完成最终的日志字符串拼接并输出


void cllogger::OutputMessage(Entry& entry)
{
#ifndef _DEBUG
	if (!m_LogToFile) return;
#endif

	const WCHAR* levelSz =
		(entry.level == Level::Error) ? L"ERROR" :
		(entry.level == Level::Warning) ? L"WARNINIG" :
		(entry.level == Level::Fatal) ? L"Fatal" :
		L"DEBUG"
		;

    // 拼装日志信息字符串
	std::wstring message = levelSz;
	message += L" (";
	message.append(entry.timestamp);
	message += L") ";
	message += entry.tag;
	message += L": ";
	message += entry.messsage;
	message += L'\n';
    
    // 输出日志信息字符串
}

日志消息的输出,可根据配置分别输出到调试窗口(或dbgview)、文件等。

先来实现输出到调试窗口,这里特指 windows 平台上 Visual studio 开发环境中,以 Debub 模式启动程序时底部显示的【输出】窗口。

在【输出】窗口中右键可以看到异常、单步筛选、模块加载、卸载、进程退出、线程退出、程序输出等信息都会输出到这里。

【程序输出】是指我们通过代码输出的信息,这里我们就要讲讲输出信息到【输出】窗口的两种方法:

1、OutputDebugString API 

    OutputDebugString(L"output message.");

如果我们希望只在debug版的时候才输出,则需要

#ifdef _DEBUG
    ::OutputDebugString(L"DEBUG");
#endif // _DEBUG

2、 _RPTn 宏系列

_RPTn 中的n后缀指定args中的(即格式化数据的)参数个数,它可以是 0、1、2、3、4 或 5。_RPTWn 是对应的宽字符版本。

如,没有args时:

_RPTW0(_CRT_WARN, message.c_str());

有1个参数时

int foo = 3;
_RPTW1(_CRT_WARN, L"foo is %d", foo);

后续以此类推。

考虑编译器及配置的不同,下面的调用方式更为周密安全:

#ifdef _UNICODE
#ifdef _RPTW0
        _RPTW0(_CRT_WARN, foo);
#endif
#else
        _RPT0(_CRT_WARN, foo);
#endif

这二者的主要区别是, OutputDebugString 是系统API,任何时候都会执行消息输出;但 _RPTn 则是 CRT 的一部分,它仅仅在调试模式下,输出到  Visual studio 的【输出】窗口中,而 Relase 版则会被替换为空,对性能基本没有影响。

CRT(C Runtime Library)

什么是CRT

Microsoft C 运行时库 (CRT) 。为了提高C语言的开发效率,C标准定义了一系列常用的函数,称为C库函数。C标准仅仅定义了函数原型,并没有提供实现。因此这个任务留给了各个支持C语言标准的编译器。每个编译器通常实现了标准C的超集,称为C运行时库(C Run Time Library),简称CRT。对于VC++编译器来说,它提供的CRT库支持C标准定义的标准C函数,同时也有一些专门针对Windows系统特别设计的函数 
  

CRT 和 API 及操作系统之间的关系

CRT包含哪些内容

内容说明
启动、退出入口函数、及所依赖的函数
标准函数C语言标准、标准库所拥有的函数实现
I/OI/O的封装
堆的封装和实现
语言实现特殊功能的实现
调试实现调试功能的代码

由此看来,CRT是 Windows 上编程的基石,提供了方方面面的底层支持。

使用 CRT 相关的功能很简单,只需要引入 <crtdbg.h> 即可。

我们接下来再列举一个 CRT 应用的例子。

CRT 调试技术

要使用 CRT 调试库之一,必须链接 /DEBUG,并使用 /MDd、/MTd 或 /LDd 进行编译。即,必须处于调试模式下。

C++ 程序员经常会遇到的困难之一便是内存泄露。内存泄露的原因是使用new或malloc等函数分配内存,但没有正确的配对使用delete和free释放内存,就会造成程序所占用的内存越来越大的现象。

检查内存泄漏的原理是,在分配的数据一头一尾分别插入对应的标志数据,微软使用的是0xfd 0xfd 0xfd 0xfd,检查是否溢出直接检查这两个标志数据是否被修改即可判断。

例如,我们分配10个字节的内存

char* a = new char[10];

a 的地址为 0x01039099,查看内存 

可以看到数据两端都填充了0xfd,初始化分配的数据填充为0xcd。 

主动调用 _CrtDumpMemoryLeaks 检查泄露:

#include <crtdbg.h>

int main()
{
	char* a = new char[10];
	_CrtDumpMemoryLeaks();
}

输出

捕获到了内存泄漏,且指明了内存地址和大小。

如果想输出泄露内存代码所在的文件名和行号,那就完美了,我们可以通过宏来做到

#define DEBUG_NEW new(_CLIENT_BLOCK, __FILE__, __LINE__) 
#define new DEBUG_NEW

效果: 

已经非常清晰明了了。我们再进一步,能否让内存泄露信息以弹出窗口的方式显示?完全可以,只需要通过 _CrtSetReportMode 设置报告现实的方式即可:

#include <crtdbg.h>

int main()
{
	_CrtSetReportMode(_CRT_ERROR, _CRTDBG_MODE_WNDW | _CRTDBG_MODE_DEBUG);
	_CrtSetReportMode(_CRT_WARN, _CRTDBG_MODE_WNDW | _CRTDBG_MODE_DEBUG);
	char* a = new char[10];
	_CrtDumpMemoryLeaks();
}

 效果:

是不是很简单就做到了内存泄露的检查,其实,通过CRT我们还可以做到程序退出时自动检查,甚至为了降低检查对程序性能的影响,我们还可以设置检查内存泄露的频率。

总结

CRT 作为 Windows 编程中不可或缺的一部分,提供了基本的函数和宏,善用 CRT 会简化开发,提供可靠性,提供层序性能,确保了程序的高效健壮,需要我们好好的把握。


参考

  • C runtime library reference | Microsoft Learn
  • _RPT, _RPTF, _RPTW, _RPTFW Macros | Microsoft Learn

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

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

相关文章

PyTorch 深度学习(GPT 重译)(三)

六、使用神经网络拟合数据 本章内容包括 与线性模型相比&#xff0c;非线性激活函数是关键区别 使用 PyTorch 的nn模块 使用神经网络解决线性拟合问题 到目前为止&#xff0c;我们已经仔细研究了线性模型如何学习以及如何在 PyTorch 中实现这一点。我们专注于一个非常简单…

python爬虫学习第二天----类型转换

&#x1f388;&#x1f388;作者主页&#xff1a; 喔的嘛呀&#x1f388;&#x1f388; &#x1f388;&#x1f388;所属专栏&#xff1a;python爬虫学习&#x1f388;&#x1f388; ✨✨谢谢大家捧场&#xff0c;祝屏幕前的小伙伴们每天都有好运相伴左右&#xff0c;一定要天天…

Word2vec学习笔记

&#xff08;1&#xff09;NNLM模型&#xff08;神经网络语言模型&#xff09; 语言模型是一个单纯的、统一的、抽象的形式系统&#xff0c;语言客观事实经过语言模型的描述&#xff0c;比较适合于电子计算机进行自动处理&#xff0c;因而语言模型对于自然语言的信息处理具有重…

Python基础入门 --- 6.数据容器

文章目录 Python基础入门第六章&#xff1a;6.数据容器6.1 列表6.1.1 列表下标索引6.1.2 列表常用操作查找指定某元素的下标修改指定位置的元素值插入元素追加元素方式1追加元素方式2删除元素删除某元素在列表中的第一个匹配项清空列表统计某元素在列表中的数量统计列表元素个数…

【Redis】Redis常见原理和数据结构

Redis 什么是redis redis是一款基于内存的k-v数据结构的非关系型数据库&#xff0c;读写速度非常快&#xff0c;常用于缓存&#xff0c;消息队列、分布式锁等场景。 redis的数据类型 string&#xff1a;字符串 缓存对象&#xff0c;分布式ID&#xff0c;token&#xff0c;se…

RAGGED: Towards Informed Design of Retrieval Augmented Generation Systems阅读笔记

论文链接&#xff1a;https://arxiv.org/abs/2403.09040 Github链接&#xff1a;GitHub - neulab/ragged: Retrieval Augmented Generation Generalized Evaluation Dataset 最近RAG&#xff08;retrieval-augmented generation&#xff09;真的好火&#xff0c;这不CMU的发了…

vscode用SSH远程开发c语言

vscode配置远程 这里我使用虚拟机进行展示&#xff0c;首先需要你的虚拟机安装好ssh 没安装好就执行下面的命令安装并开启服务 sudo apt-get install ssh sudo service ssh start ps -e | grep sshvscode安装 remote-ssh扩展 点击左下角的远程连接&#xff0c;我这里已经连接…

电脑小白入门|Windows系统下只要记住这几点,你就能流畅使用!

前言 前段时间发现有很多小伙伴在使用电脑这个问题上遇到了很大的难题&#xff1a;到底什么时候该用鼠标左键单击、什么时候该双击&#xff1b;电脑的快捷键那么多怎么记得住等等的问题。 今天小白就稍微来讲解一下&#xff0c;这个帖子适合Windows电脑都不会使用的小伙伴。 …

流畅的 Python 第二版(GPT 重译)(四)

第二部分&#xff1a;函数作为对象 第七章&#xff1a;函数作为一等对象 我从未认为 Python 受到函数式语言的重大影响&#xff0c;无论人们说什么或想什么。我更熟悉命令式语言&#xff0c;如 C 和 Algol 68&#xff0c;尽管我将函数作为一等对象&#xff0c;但我并不认为 Py…

iframe动态操作标签分享

前言 分享一个近期工作中遇到的关于IFrame的需求&#xff0c;以及解决方案。 需求大致是说在我们系统中嵌套了另一个文档页面&#xff0c;这个文档页面是爬取的&#xff0c;并且页面是原先使用后端渲染实现的&#xff0c;取到的css和script标签都是相对路径比如: "./mia…

【文件操作和IO】

文件操作和IO 1.文件2. 硬盘上文件的目录结构3. 文件路径4. 文件重要分类&#xff1a;5. Java中操作文件5.1 Java对于文件操作的API5.2 Java中使用File类来进行文件操作5.3 File类属性5.4 构造方法5.5 方法&#xff1a; 6. 文件内容的读写 -- 文件流&#xff08;数据流&#xf…

32.768K晶振X1A000141000300适用于无人驾驶汽车电子设备

科技的发展带动电子元器件的发展电子元器件-“晶振”为现代的科技带来了巨大的贡献&#xff0c;用小小的身体发挥着大大的能量。 近两年无人驾驶汽车热度很高&#xff0c;不少汽车巨头都已入局。但这项技术的难度不小&#xff0c;相信在未来几年里&#xff0c;无人驾驶汽车这项…

改进粒子群优化算法||粒子群算法变体||Improved particle swarm optimization algorithm

粒子群算法&#xff08;Particle Swarm Optimization&#xff0c;PSO&#xff09;是一种基于群体智能的优化算法&#xff0c;其思想来源于鸟群寻食和鱼群捕食等自然现象。PSO算法通过模拟群体智能的行为&#xff0c;以一种启发式的方式寻找最优解&#xff0c;因此具有全局搜索能…

数学建模(熵权法 python代码 例子)

目录 介绍&#xff1a; 模板&#xff1a; 例子&#xff1a;择偶 极小型指标转化为极大型&#xff08;正向化&#xff09;&#xff1a; 中间型指标转为极大型&#xff08;正向化&#xff09;&#xff1a; 区间型指标转为极大型&#xff08;正向化&#xff09;&#xff1a…

【Web应用技术基础】HTML(4)——表单类的标签

目录 题目1&#xff1a;文本框 题目2&#xff1a;密码框 题目3&#xff1a;单选框 题目4&#xff1a;多选框 题目5&#xff1a;单选框选中 题目6&#xff1a;禁用disabled 题目7&#xff1a;lable标签 题目8&#xff1a;下拉框 题目9&#xff1a;textarea 题目10&…

【Java之老话常谈】学习Java可以用来做什么?

对于很多新手来说,刚开始接触Java会很迷惘,不知道Java可以做什么。其实Java 可以做的东西太多了,手机游戏、中间件、软件、网站,电脑游戏,以及现在流行的安卓手机app等,都是由java语言编写的。由于Java拥有很高的安全性、平台移植性等,所以受到广大程序员的喜爱。 java…

接口测试、postman、测试点提取【主】

接口测试是测试系统组件间接口的一种测试 接口测试主要用于检测外部系统与系统之间以及内部各个子系统之间的交互点 测试的重点是要检查数据的交换&#xff0c;传递和控制管理过程&#xff0c;以及系统间的相互逻辑依赖关系 文章目录 HTTP接口 & Web Service接口RESTful接口…

ubuntu10.04 apache2.2开启tls1.2的支持,使现代的edge和firefox浏览器能正常访问https

最近发现自己ubuntu10.04服务器上的apache https无法通过win11上的edge和firefox浏览器访问&#xff0c;但xp下的ie6和ie8没有问题。 firefox的错误提示为“此网站可能不支持TLS 1.2协议,而这是Firefox支持的最低版本”。 经过检查发现&#xff1a; IE6访问https所需的版本是SS…

virtualBox镜像复制

镜像复制 有一个镜像后&#xff0c;图方便&#xff0c;想直接使用这个vdi文件&#xff0c;但vdi有个uuid值&#xff0c;同一个虚拟机中不能同时存在两个同样的uuid的介质的&#xff0c;普通的复制文件所得到的uuid是一样的 &#xff0c;所以需要用到自带的方法复制vdi文件&…

隐私计算实训营学习二:隐私计算开源如何助力数据要素流通

文章目录 一、数据要素流转与数据内外循环二、数据外循环中的信任焦虑三、数据要素流通对隐私计算的期望四、隐私计算开源助力数据要素流通 一、数据要素流转与数据内外循环 数据要素流转过程(从数据采集加工->到数据价值释放)&#xff1a; 链路主要包括采集、存储、加工、…