现在的位置: 首页 > 自动控制 > 工业·编程 > 正文

Windows/Linux下C++对于UUID的跨平台封装

2020-07-05 08:52 工业·编程 ⁄ 共 4670字 ⁄ 字号 暂无评论

Universally Unique Identifier,UUID,通用唯一识别码。是用于计算机体系中以识别信息数目的一个128位标识符,这个东西很有用,在分布式系统中经常用于标识一个结点。根据标准方法生成,不依赖中央机构的注册和分配,UUID具有唯一性,这与其他大多数编号方案不同。重复UUID码概率接近零,可以忽略不计。

UUID是128位,16个字节,可以用32个HEX进制的数字表示。标准的表示格式是8-4-4-4-12总共 36 个字符(32 个字母数字字符和 4 个连字符),如下所示:

123E4567-E89B-12D3-A456-426655440F00

其实你自己也可以搞这么长的字符串作为UUID,但是UUID的质量取决于重复率。如果你的UUID质量不行,那么很大可能就会生成重复的UUID,这样子会出问题。

因此,UUID的标准定义了5个版本的UUID,分别如下

“版本1” UUID 是根据时间和节点 ID(通常是MAC地址)生成;

“版本2” UUID是根据标识符(通常是组或用户ID)、时间和节点ID生成;

“版本3” 和 “版本5” 确定性UUID 通过散列 (hashing) 名字空间 (namespace) 标识符和名称生成;

“版本4” UUID 使用随机性或伪随机性生成。

所以,UUID的好坏最终还是落到了随机生成器的质量上

那么在Windows/Linux下如何生成一个高质量的UUID呢?

可以使用boost准标准库,代码如下

#include <boost/uuid/uuid.hpp>

#include <boost/uuid/uuid_io.hpp>

#include <boost/uuid/uuid_generators.hpp>

 

boost::uuids::uuid rand= boost::uuids::random_generator()();

const string uuid_string = boost::uuids::to_string(rand);

这是通用的,下面说一下在Windows和Linux这两个平台下C++程序如何生成UUID。

Windows

Windows下利用Windows系统API就好了。有一个结构叫GUID,配合上CoCreateGuid就行

#include <objbase.h>

HRESULT CoCreateGuid(

  GUID *pguid

);

typedef struct _GUID {

    unsigned long  Data1;

    unsigned short Data2;

    unsigned short Data3;

    unsigned char  Data4[ 8 ];

} GUID;

sizeof(GUID) = 16,刚好是128位。

注意,UUID在许多系统上都是大端序编码UUID,但是在Windows下采用混合段格式,即UUID的前三组(8-4-4)用小端序,后两组(4-12)用大端序。

Linux

Linux下利用libuuid这个库来生成,Ubuntu下可用sudo apt-get install uuid-dev来安装,在链接的时候加上-luuid就行了。使用man 3 uuid可以看看支持哪些方法,或者直接在头文件中搜一下uuid_t

这个库支持生成UUID的不同算法,可以对应着UUID的五个版本看看。

sizeof(uuid_t) = 16,刚好128个字节。

跨平台封装

现在Windows下和Linux下生成可靠UUID的代码方法都知道了,我们就自己封装一个跨平台的UUID类Uuid

// uuid.h

#pragma once

#include <stdint.h>

#include <array>

#include <string>

class Uuid

{

public:

    Uuid();

    ~Uuid();

    static Uuid GenerateRandom();

    std::string toString(const std::string& split = "-", bool up = true);

private:

    typedef uint8_t ValueType;

    static const size_t DATA_LENGTH = 16;

    friend bool operator==(Uuid const & lhs, Uuid const & rhs) noexcept;

    friend bool operator<(Uuid const & lhs, Uuid const & rhs) noexcept;

    std::array<ValueType, DATA_LENGTH> _data;

};

// uuid.c

#include "uuid.h"

#include <sstream>

#include <iomanip>

#include <ios>

#ifdef _WIN32

#include <objbase.h>

#else

#include <uuid/uuid.h>

#endif

namespace Zeus

{

Uuid::Uuid()

    :_data{ 0 }

{

}

Uuid::~Uuid()

{

}

Uuid Uuid::GenerateRandom()

{

    Uuid result;

#ifdef _WIN32

    GUID newId;

    ::CoCreateGuid(&newId);

    std::array<ValueType, DATA_LENGTH> bytes =

    { {

            // Big-Endian

            (ValueType)((newId.Data1 >> 24) & 0xFF),

            (ValueType)((newId.Data1 >> 16) & 0xFF),

            (ValueType)((newId.Data1 >> 8) & 0xFF),

            (ValueType)((newId.Data1) & 0xFF),

            (ValueType)((newId.Data2 >> 8) & 0xFF),

            (ValueType)((newId.Data2) & 0xFF),

            (ValueType)((newId.Data3 >> 8) & 0xFF),

            (ValueType)((newId.Data3) & 0xFF),

            // Little-Endian

            newId.Data4[0],

            newId.Data4[1],

            newId.Data4[2],

            newId.Data4[3],

            newId.Data4[4],

            newId.Data4[5],

            newId.Data4[6],

            newId.Data4[7]

        } };

    result._data = bytes;

#else

    uuid_t id;

    uuid_generate(id);

    std::array<ValueType, DATA_LENGTH> bytes =

    { {

            id[0],

            id[1],

            id[2],

            id[3],

            id[4],

            id[5],

            id[6],

            id[7],

            id[8],

            id[9],

            id[10],

            id[11],

            id[12],

            id[13],

            id[14],

            id[15]

        } };

    result._data = bytes;

#endif

    return result;

}

std::string Uuid::toString(const std::string& split, bool up)

{

    std::stringstream str;

    if (up)

    {

        str.setf(std::ios_base::uppercase);

    }

    else

    {

        str.unsetf(std::ios_base::uppercase);

    }

    str << std::hex << std::setfill('0')

        << std::setw(2) << (int)_data[0]

        << std::setw(2) << (int)_data[1]

        << std::setw(2) << (int)_data[2]

        << std::setw(2) << (int)_data[3]

        << split

        << std::setw(2) << (int)_data[4]

        << std::setw(2) << (int)_data[5]

        << split

        << std::setw(2) << (int)_data[6]

        << std::setw(2) << (int)_data[7]

        << split

        << std::setw(2) << (int)_data[8]

        << std::setw(2) << (int)_data[9]

        << split

        << std::setw(2) << (int)_data[10]

        << std::setw(2) << (int)_data[11]

        << std::setw(2) << (int)_data[12]

        << std::setw(2) << (int)_data[13]

        << std::setw(2) << (int)_data[14]

        << std::setw(2) << (int)_data[15];

    return str.str();

}

bool operator==(Uuid const & lhs, Uuid const & rhs) noexcept

{

    return lhs._data == rhs._data;

}

bool operator<(Uuid const & lhs, Uuid const & rhs) noexcept

{

    return lhs._data < rhs._data;

}

最后来使用一下

#include "uuid.h"

int main()

{

    std::cout << Uuid::GenerateRandom().toString() << std::endl;

    std::cout << Uuid::GenerateRandom().toString("") << std::endl;

    std::cout << Uuid::GenerateRandom().toString("", false) << std::endl;

    return 0;

}

输出的结果:

9747E266-0367-11E9-B6C5-0800274F3274

8FB2028E036711E9AF550800274F3274

54293386036711e990b40800274f3274

给我留言

留言无头像?