一.脚手架介绍以及部分工具使用

一.脚手架介绍

1.概念

2.作用

3.封装

a.基础功能库

b.中间件及客户端SDK

二.虚拟机环境安装(没有公网地址,只能自己主机访问)

1.Vmware

a.软件下载

软件下载链接:https://www.vmware.com/products/desktop-hypervisor/workstation-and-fusion

b.软件安装

2.Ubuntu22.04

a.镜像下载

国内源:https://mirrors.163.com/ubuntu-releases/

b.虚拟机安装

c.虚拟机基础配置

我们可以通过我们的xshell 和 我们的trae来进行连接

三.Docker介绍与安装

1.docker介绍

我们要部署多个服务器,就直接拉取docker容器即可

2.docker安装

sudo apt update

sudo apt-get install ca-certificates curl gnupg lsb-release

sudo apt-get install make gcc g++ libz-dev lrzsz vim software-properties-common

sudo mkdir -p /etc/docker

sudo tee /etc/docker/daemon.json <<-'EOF'

{

"registry-mirrors": [

"https://docker.m.daocloud.io",

"https://dockerhub.timeweb.cloud",

"https://huecker.io",

"https://do.nark.eu.org",

"https://dc.j8.work",

"https://docker.m.daocloud.io",

"https://dockerproxy.com",

"https://docker.mirrors.ustc.edu.cn",

"https://docker.nju.edu.cn",

"https://hub.xdark.top",

"https://dockerpull.org",

"https://dockerproxy.cn",

"https://docker.rainbond.cc",

"https://b1skj1q5.mirror.aliyuncs.com",

"https://docker.m.daocloud.io",

"https://dockerhub.timeweb.cloud",

"https://huecker.io"

]

}

EOF

sudo systemctl daemon-reload

curl -fsSL http://mirrors.aliyun.com/docker-ce/linux/ubuntu/gpg | sudo apt-key add -

sudo add-apt-repository "deb [arch=amd64] http://mirrors.aliyun.com/docker-ce/linux/ubuntu $(lsb_release -cs) stable"

sudo apt update

sudo apt-get install docker-ce docker-ce-cli containerd.io

sudo curl -L "https://github.com/docker/compose/releases/download/v2.13.0/docker-composelinux-x86_64" -o /usr/bin/docker-compose

sudo chmod +x /usr/bin/docker-compose

docker-compose --version

sudo groupadd docker

sudo gpasswd -a $USER docker

newgrp docker

docker version

3.docker常用指令

---------------------后续更新------------------

四.gflags介绍

1.gflags介绍

官方文档:https://gflags.github.io/gflags/

代码仓库:https://github.com/gflags/gflags.git

2.gflags安装

3.gflags使用

4.入门案例

a.main.cc

cpp 复制代码
/*

gflags使用:
    1. 包含头文件 <gflags/gflags.h>
    2. 定义要捕获的参数信息,以及设置默认值
    3. 解析参数,初始化参数数据
    4. 访问参数
*/

//   一.
#include <gflags/gflags.h>
#include <iostream>
#include <string>


//   二.
DEFINE_bool(reuse_addr,true,"使用启用地址重用选项");
DEFINE_int32(listen_port,8080,"服务器监听端口");
DEFINE_string(listen_ip,"0.0.0.0","服务器监听IP");
DEFINE_double(pi,3.14,"圆周率");


int main(int argc,char* argvp[])
{
    //   三.
    google::ParseCommandLineFlags(&argc,&argvp,true);
    //   四.
    std::cout << "reuse_addr: " << FLAGS_reuse_addr << std::endl;
    std::cout << "listen_port: " << FLAGS_listen_port << std::endl;
    std::cout << "listen_ip: " << FLAGS_listen_ip << std::endl;
    std::cout << "pi: " << FLAGS_pi << std::endl;
    return 0;
}

b.makefile

bash 复制代码
main:main.cc
	g++ main.cc -o main -lgflags
.PHONY:clean
clean:
	rm -rf main

如果直接进行运行,就是我们对应的默认值

写入那个参数就修改那个参数

c.main.conf

我们可以新增一个conf文件,里面写的就是我们的--listen_ip等信息

配置文件里面不要加""(双引号)

cpp 复制代码
--listen_ip=127.0.0.1
--listen_port=8080
--pi=3.1415926
--reuse_addr=false

d.外部文件访问(child.cc)

cpp 复制代码
#include <iostream>
#include <gflags/gflags.h>

DECLARE_string(listen_ip);
DECLARE_bool(reuse_addr);
DECLARE_int32(listen_port);
DECLARE_double(pi);

void print()
{
    std::cout << "呵呵" << std::endl;
    std::cout << "reuse_addr: " << FLAGS_reuse_addr << std::endl;
    std::cout << "listen_port: " << FLAGS_listen_port << std::endl;
    std::cout << "listen_ip: " << FLAGS_listen_ip << std::endl;
    std::cout << "pi: " << FLAGS_pi << std::endl;
}

makefile

bash 复制代码
main : main.cc
	g++ main.cc child.cc -o main -lgflags
.PHONY : clean
clean:
	rm -rf main

五.gtest的使用

1.介绍以及安装

2.使用

cpp 复制代码
/*
    gtest简单使用
        1.包含头文件 <gtest/gtest.h>
        2.定义测试用例
        3.初始化测试环境
        4.执行测试用例
*/


#include <gtest/gtest.h>
#include <iostream>
#include <unordered_map>

TEST(unordered_map_test,insert)
{
    std::unordered_map<int,int> map;
    map.insert(std::make_pair(1,1));
    map.insert(std::make_pair(2,2));
    map.insert(std::make_pair(3,3));
    ASSERT_EQ(map.size(),3);
}

TEST(unordered_map_test,find)
{
    std::unordered_map<int,int> map;
    map.insert(std::make_pair(1,1));
    map.insert(std::make_pair(2,2));
    ASSERT_NE(map.find(2),map.end());
    ASSERT_EQ(map.find(1)->second,1);
}


int main(int argc,char* argv[])
{
    testing::InitGoogleTest(&argc,argv);
    return RUN_ALL_TESTS();
}
bash 复制代码
simple:simple.cc
	g++ -o $@ $^ -lgtest
.PHONY:clean
clean:
	rm -rf simple

3.gtest事件机制

a.全局事件

cpp 复制代码
/*
    全局测试套件使用:
        1. 包含头文件 <gtest/gtest.h>
        2. 定义全局测试环境类
            1. 定义测试环境初始接口:会在所有用例执行前执行
            2. 定义测试环境清理接口:会在所有用例执行后执行
        3. 定义测试用例
        4. 初始化测试环境
        5. 执行测试用例
*/

#include <gtest/gtest.h>
#include <iostream>
#include <unordered_map>

class GlobalTestEnvironment : public testing::Environment {
public:
    virtual void SetUp() {
        std::cout << "执行于所有用例之前" << std::endl;
    }
    virtual void TearDown() {
        std::cout << "执行于所有用例之后" << std::endl;
    }
};


TEST(unordered_map_test,insert)
{
    std::unordered_map<int,int> map;
    map.insert(std::make_pair(1,1));
    map.insert(std::make_pair(2,2));
    map.insert(std::make_pair(3,3));
    ASSERT_EQ(map.size(),3);
}

TEST(unordered_map_test,find)
{
    std::unordered_map<int,int> map;
    map.insert(std::make_pair(1,1));
    map.insert(std::make_pair(2,2));
    ASSERT_NE(map.find(2),map.end());
    ASSERT_EQ(map.find(1)->second,1);
}

int main(int argc,char* argv[])
{
    testing::InitGoogleTest(&argc,argv);
    testing::AddGlobalTestEnvironment(new GlobalTestEnvironment);
    return RUN_ALL_TESTS();
}
cpp 复制代码
// /*
//     全局测试套件使用:
//         1. 包含头文件 <gtest/gtest.h>
//         2. 定义全局测试环境类
//             1. 定义测试环境初始接口:会在所有用例执行前执行
//             2. 定义测试环境清理接口:会在所有用例执行后执行
//         3. 定义测试用例
//         4. 初始化测试环境
//         5. 执行测试用例
// */

// #include <gtest/gtest.h>
// #include <iostream>
// #include <unordered_map>

// class GlobalTestEnvironment : public testing::Environment {
// public:
//     virtual void SetUp() {
//         std::cout << "执行于所有用例之前" << std::endl;
//     }
//     virtual void TearDown() {
//         std::cout << "执行于所有用例之后" << std::endl;
//     }
// };


// TEST(unordered_map_test,insert)
// {
//     std::unordered_map<int,int> map;
//     map.insert(std::make_pair(1,1));
//     map.insert(std::make_pair(2,2));
//     map.insert(std::make_pair(3,3));
//     ASSERT_EQ(map.size(),3);
// }

// TEST(unordered_map_test,find)
// {
//     std::unordered_map<int,int> map;
//     map.insert(std::make_pair(1,1));
//     map.insert(std::make_pair(2,2));
//     ASSERT_NE(map.find(2),map.end());
//     ASSERT_EQ(map.find(1)->second,1);
// }

// int main(int argc,char* argv[])
// {
//     testing::InitGoogleTest(&argc,argv);
//     testing::AddGlobalTestEnvironment(new GlobalTestEnvironment);
//     return RUN_ALL_TESTS();
// }


// -----------------------------------------------------------------------------
/*
    全局测试套件使用:
        1. 包含头文件 <gtest/gtest.h>
        2. 定义全局测试环境类
            1. 定义测试环境初始接口:会在所有用例执行前执行
            2. 定义测试环境清理接口:会在所有用例执行后执行
        3. 定义测试用例
        4. 初始化测试环境
        5. 执行测试用例
*/

#include <gtest/gtest.h>
#include <iostream>
#include <unordered_map>


std::unordered_map<int,int> map;

class GlobalTestEnvironment : public testing::Environment {
public:
    virtual void SetUp() {
        std::cout << "执行于所有用例之前" << std::endl;
        map.insert(std::make_pair(1,1));
        map.insert(std::make_pair(2,2));
        map.insert(std::make_pair(3,3));
    }
    virtual void TearDown() {
        std::cout << "执行于所有用例之后" << std::endl;
    }
};

TEST(GlobalTestEnvironment,remove)
{
    map.erase(2);
}

TEST(GlobalTestEnvironment,size)
{   
    ASSERT_EQ(map.size(),3);
}

TEST(GlobalTestEnvironment,find)
{
    ASSERT_NE(map.find(2),map.end());
    ASSERT_EQ(map.find(1)->second,1);
}

int main(int argc,char* argv[])
{
    testing::InitGoogleTest(&argc,argv);
    testing::AddGlobalTestEnvironment(new GlobalTestEnvironment);
    return RUN_ALL_TESTS();
}

b.TestSuite事件

cpp 复制代码
// /*
//     全局测试套件使用:
//         1. 包含头文件 <gtest/gtest.h>
//         2. 定义全局测试环境类
//             1. 定义测试环境初始接口:会在所有用例执行前执行
//             2. 定义测试环境清理接口:会在所有用例执行后执行
//         3. 定义测试用例
//         4. 初始化测试环境
//         5. 执行测试用例
// */

// #include <gtest/gtest.h>
// #include <iostream>
// #include <unordered_map>

// class GlobalTestEnvironment : public testing::Environment {
// public:
//     virtual void SetUp() {
//         std::cout << "执行于所有用例之前" << std::endl;
//     }
//     virtual void TearDown() {
//         std::cout << "执行于所有用例之后" << std::endl;
//     }
// };


// TEST(unordered_map_test,insert)
// {
//     std::unordered_map<int,int> map;
//     map.insert(std::make_pair(1,1));
//     map.insert(std::make_pair(2,2));
//     map.insert(std::make_pair(3,3));
//     ASSERT_EQ(map.size(),3);
// }

// TEST(unordered_map_test,find)
// {
//     std::unordered_map<int,int> map;
//     map.insert(std::make_pair(1,1));
//     map.insert(std::make_pair(2,2));
//     ASSERT_NE(map.find(2),map.end());
//     ASSERT_EQ(map.find(1)->second,1);
// }

// int main(int argc,char* argv[])
// {
//     testing::InitGoogleTest(&argc,argv);
//     testing::AddGlobalTestEnvironment(new GlobalTestEnvironment);
//     return RUN_ALL_TESTS();
// }


// -----------------------------------------------------------------------------
/*
    全局测试套件使用:
        1. 包含头文件 <gtest/gtest.h>
        2. 定义全局测试环境类
            1. 定义测试环境初始接口:会在所有用例执行前执行
            2. 定义测试环境清理接口:会在所有用例执行后执行
        3. 定义测试用例
        4. 初始化测试环境
        5. 执行测试用例
*/

#include <gtest/gtest.h>
#include <iostream>
#include <unordered_map>


std::unordered_map<int,int> map;

class GlobalTestEnvironment : public testing::Environment {
public:
    virtual void SetUp() {
        std::cout << "执行于所有用例之前" << std::endl;
        map.insert(std::make_pair(1,1));
        map.insert(std::make_pair(2,2));
        map.insert(std::make_pair(3,3));
    }
    virtual void TearDown() {
        std::cout << "执行于所有用例之后" << std::endl;
        map.clear();
    }
};

TEST(GlobalTestEnvironment,remove)
{
    map.erase(2);
}

TEST(GlobalTestEnvironment,size)
{   
    ASSERT_EQ(map.size(),3);
}

TEST(GlobalTestEnvironment,find)
{
    ASSERT_NE(map.find(2),map.end());
    ASSERT_EQ(map.find(1)->second,1);
}

int main(int argc,char* argv[])
{
    testing::InitGoogleTest(&argc,argv);
    testing::AddGlobalTestEnvironment(new GlobalTestEnvironment);
    return RUN_ALL_TESTS();
}
cpp 复制代码
/*
局部测试套件使用:
1. 包含头文件 <gtest/gtest.h>
2. 定义测试套件
   1. 定义测试套件初始接口:会在每个测试用例执行之前执行
   2. 定义测试套件清理接口:会在每个测试用例执行之后执行
3. 定义测试用例
4. 执行测试用例
*/


#include <gtest/gtest.h>
#include <iostream>
#include <unordered_map>

class LocalTestEnvironment : public ::testing::Test
{
public:
    std::unordered_map<int,int> map;
    virtual void SetUp() {
        std::cout << "执行于每个用例之前" << std::endl;
        map.insert(std::make_pair(1,1));
        map.insert(std::make_pair(2,2));
        map.insert(std::make_pair(3,3));
    }
    virtual void TearDown() {
        std::cout << "执行于每个用例之后" << std::endl;
        map.clear();
    }
};

TEST_F(LocalTestEnvironment,find)
{   
    ASSERT_EQ(map.size(),3);
    ASSERT_EQ(map.find(1)->second,1);
    map.erase(1);
    std::cout << "测试用例1" << std::endl;
}

TEST_F(LocalTestEnvironment,remove)
{   
    map.erase(1);
    ASSERT_EQ(map.size(),2);
    std::cout << "测试用例2" << std::endl;
}


int main(int argc,char* argv[])
{
    testing::InitGoogleTest(&argc,argv);
    return RUN_ALL_TESTS();
}

c.TestCase事件

cpp 复制代码
/*
局部测试套件使用:
1. 包含头文件 <gtest/gtest.h>
2. 定义测试套件
   1. 定义测试套件初始接口:会在每个测试用例执行之前执行
   2. 定义测试套件清理接口:会在每个测试用例执行之后执行
3. 定义测试用例
4. 执行测试用例
*/


#include <gtest/gtest.h>
#include <iostream>
#include <unordered_map>

class LocalTestEnvironment : public ::testing::Test
{
public:
    std::unordered_map<int,int> map;
    static void SetUpTestCase()
    {
        std::cout << "公共环境接口,所有用例执行之前执行" << std::endl;
    }
    static void TearDownTestCase()
    {
        std::cout << "公共环境接口,所有用例执行之后执行" << std::endl;
    }
    virtual void SetUp() {
        std::cout << "执行于每个用例之前" << std::endl;
        map.insert(std::make_pair(1,1));
        map.insert(std::make_pair(2,2));
        map.insert(std::make_pair(3,3));
    }
    virtual void TearDown() {
        std::cout << "执行于每个用例之后" << std::endl;
        map.clear();
    }
};

TEST_F(LocalTestEnvironment,find)
{   
    ASSERT_EQ(map.size(),3);
    ASSERT_EQ(map.find(1)->second,1);
    map.erase(1);
    std::cout << "测试用例1" << std::endl;
}

TEST_F(LocalTestEnvironment,remove)
{   
    map.erase(1);
    ASSERT_EQ(map.size(),2);
    std::cout << "测试用例2" << std::endl;
}


int main(int argc,char* argv[])
{
    testing::InitGoogleTest(&argc,argv);
    return RUN_ALL_TESTS();
}

SetUpTestCaseTearDownTestCase 是公共的接口,所有的环境都能进行使用

六.spdlog介绍

1.介绍

2.特点

3.安装

4.使用

a.标准输出

cpp 复制代码
/*
    标准输出的日志:
        1. 包含头文件 <spdlog/spdlog.h>
        2. 创建日志对象
        3. 设置日志输出等级
        4. 设置日志输出格式
        5. 输出日志
*/

#include <spdlog/spdlog.h>
#include <spdlog/sinks/stdout_color_sinks.h>
#include <iostream>


int main(int argc, char *argv[])
{
    auto logger = spdlog::stdout_color_mt("stdout");
    logger->set_level(spdlog::level::debug);
    logger->set_pattern("[%H:%M:%S][%7l]: %v");
    logger->debug("{} 今年 {}岁","小明",18);
    logger->info("{} 今年 {}岁","小红",19);
    logger->warn("{} 今年 {}岁","小刚",20);
    logger->error("{} 今年 {}岁","小绿",21);
    return 0;
}
bash 复制代码
stdout : stdout.cc
	g++ -o $@ $^ -lspdlog -lpthread -lfmt
.PHONY : clean
clean:
	rm -f stdout

在这个日志等级中,我们对应的等级高于DEBUG的就会进行输出

cpp 复制代码
/*
    标准输出的日志:
        1. 包含头文件 <spdlog/spdlog.h>
        2. 创建日志对象
        3. 设置日志输出等级
        4. 设置日志输出格式
        5. 输出日志
*/

#include <spdlog/spdlog.h>
#include <spdlog/sinks/stdout_color_sinks.h>
#include <iostream>


int main(int argc, char *argv[])
{
    auto logger = spdlog::stdout_color_mt("stdout");
    // logger->set_level(spdlog::level::debug);
    logger->set_level(spdlog::level::info);
    logger->set_pattern("[%H:%M:%S][%7l]: %v");
    logger->debug("{} 今年 {}岁","小明",18);
    logger->info("{} 今年 {}岁","小红",19);
    logger->warn("{} 今年 {}岁","小刚",20);
    logger->error("{} 今年 {}岁","小绿",21);
    return 0;
}

这里我们将日志level设置成为了info,意思就是所有大于等于info的都进行输出

b.文件输出

cpp 复制代码
/*
    标准输出的日志:
        1. 包含头文件 <spdlog/spdlog.h>
        2. 创建日志对象
        3. 设置日志输出等级
        4. 设置日志输出格式
        5. 输出日志
*/

#include <spdlog/spdlog.h>
#include <spdlog/sinks/basic_file_sink.h>
#include <iostream>


int main(int argc, char *argv[])
{
    auto logger = spdlog::basic_logger_mt("file_logger","./file.dat");
    logger->set_level(spdlog::level::info);
    logger->set_pattern("[%H:%M:%S][%7l]: %v");
    logger->debug("{} 今年 {}岁","小明",18);
    logger->info("{} 今年 {}岁","小红",19);
    logger->warn("{} 今年 {}岁","小刚",20);
    logger->error("{} 今年 {}岁","小绿",21);
    return 0;
}

c.循环输出

cpp 复制代码
/*
    标准输出的日志:
        1. 包含头文件 <spdlog/spdlog.h>
        2. 创建日志对象
        3. 设置日志输出等级
        4. 设置日志输出格式
        5. 输出日志
*/

#include <spdlog/spdlog.h>
#include <spdlog/sinks/rotating_file_sink.h>
#include <iostream>


int main(int argc, char *argv[])
{
    auto logger = spdlog::rotating_logger_mt("rotate_logger","./rotate.dat",1024,3);
    logger->set_level(spdlog::level::info);
    logger->set_pattern("[%H:%M:%S][%7l]: %v");
    for(int i = 0;i < 10000;i++)
    {
        logger->info("hello world - {}",i);
    }
    return 0;
}

文件的内容会进行覆盖,所以会导致文件内容是是最新的内容(不会一直堆积在一个文件中)

后续还有写入数据库的操作,网络写入等

d.异步日志使用

cpp 复制代码
/*
    标准输出的日志:
        1. 包含头文件 <spdlog/spdlog.h>
        2. 创建日志对象
        3. 设置日志输出等级
        4. 设置日志输出格式
        5. 输出日志
*/

#include <spdlog/spdlog.h>
#include <spdlog/async.h>
#include <spdlog/sinks/stdout_color_sinks.h>
#include <iostream>


int main(int argc, char *argv[])
{
    auto logger = spdlog::stdout_color_mt<spdlog::async_factory>("stdout_logger");
    logger->set_level(spdlog::level::info);
    logger->set_pattern("[%H:%M:%S][%7l]: %v");
    for(int i = 0;i < 10000;i++)
    {
        logger->info("hello world - {}",i);
    }
    return 0;
}

但是我们目前还是无法体现这个同步和异步的效率的差别

5.日志组件对比

6.封装

a.设计

b.实现

然后log.cc 和 log.h进行分离

log.h

cpp 复制代码
/*
    日志操作封装:
        1.防止头文件重复包含
        2.包含头文件
        3.声明命名空间
        4.声明全局日志器
        5.声明日志配置结构体
        6.声明全局日志器初始化接口
        7.封装日志输出宏
*/

//  防止头文件重复包含
#pragma once

//  包含头文件
#include <spdlog/spdlog.h>
#include <spdlog/sinks/stdout_color_sinks.h>
#include <spdlog/sinks/basic_file_sink.h>
#include <spdlog/sinks/rotating_file_sink.h>
#include <spdlog/async.h>
#include <iostream>

//  声明命名空间
namespace logger
{
    //  声明全局日志器
    extern std::shared_ptr<spdlog::logger> g_logger;
    //  声明日志配置结构体
    struct LogConfig
    {
        bool async;         // 是否异步
        int level;          // 日志等级: 1-debug 2-info 3-warn 4-error 6-off
        std::string format; // 日志格式: [%H:%M:%S][%-7l]: %v
        std::string path;   // 日志目标: stdout 或 ./log.dat
    };

    //  声明全局日志器初始化接口
    extern void log_init(const LogConfig& config);
    //  声明日志输出宏
    #define FMT_PREFIX std::string("[{}:{}]: ")
    #define DBG(fmt, ...) logger::g_logger->debug(FMT_PREFIX + fmt, __FILE__, __LINE__, ##__VA_ARGS__)
    #define INF(fmt, ...) logger::g_logger->info(FMT_PREFIX + fmt, __FILE__, __LINE__, ##__VA_ARGS__)
    #define WAR(fmt, ...) logger::g_logger->warn(FMT_PREFIX + fmt, __FILE__, __LINE__, ##__VA_ARGS__)
    #define ERR(fmt, ...) logger::g_logger->error(FMT_PREFIX + fmt, __FILE__, __LINE__, ##__VA_ARGS__)
};

log.cc

cpp 复制代码
#include "log.h"

namespace logger
{
    //  声明全局日志器
    std::shared_ptr<spdlog::logger> g_logger;

    //  实现全局日志器初始化接口
    void log_init(const LogConfig& config)
    {
        // 1.判断日志器类型  -- async/sync
        // 2.判断输出目标    -- stdout 或 文件
        // 3.创建日志器
        if(config.async == true)
        {
            if(config.path == "stdout")
            {
                g_logger = spdlog::stdout_color_mt<spdlog::async_factory>("stdout_logger");
            }
            else
            {
                g_logger = spdlog::basic_logger_mt<spdlog::async_factory>("file_logger",config.path);
            }
        }
        else
        {
            if(config.path == "stdout")
            {
                g_logger = spdlog::stdout_color_mt("stdout_logger");
            }
            else
            {
                g_logger = spdlog::basic_logger_mt("file_logger",config.path);
            }
        }
        // 4.设置日志输出等级
        g_logger->set_level(spdlog::level::level_enum(config.level));
        // 5.设置日志格式
        g_logger->set_pattern(config.format);
    }
}

log_test.cc

cpp 复制代码
#include <log.h>
#include <gflags/gflags.h>

//  1.通过gflags定义要捕获的参数
DEFINE_bool(log_async,true,"是否异步输出日志");
DEFINE_string(log_path,"stdout","日志输出路径");
DEFINE_int32(log_level,1,"日志输出等级: 1-debug;2-info;3-warn;4-error;6-off");
DEFINE_string(log_format,"[%H:%M:%S][%7l]%v","日志输出格式");


int main(int argc,char* argv[])
{
    // 2.解析命令行参数
    gflags::ParseCommandLineFlags(&argc,&argv,true);
    // 3.初始化日志器
    logger::LogConfig config = 
    {
        .async = FLAGS_log_async,
        .level = FLAGS_log_level,
        .format = FLAGS_log_format,
        .path = FLAGS_log_path,
    };
    logger::log_init(config);
    // 4.输出日志
    DBG("{}今年{}岁","小明",18);
    INF("{}今年{}岁","小红",19);
    WAR("{}今年{}岁","小刚",20);
    ERR("{}今年{}岁","小绿",21);
    return 0;
}

makefile

bash 复制代码
CFLAGS=-I../../source/ -std=c++17
log_test:log_test.cc ../../source/log.cc
	g++ $(CFLAGS) -o $@ $^ -lspdlog -lpthread -lfmt -lgflags

进行提交之后

这样就合并好了

七.Jsoncpp的介绍

1.基本结构

2.JsonCpp安装

a.安装

b.JsonCpp的介绍

c.Json的使用

主要注意地方:

cpp 复制代码
#include <iostream>
#include <jsoncpp/json/json.h>

int main()
{
    Json::Value val;
    val["name"] = "ltw";
    val["age"] = 18;
    val["score"].append(77.5);
    val["score"].append(88.5);
    val["score"].append(99.5);
    std::cout << val.toStyledString() << std::endl; 
    return 0;
}
cpp 复制代码
#include <jsoncpp/json/json.h>
#include <iostream>
#include <memory>
#include <sstream>

int main()
{
    Json::Value val;
    val["name"] = "ltw";
    val["age"] = 18;
    val["score"].append(77.5);
    val["score"].append(88.5);
    val["score"].append(99.5);
    
    //  1.实例化建造者对象
    //  2.通过建造者对象构造序列化对象
    //  3.通过序列化对象进行序列化
    Json::StreamWriterBuilder builder;
    std::unique_ptr<Json::StreamWriter> writer(builder.newStreamWriter());
    std::stringstream ss;
    int ret = writer->write(val, &ss);
    if(ret != 0)
    {
        std::cout << "write json failed" << std::endl;
        return -1;
    }
    std::cout << ss.str() << std::endl;
    return 0;
}
cpp 复制代码
#include <jsoncpp/json/json.h>
#include <iostream>
#include <memory>
#include <sstream>

int main()
{
    Json::Value val;
    val["name"] = "ltw";
    val["age"] = 18;
    val["score"].append(77.5);
    val["score"].append(88.5);
    val["score"].append(99.5);
    
    //  1.实例化建造者对象
    //  2.通过建造者对象构造序列化对象
    //  3.通过序列化对象进行序列化
    Json::StreamWriterBuilder builder;
    builder["commentStyle"] = "None";
    builder["indentation"] = "";
    std::unique_ptr<Json::StreamWriter> writer(builder.newStreamWriter());
    std::stringstream ss;
    int ret = writer->write(val, &ss);
    if(ret != 0)
    {
        std::cout << "write json failed" << std::endl;
        return -1;
    }
    std::cout << ss.str() << std::endl;
    return 0;
}

commentStyle也可以不进行设置

cpp 复制代码
#include <jsoncpp/json/json.h>
#include <iostream>



int main()
{
    std::string json_str = R"({
        "name": "ltw",
        "age": 18,
        "score": [77.5, 88.5, 99.5]
    })";
    Json::CharReaderBuilder builder;
    std::unique_ptr<Json::CharReader> reader(builder.newCharReader());
    Json::Value val;
    std::string errors;
    bool ret = reader->parse(json_str.c_str(), json_str.c_str() + json_str.size(), &val, &errors);
    if(!ret)
    {
        std::cout << "parse json failed: " << errors << std::endl;
        return -1;
    }
    std::cout << val.toStyledString() << std::endl;
    std::cout << "name: " << val["name"].asString() << std::endl;
    std::cout << "age: " << val["age"].asInt() << std::endl;
    if(!val["score"].isNull() && val["score"].isArray())
    {
        for(int i = 0; i < val["score"].size(); i++)
        {
            std::cout << "score[" << i << "]: " << val["score"][i].asDouble() << std::endl;
        }
    }
    
    return 0;
}

3.Jsoncpp的二次封装

cpp 复制代码
/*
    util工具封装
        1.json序列化和反序列化
*/

#pragma once
#include <jsoncpp/json/json.h>
#include <iostream>
#include <memory>
#include <sstream>
#include <optional>
#include "log.h"

namespace util
{
    class JsonUtil
    {
    public:
        // json序列化
        static std::optional<std::string> serialize(const Json::Value& val);
        // json反序列化
        static std::optional<Json::Value> deserialize(const std::string& json_str);
    };
}
cpp 复制代码
#include "util.h"

namespace util
{
    std::optional<std::string> JsonUtil::serialize(const Json::Value& val) {
        Json::StreamWriterBuilder builder;
        builder["indentation"] = "";
        std::unique_ptr<Json::StreamWriter> swp(builder.newStreamWriter());
        std::stringstream ss;
        int ret = swp->write(val, &ss);
        if (ret != 0) {
            ERR("序列化失败!");
            return std::optional<std::string>();
        }
        return ss.str();
    }
    std::optional<Json::Value> JsonUtil::deserialize(const std::string& input) {
        Json::CharReaderBuilder builder;
        std::unique_ptr<Json::CharReader> reader(builder.newCharReader());
        Json::Value val;
        std::string errs;
        bool ret = reader->parse(input.c_str(), input.c_str() + input.size(), &val, &errs);
        if (ret == false) {
            ERR("{} 反序列化失败:{}", input, errs);
            return std::optional<Json::Value>();
        }
        return val;
    }
}
cpp 复制代码
#include "../../source/util.h"

void serialize_test()
{
    Json::Value val;
    val["name"] = "ltw";
    val["age"] = 18;
    val["score"].append(77.5);
    val["score"].append(88.5);
    val["score"].append(99.5);
    auto ret = util::JsonUtil::serialize(val);
    if(!ret) {
        return;
    }
    std::cout << *ret << std::endl;
}

void deserialize_test()
{
    std::string json_str = R"({"name": "ltw", "age": 18, "score": [77.5, 88.5, 99.5]})";
    auto val = util::JsonUtil::deserialize(json_str);
    if (!val) {
        return;
    }
    std::cout << (*val)["name"].asString() << std::endl;
    std::cout << (*val)["age"].asInt() << std::endl;
    if(!(*val)["score"].isNull() && (*val)["score"].isArray()) {
        for(auto& score : (*val)["score"]) {
            std::cout << score.asDouble() << std::endl;
        }
    }
}

int main()
{
    serialize_test();
    deserialize_test();
    return 0;
}
cpp 复制代码
json_test : json_test.cc ../../source/util.cc ../../source/log.cc
	g++ -std=c++17 $^ -o $@ -ljsoncpp -lfmt -lspdlog -lpthread -lgflags
.PHONY:clean
clean:
	rm -rf json_test

八.cpp-httplib的介绍

1.介绍

2.安装

3.使用接口

4.实现原理

5.使用

cpp 复制代码
/*
    使用HTTPLIB搭建HTTP服务器
        1. 包含头文件
        2. 实例化Server对象
        3. 注册路由信息 -- 告诉httplib收到哪个请求应该由哪个接口进行处理
        4. 启动服务器
*/


#include <httplib.h>

void HelloWorld(const httplib::Request& req, httplib::Response& res)
{
    std::cout << req.method << std::endl;
    std::cout << req.path << std::endl;
    std::cout << req.body << std::endl;
    for(auto it : req.headers)
    {
        std::cout << it.first << " : " << it.second << std::endl;
    }
    for(auto it : req.params)
    {
        std::cout << it.first << " : " << it.second << std::endl;
    }
    std::string html_body = "<html><body><h1>Hello World!</h1></body></html>";
    res.set_content(html_body, "text/html");
    res.status = 200;
}

int main()
{
    httplib::Server svr;
    svr.Get("/hello", HelloWorld);
    svr.Get(R"(/number/(\d+))", [](const httplib::Request& req, httplib::Response& res)
    {
        std::cout << req.method << std::endl;
        std::cout << req.path << std::endl;
        std::string number = req.matches[1];
        std::string html_body = "<html><body><h1>Number: " + number + "</h1></body></html>";
        res.set_content(html_body, "text/html");
        res.status = 200;
    });
    svr.listen("0.0.0.0", 8080);
    return 0;
}
bash 复制代码
server:server.cc
	g++ -std=c++17 $^ -o $@ -lpthread
.PHONY:clean
clean:
	rm -rf server

启动服务器:

相关推荐
资深web全栈开发6 小时前
深入理解 Google Wire:Go 语言的编译时依赖注入框架
开发语言·后端·golang
ohoy6 小时前
EasyPoi 数据脱敏
开发语言·python·excel
fish_xk6 小时前
c++类和对象(上)
c++
Hello World呀6 小时前
Java实现手机号和身份证号脱敏工具类
java·开发语言
曹牧6 小时前
Java:serialVersionUID
java·开发语言
ekprada7 小时前
DAY36 复习日
开发语言·python·机器学习
qq_256247057 小时前
Rust 模块化单体架构:告别全局 Migrations,实现真正的模块自治
开发语言·架构·rust
历程里程碑7 小时前
C++ 6 :string类:高效处理字符串的秘密
c语言·开发语言·数据结构·c++·笔记·算法·排序算法
xu_yule7 小时前
算法基础-(数据结构)
数据结构