分享个C++日志记录类以及日志记录程序

日志Log程序(C++) 使用 C/C++ 代码实现一个日志程序,便于我们在调试程序的时候,打印出对应的日志信息,同时也可以重定向然后记录我们的日志信息。 阅读详情

前言

个人觉得开发中比较重要的一点就是“不要重复发明轮子”,现在国外、国内开源软件遍地开花,尤其是Google,开源了一系列性能、架构特别好的代码,如果能够用开源的应该尽量避免自己造轮子。那么为什么不用log4plus呢?在这里我需要的是一个简单实用、轻巧的日志记录程序,log4plus对我有点臃肿,所以才自己花店时间写了一个简单的日志记录类。

日志类实现

刚开始想的是为了避免大量的读写程序影响性能,可以保存一个大的缓存,每次写日志就往缓存中追加数据。缓存满了后,就把缓存数据写入文件中并清空缓存。这个优点很明显:I/O操作是很耗时的,写日志太过频繁,缺点:一旦日志程序异常退出,前面写的日志可能流失。那么,对应的另一种方法就是:每次写日志就打开文件写入数据,写完关闭文件,频繁操作I/O,对于性能没那么高要求的也还好。

然后就是多个线程间互斥的设置,在同一时间对一个文件操作的当然只能有一个文件。Windows上线程同步方法:临界区、事件、信号量,我这里用的是临界区。

日志记录程序实现

多个进程公用一个日志记录程序写日志,因此日志程序必须相当稳定,还要处理进程间同步的问题。我使用的是发送WM_COPYDATA消息,日志进程的消息循环会对所有进程的日志消息放入消息队列(先入先出),无需我们去处理互斥。消息循环接收到消息后,把日志数据拷贝到内存中,然后将这个数据发送到日志记录线程中。所有的日志都在一个单独的工作线程中写入文件,该线程也维护着自己的一个消息循环,处理窗口消息接收WM_COPYDATA后发送来的日志数据。同样的道理,消息循环已经为我们维护了先进先出的数据结构,我们不用考虑互斥加锁的问题,一个接着一个的写入文件就OK了。

日志类代码

/********************************************
*一个简单的日志记录类
*2015年7月28日
*Jelin
*mailto://jelinyao@163.com
*/
#pragma once
#include "../Lock.h"


//文件最大4M
static const int g_nMaxSize = 1024*1024*4;

class CLog
{
public:
	static CLog*	Instance()
	{
		static CLog log;
		return &log;
	}
	bool	WriteLog(const char* lpLog);
	bool	WriteLog(const wchar_t* lpLog);
	bool	WriteLog(const char* lpFile, long lLine, const char* lpLog);
	bool	WriteLog(const char* lpFile, long lLine, const wchar_t* lpLog);
	bool	WriteJoinLog(const wchar_t* lpText, const wchar_t* lpLog);
	bool	WriteJoinLog(const char* lpFile, long lLine, const wchar_t* lpText, const wchar_t* lpLog);

protected:
	CLog();
	~CLog();
	bool	InitLogFile();
	char*	WcharToChar(const wchar_t* lpSource);

private:
	wchar_t	m_szLog[MAX_PATH];
	int		m_nWriteSize;
	CLock	m_lock;
};

#define SLOG1(x)			CLog::Instance()->WriteLog(x);
#define SLOG2(x, y)			CLog::Instance()->WriteJoinLog(x, y);	
#define LOG1(x)				CLog::Instance()->WriteLog(__FILE__, __LINE__, x)
#define LOG2(x, y)			CLog::Instance()->WriteJoinLog(__FILE__, __LINE__, x, y)

#include "stdafx.h"
#include "Log.h"
#include <time.h>
#include <ShlObj.h>




static const char* g_lpEnd = "\n";

CLog::CLog()
	: m_nWriteSize(0)
{
	InitLogFile();
}

CLog::~CLog()
{

}

bool CLog::InitLogFile()
{
	wchar_t szPath[MAX_PATH]={0};
	::GetModuleFileName(NULL, szPath, MAX_PATH);
	for ( int i=wcslen(szPath)-1; i>=0; --i )
	{
		if ( szPath[i] == '\\' )
			break;
		szPath[i] = '\0';
	}
	wcscat(szPath, L"Log");
	SHCreateDirectory(NULL, szPath);
	SYSTEMTIME st;
	GetLocalTime(&st);
	swprintf(m_szLog, L"%s\\%d%02d%02d-%02d%02d%02d%03d.log", szPath, st.wYear, st.wMonth, st.wDay, st.wHour, st.wMinute, st.wSecond, st.wMilliseconds);
	m_nWriteSize = 0;
	return true;
}

bool CLog::WriteLog( const char* lpLog )
{
	if ( NULL == lpLog )
		return false;
	int nLen = strlen(lpLog);
	if ( 0 == nLen )
		return true;
	CScopeLock sl(m_lock);
	if ( m_nWriteSize>=g_nMaxSize )
		InitLogFile();
	FILE* fp = _wfopen(m_szLog, L"a+");
	if ( NULL == fp )
	{
		OutputDebugString(L"打开日志文件失败");
		return false;
	}
	SYSTEMTIME st;
	::GetLocalTime(&st);
	char szTime[30]={0};
	sprintf(szTime, "%d-%02d-%02d %02d:%02d:%02d\n", st.wYear, st.wMonth, st.wDay, st.wHour, st.wMinute, st.wSecond);
	fwrite(szTime, 1, strlen(szTime), fp);
	fwrite(lpLog, 1, nLen, fp);
	fwrite(g_lpEnd, 1, 1, fp);
	fclose(fp);
	m_nWriteSize += nLen+20;
	return true;
}

bool CLog::WriteLog( const wchar_t* lpLog )
{
	char* pBuffer = WcharToChar(lpLog);
	if ( NULL == pBuffer )
		return false;
	bool bRet = WriteLog(pBuffer);
	free(pBuffer);
	return bRet;
}

bool CLog::WriteLog( const char* lpFile, long lLine, const char* lpLog )
{
	if ( NULL == lpLog )
		return false;
	int nLen = strlen(lpLog);
	if ( 0 == nLen )
		return true;
	CScopeLock sl(m_lock);
	if ( m_nWriteSize>=g_nMaxSize )
		InitLogFile();
	FILE* fp = _wfopen(m_szLog, L"a+");
	if ( NULL == fp )
	{
		OutputDebugString(L"打开日志文件失败");
		return false;
	}
	SYSTEMTIME st;
	::GetLocalTime(&st);
	char szTime[30]={0};
	sprintf(szTime, "%d-%02d-%02d %02d:%02d:%02d\n", st.wYear, st.wMonth, st.wDay, st.wHour, st.wMinute, st.wSecond);
	fwrite(szTime, 1, strlen(szTime), fp);
	if ( NULL != lpFile )
		fwrite(lpFile, 1, strlen(lpFile), fp);
	char szLine[30];
	sprintf_s(szLine, " line=%ld:", lLine);
	fwrite(szLine, 1, strlen(szLine), fp);
	fwrite(lpLog, 1, nLen, fp);
	fwrite(g_lpEnd, 1, 1, fp);
	fclose(fp);
	m_nWriteSize += nLen+20;
	return true;
}

bool CLog::WriteLog( const char* lpFile, long lLine, const wchar_t* lpLog )
{
	char* lpBuffer = WcharToChar(lpLog);
	if ( NULL == lpBuffer )
		return false;
	bool bRet = WriteLog(lpFile, lLine, lpBuffer);
	free(lpBuffer);
	return bRet;
}

char* CLog::WcharToChar( const wchar_t* lpSource )
{
	if ( NULL == lpSource )
		return NULL;
	int nLen = wcslen(lpSource);
	if ( 0 == nLen )
		return NULL;
	int nNeedSize = WideCharToMultiByte(CP_ACP, 0, lpSource, nLen, NULL, 0, NULL, NULL);
	if ( 0 == nNeedSize )
		return NULL;
	char* pBuffer = (char*)malloc(sizeof(char)*(nNeedSize+1));
	if ( NULL == pBuffer )
		return NULL;
	WideCharToMultiByte(CP_ACP, 0, lpSource, nLen, pBuffer, nNeedSize, NULL, NULL);
	pBuffer[nNeedSize] = '\0';
	return pBuffer;
}

bool CLog::WriteJoinLog( const wchar_t* lpText, const wchar_t* lpLog )
{
	if ( NULL == lpText || NULL == lpLog )
		return false;
	int nTextLen = wcslen(lpText);
	int nLogLen = wcslen(lpLog);
	wchar_t* lpBuffer = (wchar_t*)malloc(sizeof(wchar_t)*(nTextLen+nLogLen+1));
	wcscpy(lpBuffer, lpText);
	wcscpy(lpBuffer+nTextLen, lpLog);
	lpBuffer[nTextLen+nLogLen] = '\0';
	bool bRet = WriteLog(lpBuffer);
	free(lpBuffer);
	return bRet;
}

bool CLog::WriteJoinLog( const char* lpFile, long lLine, const wchar_t* lpText, const wchar_t* lpLog )
{
	if ( NULL == lpFile || NULL == lpText || NULL == lpLog )
		return false;
	int nTextLen = wcslen(lpText);
	int nLogLen = wcslen(lpLog);
	wchar_t* lpBuffer = (wchar_t*)malloc(sizeof(wchar_t)*(nTextLen+nLogLen+1));
	wcscpy(lpBuffer, lpText);
	wcscpy(lpBuffer+nTextLen, lpLog);
	lpBuffer[nTextLen+nLogLen] = '\0';
	bool bRet = WriteLog(lpFile, lLine, lpBuffer);
	free(lpBuffer);
	return bRet;
}
注意,文件操作最好只在一个函数里处理,便于统一管理(加锁、解锁)。

日志程序代码(win32程序)

// Logger.cpp : 定义应用程序的入口点。
//

#include "stdafx.h"
#include "Logger.h"
#include <atlstr.h>


enum{
	WM_MSG_LOGGER = WM_USER + 200,
};



// 全局变量:
HINSTANCE hInst;
HWND	g_hMainWnd = NULL;
HWND	g_hServerWnd = NULL;
HANDLE	g_hLogTh = NULL;
UINT	g_dwLogTid = 0;

// 此代码模块中包含的函数的前向声明:
HWND				InitInstance(HINSTANCE);
LRESULT CALLBACK	WndProc(HWND, UINT, WPARAM, LPARAM);
UINT __stdcall		LogThread(void* lpParam);

int APIENTRY _tWinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPTSTR lpCmdLine, int nCmdShow)
{
	try
	{
		UNREFERENCED_PARAMETER(hPrevInstance);
		UNREFERENCED_PARAMETER(lpCmdLine);
		if ( wcslen(lpCmdLine) == 0 )
		{
			TRACEW(L"参数为空\n");
			return 0;
		}
		wstring strCmd(lpCmdLine);
		string strJson = U2A(lpCmdLine);
		Json::Reader r;
		Json::Value vRoot;
		if ( !r.parse(strJson, vRoot) )
		{
			TRACEW(L"参数格式不正确,解析失败\n");
			return 0;
		}
		g_hServerWnd = (HWND)vRoot["wnd"].asInt();
		g_hMainWnd = InitInstance (hInstance);
		if ( NULL == g_hMainWnd )
		{
			TRACEW(L"创建消息窗口失败\n");
			return 0;
		}
		MSG msg;
		while (GetMessage(&msg, NULL, 0, 0))
		{
			TranslateMessage(&msg);
			DispatchMessage(&msg);
		}
	}
	catch(std::exception e)
	{
		TRACEA("捕获到异常信息:%s\n", e.what());
	}
	catch(...)
	{
		TRACEW(L"捕获到未知异常");
	}
	return 0;
}

HWND InitInstance(HINSTANCE hInstance)
{
	static const wchar_t kWndClass[] = L"ClientMessageWindow";
	WNDCLASSEX wc = {0};
	wc.cbSize = sizeof(wc);
	wc.lpfnWndProc = WndProc;
	wc.hInstance = hInstance;
	wc.lpszClassName = kWndClass;
	RegisterClassEx(&wc);
	return CreateWindow(kWndClass, 0, 0, 0, 0, 0, 0, HWND_MESSAGE, 0, hInstance, 0);
}

LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
{
	switch (message)
	{
	case WM_CREATE:
		{
			//创建日志记录线程
			g_hLogTh = (HANDLE)_beginthreadex(NULL, 0, LogThread, NULL, 0, &g_dwLogTid);
			//通知发送方,日志记录进程的主窗口句柄
			::PostMessage(g_hServerWnd, WM_MSG_LOGGER, 0, (LPARAM)hWnd);
			//设置定时器,检测发送进程的状态
			::SetTimer(hWnd, 1, 3000, NULL);
			break;
		}
	case WM_DESTROY:
		PostQuitMessage(0);
		break;
	case WM_COPYDATA:
		{
			COPYDATASTRUCT* lpData = (COPYDATASTRUCT*)lParam;
			/*收到日志消息后,我们需要立即处理返回,因为发送方还在阻塞等待
			/*于是拷贝数据到内存中,将这个内存的地址投递到写日志的线程队列中去
			/*这里会频繁的 申请\释放内存,于是用到了tcmalloc,提高性能
			*/
			char* pLog = (char*)malloc(lpData->cbData+1);
			memcpy(pLog, lpData->lpData, lpData->cbData);
			pLog[lpData->cbData] = '\0';
			::PostThreadMessage(g_dwLogTid, WM_MSG_LOGGER, 0, (LPARAM)pLog);
			break;
		}
	case WM_TIMER:
		{
			//检测发送方窗口是否存在,不存在说明发送日志进程已经退出
			if ( !IsWindow(g_hServerWnd) )
			{
				::KillTimer(hWnd, 1);
				//先退出日志线程
				::PostThreadMessage(g_dwLogTid, WM_QUIT, 0, 0);
				WaitForSingleObject(g_hLogTh, 5*1000);
				CloseHandle(g_hLogTh);
				//关闭主窗口,退出进程
				::PostMessage(hWnd, WM_CLOSE, 0, 0);
			}
			break;
		}
	default:
		return DefWindowProc(hWnd, message, wParam, lParam);
	}
	return 0;
}

//日志记录线程
UINT __stdcall LogThread(void* lpParam)
{
	MSG msg;
	while(GetMessage(&msg, NULL, 0, 0))
	{
		if ( msg.message == WM_MSG_LOGGER )
		{//由于消息队列替我们维护了先进先出的模型,这里写日志时无需加锁
			char* pLog = (char*)msg.lParam;
			SLOG1(pLog);
			free(pLog);
		}
	}
	return 0;
}
每次写入文件都会记录下写入大小,写入大小达到设定的最大值后,程序就会自动再创建一个日志文件。

还有一个

Lock.h
是一个简单的封装类,封装了下临界区的初始化,加锁解锁操作,使用十分方便。

#pragma once
class CLock
{
public:
	CLock(void)		{ ::InitializeCriticalSection(&m_cs);	}
	~CLock(void)	{ ::DeleteCriticalSection(&m_cs);		}
	void Lock()		{ ::EnterCriticalSection(&m_cs);		}
	void UnLock()	{ ::LeaveCriticalSection(&m_cs);		}
private:
	CRITICAL_SECTION	m_cs;
};

class CScopeLock
{
public:
	CScopeLock(CLock& lock)
		:m_lock(lock)
	{
		m_lock.Lock();
	}
	~CScopeLock()
	{
		m_lock.UnLock();
	}
private:
	CLock& m_lock;
};

如何使用

#define SLOG1(x)			CLog::Instance()->WriteLog(x);
#define SLOG2(x, y)			CLog::Instance()->WriteJoinLog(x, y);	
#define LOG1(x)				CLog::Instance()->WriteLog(__FILE__, __LINE__, x)
#define LOG2(x, y)			CLog::Instance()->WriteJoinLog(__FILE__, __LINE__, x, y)
简单的日志记录,不需要打印文件名和代码行:

SLOG1(L"日志第一行");

SLOG1("日志第二行");

SLOG2("日志第三行", "第三行补充文字") 用于简单文字拼接

需要打印文件名和代码行的:

LOG1("第四行日志")

LOG2("第五行日志", "补充说明")

支持ASCII和UNICODE,当然了,UNICODE内部需要转码,如果可以选择当然还是用ASCII效率更高。







分享一个简易的程序日志记录C++类 最近一直在忙实验室的项目开发,很少上博客了,现在总算有一定时间空出来做些总结,今天起陆续更新博客,总结一下半年多来的学习心得。 今天先分享一个简易的程序日志记录类,参考自《一个简单又高效的日志系统》,针对项目本身做了一定的修改和补充,编程水平有限,仅供参考,呵呵 。 头文件: #ifndef _INFO_LOGGER_H_ #define _INFO_LOGGER_H_ /** 阅读详情

相关推荐

NX12.0标准C++异常日志记录完整示例

在NX12.0开发过程中,当捕获到标准C++异常时,如何正确记录异常日志是关键。通过完善的try-catch机制与日志系统结合,能有效提升调试效率与稳定性,尤其针对nx12.0捕获到标准c++异常怎么办提供了实用解决方案。

weixin_33660045的博客 1103

C++日志类封装(超级干货)

写程序通常用要输出日志,以便程序修复BUG和查看程序运行情况。下面是C++语言封装的一个日志类,使用的C语言下的通用文件操作函数,因此可以用于window,linux等平台下开发。

软件人生 2135

C++机器人开发:yaml-cpp配置文件实战与ROS参数管理

在软件工程中,配置文件是实现参数化设计、提升系统可维护性的核心机制。其原理是将易变的运行参数从固定的程序逻辑中解耦,通过外部文件进行管理,从而实现无需重新编译即可调整程序行为的技术价值。这一设计在机器人开发领域尤为重要,因为机器人系统通常涉及大量需要现场调试的参数,如PID控制系数、传感器标定数据和导航算法阈值。通过采用人类可读性强的YAML格式,结合成熟的C++解析库yaml-cpp,开发者可以构建出支持动态重载、分层管理的健壮配置系统。本文以机器人开发为应用场景,深入探讨了如何利用yaml-cpp进行高

weixin_34289454的博客 410

C++的日志类

前一个项目中,当时遇到日志的问题,因为时间紧迫,用了开源社区的glog。但本人对开源的东西又或者说不是自己写的东西的总有点不放心,也可以说是心理有点疾病吧。于是今天就趁着闲下来,自己写了个C++的日志类,机制是仿照自己之前写的C#log类。        日志类所要解决的问题:        1. 自检查目录以及文件的存在性,如果不存在,则创建        2. 多线程冲突问题

linshixina的专栏 1379

C++日志类libglog使用

glog简介  Google glog是一个基于程序级记录日志信息的c++库,编程使用方式与c++的stream操作类似,例:        LOG(INFO) “LOG”宏为日志输出关键字,“INFO”为严重性程度。 主要支持功能: 1, 参数设置,以命令行参数的方式设置标志参数来控制日志记录行为; 2, 严重性分级,根据日志严重性分级记录日志; 3,

sunflover454的专栏 3896

一文详解 C++ 日志框架

日志框架 - 一个经过专门设计的实用程序,用于规范应用程序的日志记录过程。

青春不老,奋斗不止! 8922

C++实现一个简洁而又强大的日志记录类

代码执行时记录日志是很有必要的,尤其在涉及到多进程、多线程调试或者是调试服务程序时。本分分享一个最近写的简单的日志类,希望有所帮助。不喜欢废话,上代码了: 头文件如下: #pragma once #include &lt;string&gt; using std::string; using std::wstring; class CSimpleLog { public: CSimple...

https://github.com/JelinYao 1万+

C++程序发生闪退且没生成dump文件问题的排查经验总结与分享

本文详细讲述了C++程序发生闪退且没有生成dump这类问题的场景及排查方法,并给出了相关实战问题分析实例,有一定的实战参考价值。

dvlinker的技术专栏 2万+

c++日志记录模块

C++ 日志记录模块 该模块从实际项目中产生,通过extern声明的方式,可在代码不同模块中生成日志,日志文件名称为随机码加用户指定名称,采用随机码是为了避免日志文件可能被覆盖的问题。 愿意的话你也能自己构建个人的日志记录模块,本次分享的模块实现方法比较简单,可能有些地方没考虑清楚。 源码: // // Created by jerry on 2/12/16. // #include ...

accvrzq796455的博客 212

53、C++程序的调试方法

本文详细介绍了C++程序的调试方法,包括常用的调试工具如GDB和Visual Studio调试器的使用方法,以及调试技术、常见调试策略和最佳实践。同时,文章还探讨了多线程程序的调试技巧和日志记录的重要性,并通过实际案例帮助读者掌握调试技巧,提高代码质量。

t8u9v0的博客 86

掌握C++类的构造与日志记录

本文通过详细的代码示例和解释,介绍了如何在C++中创建类,并实现构造函数、析构函数、以及自定义构造函数。同时,我们还探讨了如何利用成员函数实现日志记录器,记录信息到控制台和文件。通过对代码的分块处理,如将类声明放在头文件中,定义放在源文件中,以及封装类代码到命名空间中,我们学会了如何在C++项目中保持代码的组织性和可读性。

weixin_42611310的博客 208

分享一个log4cpp的配置文件

################################################################ #1、log4cpp.可以省略 #2、等号左右两侧的名称大小写敏感 #3、rootAppender是随便取的名字 #4、Win32DebugAppender等都是log4cpp类库里的类(class) #5、日志输出的开启级别(DEBUG,INFO,WARM,ERROR

lslxdx的专栏 3391

C++高效日志类(库)

前段时间在做毕业设计需要有一个功能模块是写日志,为了配合高性能服务器不拖延服务器的效率所以必须要写出一个十分高效确不太占用系统资源的日志类。 经过参考前辈们写的资料加上自己不断地研究改进,现在完成了一个C++高效日志的类。经过我本人的测试发现该类库可以在WINDOWS平台、多线程环境下出色完成写日志的任务,可以达到50M/S的日志输出量。同时做了日志切割,当日志文件大小达到设定值时自动切入下一个文件,并用日期加序列号命名。 其中用到了BOOST多线程的锁,没有安装BOOST的机器可以屏蔽BOOST的相关代码,但是在多线程执行时会出错。

【亲测免费】 C++11 日志库cpp11logger实战指南

C++11 日志库cpp11logger实战指南 项目介绍 cpp11logger是一款专为C++11设计的轻量级日志库,它提供了现代C++特性以简化日志管理过程。此库旨在提供高效、灵活的日志记录解决方案,适用于从简单的控制台应用到复杂的分布式系统。通过利用C++11的标准化特性和线程局部存储(如thread_local),cpp11logger确保了在多线程环境中的安全与便捷。 项目快速启动 安...

gitblog_00792的博客 488

实验室LIMS系统选型指南:从赛默飞到金现代,5大主流方案横评

本文深入分析了实验室LIMS系统选型的5大核心维度,包括架构选择、行业适配性、合规性设计、智能化程度和成本模型。通过对比赛默飞、金现代等主流厂商的优劣势,提供实战避坑策略,帮助实验室管理者做出明智决策,提升运营效率和合规水平。

plum99的博客 257

渲染TA实战:冰面效果制作分享

这里画一张图:我们可以看到,如果深度图的范围没有进过缩放,那就是0-1,但是归一化的-viewDir的向量长度为1,如果-viewDir不和表面垂直(这是绝大多数的情况),那么viewDir.z的分量就是一个不定值,我们不好确定需要走多少步,viewDir才能和底面相交。然后我们需要冰面的Alpha图:因为冰是透明物体,所以相对于Albedo,我们更需要的是一张冰面裂纹的蒙版图,这张图需要记录冰面的裂纹走向和深浅、冰封的气泡、以及因为压力或气体等原因产生的类似雾的效果。

qq_41166022的博客 1025

ChatLLM.cpp安全最佳实践:保护你的AI应用免受攻击

ChatLLM.cpp是一款纯C++实现的实时聊天模型应用,支持在个人计算机上运行多种AI模型(CPU和GPU)。随着AI应用的普及,安全防护变得至关重要。本文将分享保护ChatLLM.cpp应用免受常见攻击的关键策略,帮助开发者和用户构建更安全的AI交互环境。 ## 1. 输入验证与过滤:第一道安全防线 AI应用最常见的安全风险之一是恶意输入攻击。攻击者可能通过精心设计的输入触发模型异常行为

gitblog_00891的博客 813

保姆级教程:用CH34xSerCfg工具修改USB转串口芯片的VID/PID,避免驱动冲突

本文详细介绍了如何使用CH34xSerCfg工具修改USB转串口芯片的VID/PID,解决驱动冲突问题。通过实战案例和配置技巧,帮助开发者高效管理嵌入式设备,避免常见的USB设备识别问题,提升开发效率。

weixin_30924239的博客 224

Log4cpp log4cpp快速使用指南

Log4cpp log4cpp快速使用指南

ytffhew的博客 624

基于PointNet++网络的点云数据分类仿真分析

局部特征学习模块用于学习点云中每个点的局部特征表示,而全局特征学习模块用于学习整个点云的全局特征表示。点云数据分类是指将点云数据分为不同的类别或标签。PointNet++是一种用于点云数据处理的深度学习网络,它能够有效地学习点云数据的特征表示并实现高精度的分类。在本文中,我们将使用MATLAB实现基于PointNet++网络的点云数据分类仿真分析,并提供相应的源代码。然后,我们使用splitEachLabel函数将数据划分为训练集和测试集,其中80%的数据用于训练,20%的数据用于测试。

HackDashX的博客 210

组态王与SqlServer数据库交互总结

组态王可以与SqlServer、mysql、acess、oracle等数据库进行数据交互。 本文重点梳理了如何把组态王中的数据保存到SqlServer数据库中,并使用ADO控件对数据库中的数据进行查询。 一、数据存到SQL数据库 1.新建记录体 2、创建表格模板(用于创建数据库中的表格) 3、打开sqlserver,新建数据库 4、创建ODBC数据源 选择自己新建的数据库 5.使用SQLConnect函数连接数据库,SQLCraetTable函数用于在数据库中创建表格 6.使用

逍遥子的博客 2万+

分享一个不使用配置文件的log4cpp的例程

#include "log4cpp/Category.hh" #include "log4cpp/OstreamAppender.hh" #include "log4cpp/FileAppender.hh" #include "log4cpp/Win32DebugAppender.hh" #include "log4cpp/PatternLayout.hh" using namespace lo

lslxdx的专栏 1373

【AutoSAR】【WdgM】WdgM安全管理模块

一、WdgM在AutoSAR里的整体架构 WdgM是在autosar架构里位于服务层的一个基础软件模块;主要监督程序的执行; 主要包括三个模块: 看门狗管理器(WdgM) 看门狗接口(WdgIf) 看门狗驱动(Wdg) 二、三种监督机制 Alive监控-------主要监控周期性软件程序的时间 DeadLine监控-------主要监控非周期软件程序 程序流监控-------主要监控程序流执行的正确性 三、基于全局状态的检测流程 受监管实体(SE):Supervised Ent

专注汽车软件开发、AutoSAR、车载以太网、SOA、EE架构。 2990
上一篇: 使用开源C++项目WebSocketpp构建web服务器
下一篇: Linux高性能服务器编程 第八章(高性能服务器程序框架)
疯狂-的-蜗牛
博客等级 码龄16年 427粉丝 104原创
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

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

抵扣说明:

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

余额充值