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

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 
#include 
#include 
  
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 

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

Windows/Linux下C++对于UUID的跨平台封装_第1张图片

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

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

Code

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

// uuid.h

#pragma once

#include 
#include 
#include 

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 
#include 
#include 
#ifdef _WIN32
#include 
#else
#include 
#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

你可能感兴趣的:(Linux/Unix,C/C++技巧,Windows)