首页 > 编程语言 >[岩禾溪] C++20项目 muduo网络库 项目实战 (1)Logger & Timestamp

[岩禾溪] C++20项目 muduo网络库 项目实战 (1)Logger & Timestamp

时间:2023-11-25 10:24:17浏览次数:41  
标签:std muduo 20 Timestamp result 日志 nocopyable Logger

  ​编辑本项目由 岩禾溪 原创

 

项目实战+新特性用法介绍
开源代码+博客解析+视频讲解

 

GitHub+CSDN+BiliBili同步更新,三个平台同名【岩禾溪】

视频讲解和代码链接在文章末尾,你的关注是我更新的最大动力

项目环境

本项目采用C++20开发 精简Muduo网络库

Build Tool:Xmake

archlinux-2023.09.01-x86_64

gcc version 13.2.1 20230801 (GCC)

xmake

构建工具简介 xmake 是一个灵活且功能强大的构建工具,支持多种编程语言,特别适用于 C/C++ 项目。

xmake.lua 配置文件

target("cpp")
    set_kind("binary")
    add_files("*.ixx","*cpp")
    set_languages("c++20")

xmake.lua 是 xmake 的配置文件,在这里定义了项目的构建规则和参数。 项目构建规则

target("cpp"):定义项目名称。 set_kind("binary"):指定构建生成的是可执行文件。 add_files(".ixx", "cpp"):添加项目源文件。 set_languages("c++20"):设置使用 C++20 标准编译代码。 C++20 支持

通过 set_languages("c++20"),我们启用了 C++20 标准的支持,使得项目可以利用最新的 C++ 特性。 简洁高效的构建

xmake 简化了构建流程,使得项目的构建过程更加简洁高效。

nocopyable

export module nocopyable;

export class nocopyable{
    public:
    nocopyable(const nocopyable&) = delete;
    nocopyable& operator=(const nocopyable&)   = delete;

    protected:

    nocopyable() = default;
    ~nocopyable() = default;
};
  1. 模块定义

    使用 export module nocopyable; 定义了一个名为 nocopyable 的 C++20 模块。
  2. 禁止复制

    nocopyable 类中的 nocopyable(const nocopyable&) = delete;nocopyable& operator=(const nocopyable&) = delete; 阻止了对象的复制和赋值。通过删除拷贝构造函数和拷贝赋值运算符,该类确保不会被复制创建或赋值。
  3. 受保护的构造函数和析构函数

    构造函数和析构函数声明为 protected,这意味着它们只能被该类及其派生类内部访问。这使得该类可以被继承,但不允许直接创建类的对象或对其进行复制。

nocopyable 模块是一种常见的技术,用于设计不希望被复制或赋值的类,例如单例模式的类或管理资源的类。

Logger

Logger.cpp

import Logger;
import Timestamp;
import <iostream>;
import <format>;
// 获取日志唯一的实例对象
Logger &Logger::instance()
{
    static Logger logger;
    return logger;
}

// 设置日志级别
void Logger::setLogLevel(LogLevel level)
{
    logLevel_ = level;
}
template <typename... Args>
    requires(std::convertible_to<Args, std::string> && ...)
void Logger::log(LogLevel level,
                 const std::string &format,
                 Args &&...args)
{
    // 设置日志级别
    setLogLevel(level);

    // 构建日志消息可以无所谓,不能无所获
    std::string message = std::format("[{}] {} : {}", level, Timestamp::now().toString(), std::format(format, std::forward<Args>(args)...));

    // 打印日志消息到控制台
    std::cout << message << std::endl;
}

这段代码展示了一个简单的日志记录器 Logger 的部分实现,利用模块化方式组织。这个模块包含了日志记录的实例获取、日志级别设置和日志记录功能。

  1. 模块导入

    import Logger; 语句导入了 Logger 模块
  2. 获取日志实例对象

    Logger::instance() 函数返回唯一的日志实例。
  3. 设置日志级别

    Logger::setLogLevel(LogLevel level) 函数用于设置日志级别。
  4. 日志记录功能

    Logger::log() 函数实现了日志记录的功能,根据传入的日志级别和格式进行记录。

Logger.ixx

export module Logger;
import <string>;
import nocopyable;
import <concepts>;
export class Logger : public nocopyable
{
public:
    enum class LogLevel
    {
        INFO,  // 普通信息
        ERROR, // 错误信息
        FATAL, // core信息
        DEBUG, // 调试信息
    };
    static Logger &instance();
    void setLogLevel(LogLevel level);

    template <typename... Args>
        requires(std::convertible_to<Args, std::string> && ...)
    void log(LogLevel level, const std::string &format, Args &&...args);

private:
    LogLevel logLevel_ = LogLevel::INFO;
};
  1. 模块化定义

    通过 export module Logger; 定义了一个名为 Logger 的 C++20 模块,但前面的 module; 是无效的。
  2. 模块导入

    import <string>;import nocopyable; 语句导入了相应的模块和头文件。
  3. 日志记录器类

    Logger 类是一个日志记录器类,继承了 nocopyable,表明该类不可被复制。
  4. 日志级别定义

    enum class LogLevel 定义了几个日志级别:INFO、ERROR、FATAL、DEBUG。
  5. 获取日志实例和设置日志级别

    instance() 函数用于获取日志唯一的实例对象。setLogLevel() 函数用于设置日志级别。
  6. 日志记录功能

    log() 函数实现了根据传入的日志级别、格式和参数进行日志记录的功能。
template <typename... Args>
        requires(std::convertible_to<Args, std::string> && ...)
    void log(LogLevel level, const std::string &format, Args &&...args);

在这段代码中,使用了C++20中的模板约束(template constraints)特性。模板约束允许你对模板参数进行更精细的限制,以确保模板参数满足特定的条件。在你提供的函数签名中,使用了 requires 关键字并结合了 std::convertible_to 的概念约束(concept constraint)。

让我们来解释这个函数签名中的不同部分:

  • template <typename... Args>:这声明了一个模板函数,它接受零个或多个模板参数,并将它们命名为 Args

  • requires:这是C++20中的关键字,用于引入模板约束。它后面紧跟着模板约束表达式,用于对模板参数进行约束。

  • (std::convertible_to<Args, std::string> && ...):这是模板约束表达式的一部分。std::convertible_to 是C++20中的概念(concept),用于检查类型是否可以转换为另一种类型。在这里,std::convertible_to<Args, std::string> 检查模板参数 Args 是否可以隐式转换为 std::string 类型。&& ... 是展开运算符,表示对参数包中的每个元素应用这个概念。

因此,这个模板函数 log 只允许接受满足以下条件的参数:

  • 模板参数 Args 的每个类型必须能够隐式转换为 std::string 类型。

这样的约束可以确保在调用 log 函数时,传递的参数能够隐式转换为 std::string,这符合函数签名中 format 参数为 const std::string & 类型的要求。

Timestamp

Timestamp.ixx

export module Timestamp;
export import <string>;
// 时间类
export class Timestamp
{
public:
    Timestamp();
    explicit Timestamp(int64_t microSecondsSinceEpoch);
    static Timestamp now();
    std::string toString() const;

private:
    int64_t microSecondsSinceEpoch_;
};
  1. 模块化定义

    通过 export module Timestamp; 定义了一个名为 Timestamp 的 C++20 模块,但前面的 module; 是无效的,应该指定模块的名称。
  2. 头文件包含

    #include <cstdint>#include <string>:这些是标准头文件,可能用于定义时间类所需的类型和字符串处理。
  3. 时间类定义

    Timestamp
    • 类定义了一个时间类,封装了一些关于时间处理的功能:

    • std::string toString() const 返回时间的字符串表示形式。

    • 静态函数 static Timestamp now() 用于获取当前时间;

    • 参数构造函数 explicit Timestamp(int64_t microSecondsSinceEpoch)

    • 默认构造函数 Timestamp()

 

Timestamp.cpp

import Timestamp;
import <ctime>;
Timestamp::Timestamp() : microSecondsSinceEpoch_(0) {}

Timestamp::Timestamp(int64_t microSecondsSinceEpoch) : microSecondsSinceEpoch_(microSecondsSinceEpoch) {}

Timestamp Timestamp::now()
{
    return Timestamp(time(nullptr));
}

std::string Timestamp::toString() const
{
    char buf[128] = {0};
    time_t timeValue = static_cast<time_t>(microSecondsSinceEpoch_ / 1000000);
    std::tm result;

    if (localtime_r(&timeValue, &result) != nullptr)
    {
        snprintf(buf, 128, "%4d/%02d/%02d %02d:%02d:%02d",
                 result.tm_year + 1900,
                 result.tm_mon + 1,
                 result.tm_mday,
                 result.tm_hour,
                 result.tm_min,
                 result.tm_sec);
    }

    return buf;
}
  1. 函数定义

    实现了
    • Timestamp

      类中声明的函数:

    • std::string Timestamp::toString() const 返回时间的字符串表示形式。

    • 静态函数 Timestamp Timestamp::now() 用于获取当前时间;

    • 参数构造函数 Timestamp::Timestamp(int64_t microSecondsSinceEpoch)

    • 默认构造函数 Timestamp::Timestamp()

  2. 时间格式化

    Timestamp::toString() 函数将时间戳格式化为字符串形式,使用 localtime_rsnprintf 函数进行格式化。这段代码是一个 Timestamp 类中的成员函数 toString() 的实现,它用于将时间戳转换为字符串表示形式。在这个函数中:
    • 修改返回类型为 std::string,可以确保时间戳的字符串副本被正确地返回,而不是指向局部数组的悬挂指针。

    • char buf[128]:定义了一个大小为 128 的字符数组 buf,用于存储格式化后的时间戳字符串。

    • time_t timeValue = static_cast<time_t>(microSecondsSinceEpoch_ / 1000000):将类成员变量 microSecondsSinceEpoch_ 转换为秒级的时间戳,存储在 timeValue 中。
    • std::tm result:创建一个 std::tm 结构体,用于存储时间的分解信息。
    • if (localtime_r(&timeValue, &result) != nullptr):使用 localtime_r() 函数将时间戳 timeValue 转换为本地时间,并将结果存储在 result 中。localtime_r() 函数会将时间戳转换为本地时间,并填充 result 结构体,如果转换成功,则返回 result 结构体指针,否则返回 nullptr
    • snprintf(buf, 128, "%4d/%02d/%02d %02d:%02d:%02d", result.tm_year + 1900, result.tm_mon + 1, result.tm_mday, result.tm_hour, result.tm_min, result.tm_sec):使用 snprintf 函数将时间分解信息按照指定的格式写入 buf 数组中。这里的格式字符串 "%4d/%02d/%02d %02d:%02d:%02d" 将年月日时分秒格式化为字符串,并将其写入到 buf 中。
    • return buf:返回格式化后的时间戳字符串。但是需要注意的是,返回的是 buf 的指针,并且在函数返回时,buf 数组将超出其作用域,这可能导致未定义的行为。为了避免此问题,可以考虑返回一个 std::string 对象,而不是指向局部数组的指针。

 

总结

可以无所谓,不能无所获

                                        ——岩

GitHub代码链接:  GitHub - YanHeXi/muduo_cpp20
项目讲解视频连接:岩禾溪的个人空间-岩禾溪个人主页-哔哩哔哩视频

标签:std,muduo,20,Timestamp,result,日志,nocopyable,Logger
From: https://www.cnblogs.com/YanHeXi/p/17855224.html

相关文章

  • [岩禾溪] C++20项目 muduo网络库 项目实战 (2)InetAddress & Channel
    ​ 目录 ​本项目由岩禾溪原创InetAddress.ixx模块介绍类InetAddress:C++20新特性内容:InetAddress.cpp函数实现解释:Channel.ixx模块介绍类Channel:Channel.cpp模块导入和常量定义:类Channel的函数实现:关于注释部分:更新LoggerLogger.ixx(更新) ​编辑本......
  • 2023版 STM32实战7 通用同步/异步收发器(串口)F103/F407
    串口简介和习惯-1-通用同步异步收发器(USART)能够灵活地与外部设备进行全双工数据交换,满足外部设备对工业标准NRZ异步串行数据格式的要求。-2-硬件流控制一般是关闭的-3-波特率指单位时间传输bit个数-4-数据位一般是8位-5-一般无校验位编写代码思路-1-参考帮助手册(F1/F4都有)-2......
  • 2023.11.25学习笔记
    集合SubsetSumsP1466[USACO2.2]集合SubsetSums-洛谷|计算机科学教育新生态(luogu.com.cn)背包板子题,有一说一看出来很简单贴accode#include<iostream>usingnamespacestd;longlonga[50];intmain(){intn;cin>>n;intsum=0,ans=0;fo......
  • NOIP2023游记
    之前忘写了现在忘了(雾)Day0在七高考。欣赏了一会走廊边上的展示柜。进考场。印象里是圆桌,结果还是常规的几排。(好像各个考场不一样……?)开考之后因为键盘的奇怪手感而奇怪了很久。一行头文件打了半天,,。P.S.之前同学说过七高键盘难用,然而没当回事TAT。T1有点水……但是还是......
  • 2023版 STM32实战7 通用同步/异步收发器(串口)F103/F407
    串口简介和习惯-1-通用同步异步收发器(USART)能够灵活地与外部设备进行全双工数据交换,满足外部设备对工业标准NRZ异步串行数据格式的要求。 -2-硬件流控制一般是关闭的 -3-波特率指单位时间传输bit个数 -4-数据位一般是8位 -5-一般无校验位 编写代码思路 -......
  • 20231124
    又是容斥的一天呢。容斥做傻了,每次推容斥系数都能推错。放学的时候@Super_Cube给我说了明天gm打算从「CDQ解决二维偏序问题」讲起,瞬间就不想去听gm上课了。但是@Super_Cube给我说「偶尔听gm发癫还是能放松一下的。」我说『不行,浪费我时间。』「也就两个小时而已,况且“......
  • 20211128《信息安全系统设计与实现》第十三章学习笔记
    一、任务内容自学教材第13章,提交学习笔记(10分)1.知识点归纳以及自己最有收获的内容,选择至少2个知识点利用chatgpt等工具进行苏格拉底挑战,并提交过程截图,提示过程参考下面内容(4分)“我在学***X知识点,请你以苏格拉底的方式对我进行提问,一次一个问题”核心是要求GPT:“请你以苏格......
  • 2023.11.24 日记 夜浓浓
    轻闲的一天。夜浓浓地笼罩在窗外,远远地依稀见到明暗的城市灯火。白日久违地听孙佳讲课,内容是没细听了,只是边学着英语的《语法通霸》边挂着一只耳朵听讲(纪中的英语老师笑着对我们仨说,挂着一只耳朵听课。她没有解释下去,我约摸是边做自己的事边听课,偶尔会被课堂吸引。不知这样是否是......
  • NOIP2023 退役记
    省流:爆单了。\(\rmDay\0\)中午感觉身体发冷,有一种不详的预感。下午润去看病,好像寄了。做了甲流的检测,不过好像要考\(\rmNOIP\)时才能出结果。吃了退烧药,但还是\(\rm38\)度多。没有胃口吃晚饭。晚上到了杭州稍微好了一点,喝了一点粥。\(\rmDay\1\)早上一测温度还......
  • CSP2023+NOIP2023邮寄
    本文同时发表在个人洛谷博客。CSPDay-1上午打德文布置的毒瘤信心赛,据说请了一个D类金验题,没有成功ak。打完没信心了。下午去下沙。有点像小县城。晚饭在下沙天街,好评。颓废。Day0上午打J。开场3分钟没过T1,然后发现次数是\(\log\)级别的,无脑暴力。菜死了。菜死了。菜......