用C++实现一个Log系统

提要

最近在写一些C++的图形代码,在调试和测试过程中都会需要在终端打印一些信息出来。之前的做法是直接用

std::cout<<"Some Word"<<std::endl;

这样做其实非常的麻烦,每次都要打很多的字母还有特殊符号,除去我要打印的内容,还需要按下28下键盘,简直不能忍!

参考Unity里面的打log的方式

Debug.Log("Some Word");

或者Qt中的处理方式

qDebug() << "Some Word";

这两种都方便太多。

今天要实现的Log系统需要满足的特性有:

1.很方便地在终端打印各种类型数据信息;

2.可以区分Log等级;

3.打印信息的同时能够提供打印语句的文件,函数名,行号


类说明

简单地画了下UML,主要分为下面几个类


简单说一下类的作用

MessageLogContext

记录Log的上下文,也就是Log处在的文件,函数名,行号。

MessageLogger

主要的Log类,提供了上次调用的一些接口,注意一下这个宏比较有意思

qDebug MessageLogger(__FILE__, __FUNCTION__, __LINE__).debug

这样当使用

qDebug()

的时候,

宏替换就直接转换成了MessageLogger的构造函数

MessageLogger(__FILE__, __FUNCTION__, __LINE__).debug()

等于是先构造MessageLogger,然后调用这个对象的debug()方法。


Debug

具体处理Debug信息的类。

用了一个内部Stream结构体来记录Debug信息,记得在使用前要new,析构的时候delete掉。

重构了很多的<<方法,就是为了能处理多种数据类型,包括自定义的类。还可以通过模板来打印stl里面的东西。

LogToConsole是将log信息打印到终端的函数,在析构函数中会被调用。如果想要实现更加炫酷的打印log方式(各种颜色),扩展这个函数就好了。


整个Log的流程如下图


测试代码

void DebugTest()
{
	Vector2 v = Vector2(1, 1);
	Vector2 v2 = Vector2(2, 1);
	Vector3 v3 = Vector3(0, 2, 1);
	Vector3 v4 = Vector3(0, 2, 1);
	Vector3 v5 = Vector3(23, 112, 22);
	Vector3 v6 = Vector3(23, 112, 22);
	std::vector<Vector3> vec;
	vec.push_back(v3);
	vec.push_back(v4);
	vec.push_back(v5);
	vec.push_back(v6);
	vec.push_back(v6);
	vec.push_back(v6);
	vec.push_back(v6);
	vec.push_back(v6);
	std::string testStr = "vector Test";
	qDebug() << "Hello Debug";
	qDebug() <<""<< v << v2<< v3;
	qDebug() << v3;
	qWarning() << vec;
}

运行结果





代码清单

MessageLogContext.h
#pragma once
#include <string>

class MessageLogContext
{
public:
	MessageLogContext() : line(0), file(0), function(0) {}
	MessageLogContext(const char *fileName, const char *functionName, int lineNumber)
		: file(fileName), function(functionName), line(lineNumber) {}

	int line;
	const char *file;
	const char *function;
	void copy(const MessageLogContext &logContext)
	{
		this->file = logContext.file;
		this->line = logContext.line;
		this->function = logContext.function;
	}

private:
	friend class MessageLogger;
	friend class Debug;
};


Log.h
#pragma once
#define qDebug MessageLogger(__FILE__, __FUNCTION__, __LINE__).debug
#define qInfo MessageLogger(__FILE__, __FUNCTION__, __LINE__).info
#define qWarning MessageLogger(__FILE__, __FUNCTION__, __LINE__).warning
#define qCritical MessageLogger(__FILE__, __FUNCTION__, __LINE__).critical
#define qFatal MessageLogger(__FILE__, __FUNCTION__, __LINE__).fatal
#include "Debug.h"
#include "MessageLogContext.h"

class MessageLogger
{
public:
	MessageLogger() : context(){}
	MessageLogger(const char *fileName, const char *functionName, int lineNumber)
		: context(fileName, functionName, lineNumber) {}

	Debug info() const;
	Debug warning() const;
	Debug critical() const;
	Debug debug() const;

protected:
private:
	MessageLogContext context;
};




Log.cpp

#include "Log.h"


Debug MessageLogger::debug() const
{
	std::string debug = "debug";
	Debug dbg = Debug(&debug);
	MessageLogContext &ctxt = dbg.stream->context;
	ctxt.copy(context);
	dbg.stream->logType = Info;
	return dbg;
}

Debug MessageLogger::info() const
{
	Debug dbg = Debug();
	MessageLogContext &ctxt = dbg.stream->context;
	ctxt.copy(context);
	dbg.stream->logType = Info;
	return dbg;
}

Debug MessageLogger::warning() const
{
	Debug dbg = Debug();
	MessageLogContext &ctxt = dbg.stream->context;
	ctxt.copy(context);
	dbg.stream->logType = Warning;
	return dbg;
}

Debug MessageLogger::critical() const
{
	Debug dbg = Debug();
	MessageLogContext &ctxt = dbg.stream->context;
	ctxt.copy(context);
	dbg.stream->logType = Error;
	return dbg;
}



Debug.h

#pragma once

#include <iostream>  
#include <iomanip>  
#include <fstream>  
#include <string>  
#include <cstdlib>  
#include <stdint.h>  
#include <sstream>  
#include "Math/Vector2.h"  
#include "Math/Vector3.h"  
#include <vector>
//#include "Log.h"
#include "MessageLogContext.h"

enum LogType
{
	Info,
	Warning,
	Error,
	Default,
};

class Debug
{
public:
	struct Stream {
		Stream():ss(), space(true), context() {}
		Stream(std::string *s) :ss(*s), space(true), context(){}
		std::ostringstream ss;
		bool space;
		MessageLogContext context;
		LogType logType;
	} *stream;

	Debug() : stream(new Stream()) {}
	inline Debug(std::string *s) : stream(new Stream(s)) {}
	~Debug();
	inline Debug &operator<<(bool t) { stream->ss<<(t ? "true" : "false"); return maybeSpace(); }
	inline Debug &operator<<(char t) { stream->ss<< t; return maybeSpace(); }
	inline Debug &operator<<(signed short t) { stream->ss << t; return maybeSpace(); }
	inline Debug &operator<<(unsigned short t) { stream->ss << t; return maybeSpace(); }
	inline Debug &operator<<(std::string s) { stream->ss << s; return maybeSpace(); }
	inline Debug &operator<<(const char* c) { stream->ss << c; return maybeSpace(); }
	inline Debug &operator<<(Vector2 vec) { stream->ss << "(" << vec.x <<","<< vec.y<<")"; return maybeSpace(); }
	inline Debug &operator<<(Vector3 vec) { stream->ss << "(" << vec.x << "," << vec.y <<"," << vec.z << ")"; return maybeSpace(); }
	inline Debug &space() { stream->space = true; stream->ss << ' '; return *this; }
	inline Debug &nospace() { stream->space = false; return *this; }
	inline Debug &maybeSpace() { if (stream->space) stream->ss << ' '; return *this; }

	template <typename T>
	inline Debug &operator<<(const std::vector<T> &vec)
	{
		stream->ss << '(';
		for (int i = 0; i < vec.size(); ++i) {
			stream->ss << vec.at(i);
			stream->ss << ", ";
		}
		stream->ss << ')';
		return maybeSpace();
	}

	void LogToConsole(LogType type, const MessageLogContext &context, std::string logBuffer);

private:
	static Debug* _instance;
};


Debug.cpp

#include "Debug.h"

Debug::~Debug()
{
	LogToConsole(stream->logType, stream->context, stream->ss.str());
	delete stream;
}

void Debug::LogToConsole(LogType type, const MessageLogContext &context, std::string logBuffer)
{
	std::string logString;
	switch (type)
	{
	case Error:
		logString.append("Error! ");
		break;
	case Info:
		//logString.append("");
		break;
	case Warning:
		logString.append("Warning! ");
		break;
	default:
		break;
	}
	logString.append(logBuffer);
	logString.append("......");

	logString.append(context.file);
	logString.append(" ");
	logString.append(context.function);
	logString.append("()");

	std::cout << logString <<" line: " << context.line << " "  << std::endl;

	//logString.append(context.line);
}



参考

Qt source code

Qt Documentation http://doc.qt.io/qt-4.8/qdebug.html

http://www.cplusplus.com/

  • 8
    点赞
  • 18
    收藏
    觉得还不错? 一键收藏
  • 4
    评论
在Unreal Engine 5 (UE5)中实现合成系统可以通过以下步骤完成: 1. 创建一个Actor类,作为合成系统的基础。可以使用UE5自带的Actor类或者自定义一个Actor类。 2. 在Actor类中添加必要的变量和函数,例如合成的输入和输出物品、合成的材料、合成的效果等。 3. 创建一个UI界面,用于显示和控制合成系统。可以使用UE5自带的UMG或者其他UI框架来创建。 4. 在UI界面中添加按钮和其他控件,用于控制合成系统。例如,添加一个合成按钮,当用户点击该按钮时,合成系统将进行合成操作。 5. 在合成系统的函数中实现合成的逻辑。根据输入的材料和合成的效果,计算出合成的结果,并将结果输出到指定的位置。 6. 在UE5中创建一些测试场景,用于测试合成系统的功能和性能。 下面是一个简单的例子,演示如何使用UE5实现一个合成系统: 1. 创建一个Actor类,名为CraftingSystem。 2. 在CraftingSystem中添加以下变量和函数: ```c++ UPROPERTY(EditAnywhere) UStaticMeshComponent* OutputItemMesh; // 合成的输出物品 UPROPERTY(EditAnywhere) TArray<TSubclassOf<AActor>> InputItemClasses; // 合成的输入物品 UPROPERTY(EditAnywhere) TArray<int32> InputItemCounts; // 合成的输入物品数量 UPROPERTY(EditAnywhere) TSubclassOf<AActor> CraftEffectClass; // 合成的效果 UFUNCTION(BlueprintCallable) void Craft(); // 合成的函数 ``` 3. 创建一个UI界面,名为CraftingUI。 4. 在CraftingUI中添加一个合成按钮。 5. 在CraftingUI中为合成按钮添加点击事件,调用CraftingSystem的Craft函数。 6. 在CraftingSystem的Craft函数中实现合成的逻辑。计算出需要的输入物品和数量,如果输入物品足够,将其减少并生成一个输出物品和一个合成效果。 ```c++ void ACraftingSystem::Craft() { TArray<AActor*> inputItems; for (int32 i = 0; i < InputItemClasses.Num(); i++) { UClass* inputClass = InputItemClasses[i]; int32 inputCount = InputItemCounts[i]; TArray<AActor*> foundActors; UGameplayStatics::GetAllActorsOfClass(GetWorld(), inputClass, foundActors); int32 count = 0; for (AActor* actor : foundActors) { if (count >= inputCount) { break; } if (inputItems.Contains(actor)) { continue; } inputItems.Add(actor); count++; } if (count < inputCount) { UE_LOG(LogTemp, Error, TEXT("Not enough input items.")); return; } } for (AActor* actor : inputItems) { actor->Destroy(); } AActor* outputItem = GetWorld()->SpawnActor<AActor>(OutputItemClass

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值