1 Star 2 Fork 0

daemon / CXXTOOLS

加入 Gitee
与超过 1200万 开发者一起发现、参与优秀开源项目,私有仓库也完全免费 :)
免费加入
该仓库未声明开源许可证文件(LICENSE),使用请关注具体项目描述及其代码上游依赖。
克隆/下载
贡献代码
同步代码
取消
提示: 由于 Git 不支持空文件夾,创建文件夹后会生成空的 .keep 文件
Loading...
README

CXXTOOLS

介绍

避免造轮子,从造轮子开始。 里面都是一些简短的好用的与具体项目无关的工具函数或者工具类。 不依赖三方库,且跨平台,支持c++14及以上,开箱即用。

软件架构

软件架构说明

安装教程

  1. 从gitee上拉下来

  2. 放入你的工程文件中

  3. 添加到工程目录里,如果是用vs,建议使用新建筛选器的方式添加。如果是qt

    则建议使用pri方式添加。

  4. 对于msvc,需要添加宏

    _CRT_SECURE_NO_WARNINGS _WINSOCK_DEPRECATED_NO_WARNINGS _SILENCE_ALL_CXX17_DEPRECATION_WARNINGS _CRT_NONSTDC_NO_DEPRECATE

    来避免一些独有的警告或者错误

  5. 对于TCP/UDP,如果要在windows下使用,需要添加库-lws2_32 -liphlpapi

  6. 对于日志类,如果在LogMod.cpp中定义了宏USE_FILESYSTEM,则需要在

    • qt下 增加c++14支持,添加库-lstdc++fs
    • vs下 增加c++17支持,且在属性->C/C++->命令行中添加 /Zc:__cplusplus

使用说明

基本和通用功能

声明文件Utils.h

  1. size_t ArraySize(T)

    编译期计算数组大小的函数,

  2. uint32_t MakeUInt32(uint16_t low, uint16_t high)

    用两个16位整数合并成一个无符号的32位整数

  3. int32_t MakeInt32(uint16_t low, uint16_t high)

    用两个16位整数合并成一个有符号的32位整数

  4. template <typename T> constexpr T ByteSwap(T source)

    交换字节,可用于大小端互换,支持8位,16位,32位,64位的无符号及有符号整数,float,double的字节交换

  5. template <typename T> inlineconstexpr T ToBigEndian(T source)

    大小端转换函数,共4个,这里只例举了一个

  6. inline constexpr uint32_t ToNetOrderL(uint32_t source)

    主机序与网络序转换函数共4个,用于替换ntohs,ntohl,htons,htonl4个函数,这里重写的原因是为了减少跨平台头文件依赖和库依赖。

  7. CHECK_FORMAT(str,arg)

    格式化字符串编译期检测宏,仅gcc下有效

  8. DISABLE_COPY(Class)

删除类的默认复制型构造和赋值函数

  1. DEFAULT_MOVE(Class)

    定义类的默认移动型构造和赋值函数,仅支持运用了pImpl机制的类

  2. Q_DECLARE_PRIVATE(Class) Q_DECLARE_PUBLIC(Class)

    qt中常用的技术,用于隐藏类的细节,减少继承类的大小。具体可自行查阅qt中的资料

  3. DEBUGSOCK(name)

    打印当前的网络错误,name为用到的网络相关函数

  4. INIT_WIN_SOCK CLEAN_WIN_SOCK

    初始化及清除网络函数。

线程池

声明文件ThreadPool.h

提供了一个HeaderOnly的线程池

  1. 使用示例

    int main(){

    tool::ThreadPool pool(8);//创建含8条线程的线程池

    提交一个普通任务

    auto f1 = pool.AddTask([](){

    return 1;

    });

    f1.get();//获取提交任务的计算结果

    return 0;

    }

网络地址

声明文件NetAdress.h

网络地址相关

  1. transAddr()

    包含两个重载函数,用于主机序32位整数和ip地址字符串之间的互换

  2. uint16_t HtoNs(uint16_t value)

    共4个函数,用于主机序和网络序之间的整数互换,这里之所以要重新定义主要是为了跨平台,且不再需要include跨平台相关的头文件。

  3. class NetAddress

    一个用于存储ipv4地址的类,可以使用多种常见的方式构造,包括(0x00000000,10000)或者("127.0.0.1",10000)或者("127.0.0.1:10000")或者(AddrID)或者(const sockaddr *)

    这里的AddrID定义位64位无符号整数,其内部结构为ip占据4字节,port占2字节,最后2字节为0。可用来当作网络地址的Hash值

    可以使用toKey()toString()来获取当前网络地址的64位Key和字符串表示

Tcp

声明文件:TcpSocket.h

Tcp相关,包含一个客户端和一个服务端。

这里多说一句,TCP和UDP中的四个类,请尽量不要用到new/delete的来构造,把这些类当作简单类型来使用。

注意notifyForQuit()这个函数,这个函数用来通知当前的类准备退出,调用后,会在合适的时候安全退出内部的线程,关闭连接。

class TcpServer
  1. 构造函数中包含一个数据回调函数。

  2. 当调用sendData()函数时,会像服务端当前连接中的所有客户端发送此消息。

  3. 该tcp中设置的keep_alive间隔为5s。每有一个连接进来都会创建一个新线程来监听数据。

  4. 示例

    #include "tools/tcpsocket.h"
    
    void connCb(tool::net::NetAddress addr, bool state)
    {
        if (state)
            printf("new client[%s] connected\n",addr.toString().data());
        else
            printf("client[%s] disconnected\n", addr.toString().data());
    }
    
    int main()
    {
        auto recvCb = [](const char* data, int size) {
            printf("server recv data[%d]:%s",size,data);
        };
    	tool::net::TcpServer server;
    	server = tool::net::TcpServer(recvCb,connCb);
    	server.connect("127.0.0.1",8888,1);//创建服务端
    
    	while (getch() != 'q');
    	return 0;
    }
class TcpClient
  1. 构造函数中包含两个回调函数,第一个回调函数是数据回调函数,当有客户端发来数据时触发,第二个回调函数是连接状态回调函数,每当有新的客户端连接或者旧的客户端断开就会触发此回调。

  2. 当调用sendData()函数时,会像服务端当前连接中的所有客户端发送此消息。

  3. 该tcp中设置的keep_alive间隔为5s。

  4. 示例,该示例每隔一秒向服务端发送一次消息,10次后停止发送。

    #include "tools/tcpsocket.h"
    #include <thread>
    
    int main()
    {
    	tool::net::TcpClient client("127.0.0.1",8888,nullptr);
        char data[] = "hello server\n";
    	for(int i = 0;i < 10;++i){
            client.sendData(data,sizeof(data));
            std::this_thread::sleep_for(std::chrono::seconds(1));
        }
    	while (getch() != 'q');
    	return 0;
    }
Udp

声明文件UdpSocket.h

这里的Udp使用的是组播,且不是以客户端和服务端来区分,而是以UdpRecver和UdpSender来区分,一个只用来接收组播地址的数据,一个只用来向组播地址发送数据,使用方式和TcpSocket类似,这里就不再赘述了。

数据解析类

对应头文件StreamParser.h

流数据解析相关

ascii和hex字符串转换
  1. std::string ToHex(const std::string& buffer)

    将字符串按ASCII码转换为16进制的字符串,如"hello"->"68656C6C6F"

  2. std::string FromHex(const std::string& buffer)

    将16进制格式的字符串转换为ASCII格式的字符串,如"68"->"h"

class BinaryStream

二进制数据流生成类,这是一个可以用来简单序列化数据的类。将数据以二进制的形式放入流而不是以文本的形式,支持基本类型,和容器类,

示例,示例中展示了如何将一个非POD数据序列化,并通过网络发送出去,

#include "tools/StreamParser.h"

struct Student{
    std::string name;
    int32_t id;
    std::vector<int64_t> phone;
    std::string address;
};

int main()
{
    tool::BinaryStream bs;
    Student stu;
    stu.name = "Tom";
    stu.id = 123456;
    stu.phone = {123456789,987654321};
    stu.address = "Null Street";
	bs << stu.name << stu.id << stu.phone << stu.address;

	tool::net::TcpClient client("127.0.0.1",8888,nullptr);
	client.sendData(bs.Data(),bs.Size());

	return 0;
}
class StreamParser

一个简单易用的数据提取工具,可用于tcp中的粘包或者文件读取,总共只有三个接口。

  1. void RegisterUpdator(std::function<void(const char*, int)> functor)

    注册数据回调函数,当我们从数据流中提取出一条完整的数据,通过这个回调通知。

  2. void InstallParser(std::function<int(const char*, int)> functor)

注册数据解析函数,可以自定义如何从数据中提取数据,大多数时候配合第四条中的函数使用就行了

  1. void Update(const char* data, int size)

    数据更新函数,每当收到新数据,通过这个接口传递数据。

  2. template<typename HTType, HTType Head, HTType Tail, typename SizeType>int UpdateCommonPackage(const char* data, int size)

    这个函数是一个模板函数,由于模板声明过长,这里并没有放完整,简单来说,如果一个数据

    满足(Head+Size+...+Tail)这样的形式,我们就可以使用这个函数来解析,其中的模板参数HTType表示帧头帧尾

    的类型,后面是帧头帧尾的参数,而SizeType则是紧接着帧头后表示长度的类型,需要注意的是,这里的长度

    是指含帧头帧尾的一共的字节数。

    输入参数为带解析的数据,输出参数为解析出来的包的长度,为0表示数据长度不够,为-1表示数据错误。

    示例中展示了如何创建一个服务器,并解析客户端发送过来的tsmr2期协议的数据。

    #include "tools/tcpsocket.h"
    
    void connCb(tool::net::NetAddress addr, bool state)
    {
        if (state)
            printf("new client[%s] connected\n",addr.toString().data());
        else
            printf("client[%s] disconnected\n", addr.toString().data());
    }
    
    #include "tools/StreamParser.h"
    
    int main()
    {
        using namespace std::placeholders;
    
        tool::StreamParser sp;
        sp.InstallParser(tool::UpdateCommonPackage<uint32_t,0x5A5A5A5A,0xA5A5A5A5,uint32_t>);
        sp.RegisterUpdator([](const char*,int) {
            printf("recv a package\n");
            });
        tool::net::TcpServer server(std::bind(&tool::StreamParser::Update, &sp, _1, _2),connCb);
        server.connect("127.0.0.1", 8888,1);
    
        while (getch() != 'q');
        return 0;
    }
  3. 12

日志类

日志类的相关声明可在头文件LogMod.h中查看

时间
  1. int64_t TimeStamp()

    返回时间戳,这里的时间戳是UTC时间,毫秒精度。

  2. std::string TimeToString(int64_t timeStamp, const std::string& format = "%Y-%m-%d %H:%M:%S")

    将时间戳按指定格式转换为字符串

  3. std::string TimeMsToString(int64_t timeStamp, const std::string& format = "%Y-%m-%d %H:%M:%S.%g")

    将时间戳按指定格式转换为字符串,支持毫秒精度,不过这个函数目前不太完善,会忽略时间中最前面的0,无法固定宽度

    比如如果是01:01.001则会输出为1.1.1

  4. std::vector<std::string> GetFilesFromDir(const std::string& dirName)

    获取指定路径下的文件,在windows下支持使用通配符搜索,linux下暂无此功能。

  5. void CreatePathForFile(const std::string& fileName)

    为一个带完整路径名的文件创建路径(如果这个路径不存在),在LogMod.cpp中有一个宏USE_FILESYSTEM,如果启用的话,

    会使用c++17中的filesystem来完成4,5函数,在这个模式下,该函数可以创建多个不存在的路径,如果不定义这个宏,则采用跨平台函数创建,仅支持创建一层路径。

  6. class Timer

    一个计算耗时的类。支持微秒精度

日志

日志内部中共有两个类,AsyncLog和SyncLog,请不要在工程中直接包含两个对应的头文件。而是只包含LogMod.h

可通过是否定义宏USE_ASYNC_LOG来选择使用哪种日志。

SyncLog属于比较老旧的版本,内部不会创建线程,结构比较简单,也不支持自定义格式输出。

建议使用AsyncLog,异步日志,启用后,内部会创建一个线程,定期同步日志。

日志共分为6个等级,Trace,Debug,Info,Warning,Error,Success,默认全部开启,如果要全部关闭,可定义宏LOG_NO_OUTPUT

如果要取消其中某一级,可通过定义LOG_NO_XXX的方式完成,如LOG_NO_DEBUG,定义这个宏后,则debug打印会失效。

  1. LOG_T,LOG_D,LOG_I,LOG_W,LOG_E,LOG_S(format , ...)

    格式话日志输出宏

  2. inline void InstallLogMessageScreenState(bool state)

    修改日志的屏幕输出状态,初始时默认开启。

  3. inline void InstallLogMessageFileState(bool state, const std::string& fileName = "")

    修改日志的文件输出状态,初始时默认关闭。当开启日志时,第二个参数表示日志的输出文件,如果使用默认参数,则会在当前文件下创建

    一个log文件夹,在其中生成一个含时间戳的log文件。

  4. inline void InstallLogMessageHandler(std::function<void(const char* data, int size)> writer, std::function<void()> flusher = nullptr, std::function<void()> closer = nullptr)

    重定向日志输出,共需要传递三个回调函数,分别是写回调,刷新回调,和关闭回调。比如我们有一个QTextEdit控件,我们需要把日志输出

    到这个控件里,则可以这样做,

    InstallLogMessageHandler([pEdit](const char* data,int size){``pEdit.append(data);});`

  5. void InstallLogMessageFormat(const char* data)

    设置日志前缀输出格式,默认没有,日志前缀指的是每条日志前的附加打印,包括时间戳,类型,文件,行数,函数名这几种。

    对应格式串为{time} {type} {file} {line} {function}。例如

    InstallLogMessageFormat("[{time} {file} {line} {function} {type}]");
    LOG_D("hello world");

    则输出 [2021-09-01 09:37:13.747 main.cpp 130 main DEBUG]hello world

  6. void InitLogMessage(int interval_ms = 3000)

    初始化日志,必须调用该语句后才能输出日志。参数表示刷新时间,单位毫秒,即每隔多长时间把相应的数据从缓存中取出。建议根据实际情况设置1~5s就够了。

    一个简单的使用日志的示例,展示了如何初始化日志,创建了一个TCP服务端,并使用日志打印客户端发送过来的消息。

    #include "tools/tcpsocket.h"
    #include "tools/LogMod.h"
    int mains()
    {
        InitLogMessage(1000);
        InstallLogMessageFileState(true);
    #ifdef NDEBUG
        InstallLogMessageFormat("[{time} {type}]");
    #else
        InstallLogMessageFormat("[{time} {file} {line} {function} {type}]");
        
    #endif
        LOG_D("hello world");
    
        auto recver = [](const char*data,int) {
            LOG_I("server recv:%s",data);
            };
        tool::net::TcpServer server(recver, nullptr);
        server.connect("127.0.0.1", 8888,1);
    
        while (getch() != 'q');
        return 0;
    }

贡献要求

  1. 放入的代码应该是用来解决某一类通用的功能的,而不是与具体工程相关的;

  2. 代码必须是跨平台的,头文件中不能include平台相关的头文件,所有功能必须放置在namespace tool下,不能使用三方库。

  3. 每个添加进去的新模块应该是独立的,即除了Utils.h,该模块不能依赖于其他模块,这样做的目的是,如果某个使用者只需要其中的一个功能

    那么他可以直接把功能相关的两个文件拉入工程而不是要把整个tools都拉进来。

参与贡献

  1. Fork 本仓库

  2. 新建 Feat_xxx 分支

  3. 提交代码

  4. 新建 Pull Request

特技

  1. 使用 Readme_XXX.md 来支持不同的语言,例如 Readme_en.md, Readme_zh.md
  2. Gitee 官方博客 blog.gitee.com
  3. 你可以 https://gitee.com/explore 这个地址来了解 Gitee 上的优秀开源项目
  4. GVP 全称是 Gitee 最有价值开源项目,是综合评定出的优秀开源项目
  5. Gitee 官方提供的使用手册 https://gitee.com/help
  6. Gitee 封面人物是一档用来展示 Gitee 会员风采的栏目 https://gitee.com/gitee-stars/

空文件

简介

避免造轮子,从造轮子开始。 里面都是一些简短的好用的与具体项目无关的工具函数或者工具类。 不依赖三方库,模块独立,且跨平台,支持c++14及以上。 展开 收起
取消

发行版

暂无发行版

贡献者

全部

近期动态

加载更多
不能加载更多了
C/C++
1
https://gitee.com/melphi/cxxtools.git
git@gitee.com:melphi/cxxtools.git
melphi
cxxtools
CXXTOOLS
master

搜索帮助