C++类设计:一个不同版本的日志类(完整源码)

初级代码游戏的专栏介绍与文章目录-CSDN博客

我的github:codetoys,所有代码都将会位于ctfc库中。已经放入库中我会指出在库中的位置。

这些代码大部分以Linux为目标但部分代码是纯C++的,可以在任何平台上使用。

 


        如何设计日志类请看:C++类设计:设计一个日志类(源码)_初级代码游戏的博客-CSDN博客        

        这个版本有些不同,支持输出到编辑框、文件和VS的输出窗口,基本结构完全相同。

目录

一、效果

二、完整源码

三、详解

3.1 输出到VS输出窗口

3.2 输出到编辑框

3.3 输出到文件


一、效果

        VS2017,MFC,UNICODE字符集。

        输出到编辑框:

        自动向下滚动,编辑框必须是多行的。

         输出到VS输出窗口(调试模式):

二、完整源码

        头文件:

//common.h

#pragma once
#include <sstream>
#include <map>
using namespace std;

extern CEdit* pGlobalLogWnd;

class CStringConvert
{
public:
	static string WStringToString(wstring const& wstr)
	{
		size_t buflen = wstr.size() * sizeof(wchar_t) + 1;
		char* buf = new char[buflen];
		WideCharToMultiByte(CP_ACP, 0, wstr.c_str(), -1, buf, (int)buflen, NULL, 0);
		string ret = buf;
		delete[] buf;
		return ret;
	}
	static wstring StringToWString(string const& str)
	{
		//返回接受字符串所需缓冲区的大小,已经包含字符结尾符'\0'
		int iSize = MultiByteToWideChar(CP_ACP, 0, str.c_str(), -1, NULL, 0);
		wchar_t* buf = new wchar_t[iSize * sizeof(wchar_t)];
		MultiByteToWideChar(CP_ACP, 0, str.c_str(), -1, buf, iSize);
		wstring ret = buf;
		delete[] buf;
		return ret;
	}
};

class CLog
{
private:
	HANDLE hFile = 0;//输出文件

	stringstream m_buf;//一条信息
	CString m_file;//产生日志的文件名
	int m_line;//产生日志的行号

	int m_limit_text = UINT_MAX;//最大长度,编辑框的默认值大概是几十K,需要设置得更大
	
	void end(char const* logtype)
	{
		CString str;

		//格式化信息
		str += logtype;
		str += ":";
		str += m_buf.str().c_str();
		if (m_file.GetLength() > 0)
		{
			CString line;
			line.Format(TEXT("%d"), m_line);
			str += TEXT(" [From ") + m_file + TEXT(":") + line + TEXT("]");
		}
		str += "\r\n";

		//输出到VS信息窗口
		OutputDebugString(str);

		//输出到文件
		if (hFile)
		{
			DWORD dwCount;
			string mbcs_string = CStringConvert::WStringToString(str.GetString());
			WriteFile(hFile, mbcs_string.c_str(), (DWORD)mbcs_string.size(), &dwCount, 0);
		}
		
		//输出到编辑框
		if (pGlobalLogWnd && ::IsWindow(pGlobalLogWnd->m_hWnd))
		{
			pGlobalLogWnd->SetLimitText(m_limit_text);
			while (pGlobalLogWnd->GetWindowTextLength() >= m_limit_text)
			{
				//pGlobalLogWnd->SetSel(0, mag_size.begin()->second + 2);
				//pGlobalLogWnd->ReplaceSel(TEXT(""));
				//mag_size.erase(mag_size.begin());
				pGlobalLogWnd->SetWindowText(TEXT(""));
			}

			int nLength = pGlobalLogWnd->GetWindowTextLength();
			pGlobalLogWnd->SetSel(nLength, nLength);
			pGlobalLogWnd->ReplaceSel(str);
			pGlobalLogWnd->LineScroll(pGlobalLogWnd->GetLineCount());
		}
		else
		{
			OutputDebugString(TEXT("日志窗口失效\r\n"));
		}
		
		//清除缓存的信息
		m_buf.str("");
		m_file = "";
		m_line = 0;
	}
public:
	CLog()
	{
		wchar_t pszPathName[2048];
		GetModuleFileName(NULL, pszPathName, 2048);
		StrCat(pszPathName, TEXT(".log"));
		hFile = CreateFile(pszPathName, GENERIC_WRITE, FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE, NULL
			, OPEN_ALWAYS, FILE_ATTRIBUTE_NORMAL, 0);
		if (INVALID_HANDLE_VALUE == hFile)
		{
			MessageBox(NULL,TEXT("创建或打开日志文件失败"),TEXT("出错"),0);
		}
	}
	~CLog()
	{
		if (hFile)CloseHandle(hFile);
	}
	void set_LimitText(int n) { m_limit_text = n; }
	struct CLog_ende
	{};
	struct CLog_endi
	{};
	struct CLog_endd
	{};
	CLog& SetPos(char const * file, int line)
	{
		m_file = file;
		m_line = line;
		return *this;
	}
	template<typename T>
	CLog& operator<<(T const& data)
	{
		m_buf << data;
		return *this;
	}
	CLog& operator<<(CLog_ende const& data)
	{
		end("错误");
		return *this;
	}
	CLog& operator<<(CLog_endi const& data)
	{
		end("信息");
		return *this;
	}
	CLog& operator<<(CLog_endd const& data)
	{
		end("调试");
		return *this;
	}
	CLog& operator<<(CString const& data)
	{
		return operator<<(data.GetString());
	}
	CLog& operator<<(wchar_t const* data)
	{
		return operator<<(CStringConvert::WStringToString(wstring(data)));
	}
	CLog& operator<<(RECT const& data)
	{
		return *this << "{" << data.left << "," << data.top << "," << data.right << "," << data.bottom << "}";
	}
};

extern bool G_IS_DEBUG;
extern CLog gloablLog;
#define thelog (gloablLog.SetPos( __FILE__ , __LINE__))
#define debuglog if(G_IS_DEBUG)gloablLog.SetPos( __FILE__ , __LINE__)
extern CLog::CLog_ende ende;
extern CLog::CLog_endi endi;
extern CLog::CLog_endd endd;

        源文件:

#include "pch.h"
#include "common.h"

CEdit* pGlobalLogWnd = nullptr;

bool G_IS_DEBUG = false;
CLog gloablLog;
CLog::CLog_ende ende;
CLog::CLog_endi endi;
CLog::CLog_endd endd;

        使用:

	//设置编辑框最大长度
    gloablLog.set_LimitText(1024 * 1024);
    //设置编辑框指针,不设置则不输出到编辑框
	pGlobalLogWnd = &this->m_LogWnd;
    //打开调试开关,否则debuglog不输出
	G_IS_DEBUG = true;

    //输入出日志:
    thelog<<a<<b<<c<<endi;
    thelog<<a<<b<<c<<ende;
    thelog<<a<<b<<c<<endd;
    debuglog<<a<<b<<c<<endi;
    debuglog<<a<<b<<c<<ende;
    debuglog<<a<<b<<c<<endd;

三、详解

3.1 输出到VS输出窗口

        在调试模式下可以直接输出到VS的输出窗口,使用如下函数即可:

debugapi.h
Kernel32.lib/Kernel32.dll

void OutputDebugStringA(
  [in, optional] LPCSTR lpOutputString
);

        不过如果不是调试模式就没用了。

3.2 输出到编辑框

        图形界面程序完全可以自带日志窗口,这里用了一个编辑框CEdit。用作日志的编辑框需要做一些设置:

  • 多行
  • 滚动条
  • 最大字符数大一些
  • 自动滚动

        前两个在窗体设计器设置即可,后两个靠代码:

		//输出到编辑框
		if (pGlobalLogWnd && ::IsWindow(pGlobalLogWnd->m_hWnd))
		{
			pGlobalLogWnd->SetLimitText(m_limit_text);
			while (pGlobalLogWnd->GetWindowTextLength() >= m_limit_text)
			{
				//pGlobalLogWnd->SetSel(0, mag_size.begin()->second + 2);
				//pGlobalLogWnd->ReplaceSel(TEXT(""));
				//mag_size.erase(mag_size.begin());
				pGlobalLogWnd->SetWindowText(TEXT(""));
			}

			int nLength = pGlobalLogWnd->GetWindowTextLength();
			pGlobalLogWnd->SetSel(nLength, nLength);
			pGlobalLogWnd->ReplaceSel(str);
			pGlobalLogWnd->LineScroll(pGlobalLogWnd->GetLineCount());
		}

        编辑框的修改要用ReplaceSel,不能用SetWindowText,会严重闪烁。

3.3 输出到文件

        输出到文件没什么好解释了,要注意的就是输出文件的编码,尽量用utf-8,兼容性比较好。如果输出宽字符,用记事本打开可能乱码(或许是没有添加BOM的原因,总之utf-8最简单啦)。


(这里是结束)

  • 9
    点赞
  • 14
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
### 回答1: 11-tie源码一个用C语言实现的简单且高效的哈希表结构,可以用来实现键-值对的存储和查找。以下是对11-tie源码的详细解释。 11-tie源码主要由三个关键部分组成:哈希表结构、哈希函数和碰撞解决方法。 首先是哈希表结构。11-tie源码中使用了一个固定大小的数组作为哈希表来存储键-值对。数组的大小由用户在创建哈希表时指定,并具有较好的素数特性,以减少碰撞的发生。哈希表中的每个元素(bucket)是一个指向键-值对链表的指针。如果出现碰撞,新的键-值对将被添加到链表的头部。 然后是哈希函数。11-tie源码中使用了一个简单且高效的哈希函数,它会根据键的特征将其映射到数组的索引位置。哈希函数使得不同的键被均匀地分布在数组中,从而减少碰撞的发生。该哈希函数通常基于键的型和特性,但也可以根据特定需求进行自定义。 最后是碰撞解决方法。当多个键映射到数组的同一个索引位置时,就会发生碰撞。11-tie源码中使用了链表来解决碰撞问题。当发生碰撞时,新的键-值对将被添加到链表的头部。这种解决方法简单且有效,但当哈希表中的元素数量较大时,链表的遍历会导致性能下降。 总结起来,11-tie源码一个使用C语言实现的简单高效的哈希表结构。通过哈希函数将键映射到数组的索引位置,使用链表解决碰撞问题。这种结构可以用来存储和查找键-值对,适用于快速查询和插入数据的场景。 ### 回答2: c 11 tie 源码详解是指对 C++ 11 中的 `std::tie` 函数进行解析。`std::tie` 是一个模板函数,用于将多个值绑定到一个元组中。 `std::tie` 的源码实现如下: ```cpp namespace std { template <typename... Types> tuple<Types&...> tie(Types&... args) noexcept { return tuple<Types&...>(args...); } } ``` `std::tie` 函数是一个模板函数,接受任意数量的参数,并将这些参数作为引用传递给 `std::tuple`,然后返回这个 `std::tuple`。 `std::tuple` 是一个模板,用于保存一组不同型的值。`std::tuple<Types&...>` 的含义是保存参数 Types&... 的引用。 利用 `std::tie` 函数,可以将多个变量绑定到一个 `std::tuple` 中,并且可以通过解构绑定的方式获取这些变量。 例如,假设有两个变量 `int a` 和 `double b`,可以使用 `std::tie` 将它们绑定到一个元组中,并通过解构绑定方式获取它们的值: ```cpp int a = 1; double b = 2.0; std::tuple<int&, double&> t = std::tie(a, b); std::get<0>(t) = 10; std::get<1>(t) = 20.0; std::cout << a << ", " << b << std::endl; ``` 在上面的代码中,通过 `std::tie(a, b)` 将变量 `a` 和 `b` 绑定到一个元组 `t` 中,然后通过 `std::get<0>(t)` 和 `std::get<1>(t)` 获取元组中第一个和第二个值,并将它们分别赋值为 10 和 20.0。最后输出结果为 `10, 20`。 `std::tie` 的源码实现简单明了,通过将多个参数作为引用传递给 `std::tuple`,实现了将多个变量绑定到一个元组中的功能。这个功能在一些情况下非常方便,可以减少代码的复杂性和重复性。 ### 回答3: c 11 tie 是 C++ 11 标准中新增的一个标准库函数,用于将多个输出流(ostream)绑定到一个流对象上。通过将多个输出流绑定在一起,可以在输出时同时向多个流对象输出数据,提高代码的易读性和简洁性。 使用 c 11 tie 首先需要包含 `<tuple>` 头文件,并且可以接受任意个数的流对象作为参数。例如 `std::tie(stream1, stream2)` 表示将 stream1 和 stream2 绑定在一起。 在绑定之后,输出到绑定对象的数据会自动发送到所有绑定的流对象中。例如 `std::cout << "Hello World";`,如果之前使用 `std::tie(std::cout, fileStream)` 进行了绑定,那么输出的 "Hello World" 既会在控制台上显示,也会同时写入到文件流对象中,实现了同时输出到两个流对象的效果。 需要注意的是,绑定只在绑定操作发生时生效,之后对流对象的修改不会影响绑定。因此,如果在绑定之后修改了流对象,需要重新进行绑定操作。 c 11 tie 的使用可以简化代码,提高开发效率。通过同时输出到多个流对象,可以实现在不同目的地同时记录相同的输出信息,提供了一种方便的日志记录功能。此外,绑定的流对象可以是任意的输出流,不限于标准输出流和文件流,也可以是用户自定义的流对象。 总结来说,c 11 tie 是 C++ 11 标准中新增的一个标准库函数,用于将多个输出流绑定在一个流对象上,实现同时输出到多个流对象的功能。它提高了代码的可读性和简洁性,并且可以应用于日志记录等多种场景。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值