我怎样才能为一个类定义一个UUID,并且使用__uuidof,与g ++和Visual C ++一样? [英] How can I define an UUID for a class, and use __uuidof, in the same way for g++ and Visual C++?

查看:790
本文介绍了我怎样才能为一个类定义一个UUID,并且使用__uuidof,与g ++和Visual C ++一样?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

注意:这是 问题与答案 ,以便记录其他人可能会发现有用的技术,并为了或许意识到其他人更好的解决方案。随意添加批评或问题作为评论。也请随时添加其他答案。 :)




Visual C ++一直有一个语言扩展 __ uuidof ( classname ,可以检索 UUID 如果UUID已经通过 __ declspec 与类相关联,这也是一种Visual C ++语言扩展:

  #include< guiddef.h> // GUID,UUID的另一个名称

class
__declspec(uuid(290ff5cb-3a21-4740-bfda-2697ca13deae))
Foo
{};

#include< iostream>
使用namespace std;

auto main()
- > int
{
cout<<十六进制<< __uuidof(Foo).Data1<< ENDL; / / 290ff5cb
}

MinGW g ++ 4.8.2(可能还有一些早期版本)支持 __ uuidof ,但它不支持MSVC的 __ declspec 。因此,用g ++ 4.8.2编译上述代码失败了,至少在我使用的Nuwen发行版中是如此。首先g ++发出警告''uuid'属性指令被忽略',然后得到一个链接器错误'undefined reference to _GUID const& __mingw_uuidof< Foo>()"。



错误提示UUID与g ++的类关联的方式,该类的函数模板 __ mingw_uuidof



不幸的是,UUID规范是一种与Visual C ++形式截然不同的形式。它的数字,而不是一个字符串。它并没有隐藏在 struct 关键字之后,而是遵循类的声明:

  #include< guiddef.h> // GUID,UUID的另一个名称

class Foo {};

模板<>
auto __mingw_uuofof< Foo>()
- > GUID const&
{
static const GUID the_uuid =
{
0x290ff5cb,0x3a21,0x4740,
{0xbf,0xda,0x26,0x97,0xca,0x13,0xde,0xae}
};
返回the_uuid;
}

#include< iostream>
使用namespace std;

auto main()
- > int
{
cout<<十六进制<< __uuidof(Foo).Data1<< ENDL; // 290ff5cb
}

如何将UUID与类关联起来,与两个编译器一起工作’ __ uuidof ,没有冗余,最好用UUID作为数字的直接序列(与Visual C ++一样)?

解决方案

前面的免责声明:没有任何内容已经过广泛的测试或审查。我只是写了它。



一个可能的统一方法是由这个事实提出的:


  • 不需要为类的第一个声明提供一个Visual C ++ __ declspec(uuid):它可以在第一个声明。



例如Visual C ++代码可能如下所示:

  class Foo 
{};

class __declspec(uuid(290ff5cb-3a21-4740-bfda-2697ca13deae))Foo;

因此,which-compiler-is-it嗅探宏 CPPX_UUID_FOR 可定义为…

  #if!defined(CPPX_UUID_FOR)
#if defined(_MSC_VER)
#define CPPX_UUID_FOR CPPX_MSVC_UUID_FOR
#elif defined(__GNUC__)
#define CPPX_UUID_FOR CPPX_GNUC_UUID_FOR
#endif
#endif

并在该类的第一个声明之后被调用:

  #include< iostream> 
使用namespace std;

struct Foo {};
CPPX_UUID_FOR(Foo,dbe41a75-d5da-402a-aff7-cd347877ec00);

void test()
{
using cppx :: uuid :: operator<< ;;
cout<< (20)<< __uuidof:<< __uuidof(Foo)<< ENDL;
}

Visual C ++的宏实现是微不足道的:

  #define CPPX_MSVC_UUID_FOR(name,spec)\ 
class __declspec(uuid(spec))name

g ++的宏实现有点涉及:

  #define CPPX_GNUC_UUID_FOR(name,spec)\ 
template<> \
inline \
auto __mingw_uuidof< name>()\
- > GUID const& \
{\
使用cppx :: operator_uuid; \
static constexpr GUID the_uuid = spec ## _uuid; \
\
返回the_uuid; \
} \
\
模板<> \
inline \
auto __mingw_uuidof< name *>()\
- > GUID const& \
{return __mingw_uuidof< name>(); } \
\
static_assert(true,)

&hellip ;其中 static_assert 仅用于支持调用中的最终分号。



用户定义的文字不是必须的,但我认为这样做很有趣。
$ b cppx :: operator_uuid 定义在命名空间 cppx 中:

  namespace detail {
CPPX_CONSTEXPR
auto uuid_from_spec(char const * const s,size_t const size)
- > cppx :: Uuid
{
return(
size == 36?cppx :: uuid :: from(
reinterpret_cast< char const(&)[37]>( * s)
):
cppx :: fail(
uuid规范必须是36个字符,例如
\dbe41a75-d5da-402a-aff7-cd347877ec00 \

);
}
} //命名空间详细信息

#if!(defined(_MSC_VER)|| defined(NO_USER_LITERALS))
CPPX_CONSTEXPR
自动操作符 _uuid(char const * const s,size_t const size)
- > cppx :: Uuid
{return detail:uuid_from_spec(s,size); }
#endif

其中 cppx :: uuid :: from 在名称空间 cppx :: uuid 中定义:

  inline CPPX_CONSTEXPR 
auto from(char const(& spec)[37])
- > Uuid
{return可初始化(ce,spec); }

其中 ce 只是一个构造函数标记,枚举类型 Const_expr ,它选择<$ c $>的 constexpr 的构造函数
$ b $ $ p $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ b explicit CPPX_CONSTEXPR
可初始化(Const_expr,char const(& spec)[37])
:Uuid({
// Data1
((((((( (((((
static_cast< unsigned long>(nybble_from_hex(spec [0]))
< 4)| nybble_from_hex(spec [1]))
<< 4)| nybble_from_hex(spec [2]))
<< 4)| nybble_from_hex(spec [3]))
<4)| nybble_from_hex(spec [4]))
<4)| nybble_from_hex(spec [5]))
<4)| nybble_from_hex(spec [6]))
<< 4)| nybble_from_hex(spec [7]),
// Data2
static_cast< unsigned short>(
(((((
static_cast< unsigned>(nybble_from_hex(spec [9])) )
<4)| nybble_from_hex(spec [10]))
<4)| nybble_from_hex(spec [11]))
<< 4) nybble_from_hex(spec [12])
),
// Data 3
static_cast< unsigned short>(
(((((
static_cast< unsigned>(nybble_from_hex (spec [14]))
<4)| nybble_from_hex(spec [15]))
<4)| nybble_from_hex(spec [16]))
< ;< 4)| nybble_from_hex(spec [17])
),
//数据4
{
(byte_from_hex(spec [19],spec [20])),
static_cast< unsigned char>(byte_from_hex(spec [21],spec [22])),
static_cast< (byte_from_hex(spec [24],spec [25])),
static_cast< unsigned char>(byte_from_hex(spec [26],spec [27])),
static_cast< unsigned char> ; byte_from_hex(spec [28],spec [29])),
static_cast< unsigned char>(byte_from_hex(spec [30],spec [31])),
static_cast< unsigned char>( (byte_from_hex(spec [34],spec [35]))
}
})


explicit
可以初始化(char const(& spec)[37])
:Uuid()
{
for(int i = 0;我< 8; ++ i)
{
Uuid :: Data1 =(Uuid :: Data1 <4)| nybble_from_hex(spec [i]);
}
assert(spec [8] ==' - '); (int i = 9; i <13; ++ i)
{
Uuid :: Data2 =(Uuid :: Data2 <4)| nybble_from_hex(spec [i]);
}
assert(spec [13] ==' - '); (int i = 14; i <18; ++ i)
{
Uuid :: Data3 =(Uuid :: Data3 <4)| nybble_from_hex(spec [i]);
}
assert(spec [18] ==' - '); (int i = 19; i <23; i + = 2)
{
Uuid :: Data4 [(i-19)/ 2] = byte_from_hex(spec [i] ,spec [i + 1]);
}
assert(spec [23] ==' - '); (int i = 24; i <36; i + = 2)
{
Uuid :: Data4 [2 +(i-24)/ 2] = byte_from_hex(spec [ i],spec [i + 1]);
}
}
};

这两个构造函数的区别主要在于判断代码的正确与否,最后一个(我先写的!)也有用 assert 语句。我不知道如何最好地为 constexpr 构造函数做这样的 assert 。或甚至是否可行,这是为什么有两个构造函数而不是一个构造函数的原因之一。



哦,<< / code>这里的调用只是很好的左移,而不是花哨的自定义操作符符号输出或流或存储操作。 :)




nybble_from_hex byte_from_hex 非常简单,但失败函数有点微妙。尽管表面上并没有 a constexpr 函数。相反,它是不返回功能。 C ++ 11有一个符号表示, [[noreturn]] ,但据我所知,Visual C ++和g ++都不支持。因此,我使用了特定于编译器的注释,如下所示:

  #if!defined(CPPX_NORETURN)
#if defined _MSC_VER)
#define CPPX_NORETURN __declspec(noreturn)
#elif defined(__GNUC__)
#define CPPX_NORETURN __attribute __((noreturn))
#else
#define CPPX_NORETURN [ [noreturn]]
#endif
#endif

然后 fail 可以简单地编码为例如

  struct无论
{
模板<班级类型>
CPPX_CONSTEXPR运算符类型()const {return Type(); }
};

内嵌
CPPX_NORETURN
自动失败(字符串常量& s)
- >无论
{throw runtime_error(s); }

我发现表达失败的不平凡(也可能不可能) 当它有 std :: string 参数时作为 constexpr 函数,并且作为普通它的函数调用会抑制 constexpr 属性。不返回的变体适用于g ++。但是,我不确定该标准对此有何评论。


Note: This is a question-with-answer in order to document a technique that others might find useful, and in order to perhaps become aware of others’ even better solutions. Do feel free to add critique or questions as comments. Also do feel free to add additional answers. :)


Visual C++ has always had a language extension __uuidof(classname) that can retrieve an UUID, a 128 bit Universally Unique Identifier, provided that the UUID’s been associated with the class via __declspec, which also is a Visual C++ language extension:

#include <guiddef.h>        // GUID, another name for UUID

class
    __declspec( uuid( "290ff5cb-3a21-4740-bfda-2697ca13deae" ) )
    Foo
{};

#include <iostream>
using namespace std;

auto main()
    -> int
{
    cout << hex << __uuidof( Foo ).Data1 << endl;   // 290ff5cb
}

MinGW g++ 4.8.2 (and possibly some earlier versions) supports __uuidof, but it does not support MSVC’s __declspec. Compiling the above with g++ 4.8.2 therefore fails, at least with the Nuwen distribution that I use. First g++ issues the warning “'uuid' attribute directive ignored”, and then one gets a linker error “undefined reference to _GUID const& __mingw_uuidof<Foo>()”.

The error hints at the way an UUID is associated with a class for g++, namely by specializing the function template __mingw_uuidof for the class.

Unfortunately the UUID specification is then of a form that’s radically different from the Visual C++ form. It’s numbers, not a string. And it’s not tucked in after the class or struct keyword, but follows a declaration of the class:

#include <guiddef.h>        // GUID, another name for UUID

class Foo {};

template<>
auto __mingw_uuidof<Foo>()
    -> GUID const&
{
    static const GUID the_uuid = 
    {
        0x290ff5cb, 0x3a21, 0x4740,
        { 0xbf, 0xda, 0x26, 0x97, 0xca, 0x13, 0xde, 0xae }
    };
    return the_uuid;
}

#include <iostream>
using namespace std;

auto main()
    -> int
{
    cout << hex << __uuidof( Foo ).Data1 << endl;   // 290ff5cb
}

How can one associate an UUID with a class so that it will work with both compilers’ __uuidof, without redundancy, and preferably with the UUID as a direct sequence of digits (as with Visual C++)?

解决方案

Up-front disclaimer: nothing of this has been extensively tested or reviewed. I just wrote it.

One possible unification approach is suggested by this fact:

  • A Visual C++ __declspec( uuid ) doesn’t need to be provided with the first declaration of a class: it can be applied after the first declaration.

E.g. Visual C++ code can look like this:

class Foo
{};

class  __declspec( uuid( "290ff5cb-3a21-4740-bfda-2697ca13deae" ) ) Foo;

Thus a which-compiler-is-it sniffing macro CPPX_UUID_FOR can be defined as …

#if !defined( CPPX_UUID_FOR )
#   if defined( _MSC_VER )
#       define CPPX_UUID_FOR    CPPX_MSVC_UUID_FOR
#   elif defined( __GNUC__ )
#       define CPPX_UUID_FOR    CPPX_GNUC_UUID_FOR
#   endif
#endif

and be invoked after the first declaration of the class:

#include <iostream>
using namespace std;

struct Foo {};
CPPX_UUID_FOR( Foo, "dbe41a75-d5da-402a-aff7-cd347877ec00" );

void test()
{
    using cppx::uuid::operator<<;
    cout << setw( 20 ) << "__uuidof: " << __uuidof( Foo ) << endl;
}

The macro implementation for Visual C++ is trivial:

#define CPPX_MSVC_UUID_FOR( name, spec )    \
    class __declspec( uuid( spec ) ) name

The macro implementation for g++ is a bit more involved:

#define CPPX_GNUC_UUID_FOR( name, spec )    \
template<>                                  \
inline                                      \
auto __mingw_uuidof<name>()                 \
    -> GUID const&                          \
{                                           \
    using cppx::operator"" _uuid;           \
    static constexpr GUID the_uuid = spec ## _uuid; \
                                            \
    return the_uuid;                        \
}                                           \
                                            \
template<>                                  \
inline                                      \
auto __mingw_uuidof<name*>()                \
    -> GUID const&                          \
{ return __mingw_uuidof<name>(); }          \
                                            \
static_assert( true, "" )

… where the static_assert only serves to support a final semicolon in the invocation.

The user defined literal is not strictly necessary, but I thought it was interesting to do it.

cppx::operator"" _uuid is defined thusly, in namespace cppx:

namespace detail {
    CPPX_CONSTEXPR
    auto uuid_from_spec( char const* const s, size_t const size )
        -> cppx::Uuid
    {
        return (
            size == 36?   cppx::uuid::from(
                    reinterpret_cast<char const (&)[37]>( *s )
                            ) :
            cppx::fail(
                "An uuid spec must be 36 chars, like"
                " \"dbe41a75-d5da-402a-aff7-cd347877ec00\""
                )
            );
    }
}  // namespace detail

#if !(defined( _MSC_VER ) || defined( NO_USER_LITERALS ))
CPPX_CONSTEXPR
auto operator"" _uuid( char const* const s, size_t const size )
    -> cppx::Uuid
{ return detail::uuid_from_spec( s, size ); }
#endif

where cppx::uuid::from is defined earlier in namespace cppx::uuid:

inline CPPX_CONSTEXPR
auto from( char const (&spec)[37] )
    -> Uuid
{ return Initializable( ce, spec ); }

where ce is just a constructor tag, of enumeration type Const_expr, that selects the constexpr constructor of the uuid::Initializable class:

struct Initializable: Uuid
{
    explicit CPPX_CONSTEXPR
    Initializable( Const_expr, char const (&spec)[37] )
        : Uuid( {
                // Data1
                (((((((((((((
                    static_cast<unsigned long>( nybble_from_hex( spec[0] ) )
                    << 4) | nybble_from_hex( spec[1] ))
                    << 4) | nybble_from_hex( spec[2] ))
                    << 4) | nybble_from_hex( spec[3] ))
                    << 4) | nybble_from_hex( spec[4] ))
                    << 4) | nybble_from_hex( spec[5] ))
                    << 4) | nybble_from_hex( spec[6] ))
                    << 4) | nybble_from_hex( spec[7] ),
                // Data2
                static_cast<unsigned short>(
                    (((((
                        static_cast<unsigned>( nybble_from_hex( spec[9] ) )
                        << 4) | nybble_from_hex( spec[10] ))
                        << 4) | nybble_from_hex( spec[11] ))
                        << 4) | nybble_from_hex( spec[12] )
                    ),
                // Data 3
                static_cast<unsigned short>(
                    (((((
                        static_cast<unsigned>( nybble_from_hex( spec[14] ) )
                        << 4) | nybble_from_hex( spec[15] ))
                        << 4) | nybble_from_hex( spec[16] ))
                        << 4) | nybble_from_hex( spec[17] )
                    ),
                // Data 4
                {
                    static_cast<unsigned char>( byte_from_hex( spec[19], spec[20] ) ),
                    static_cast<unsigned char>( byte_from_hex( spec[21], spec[22] ) ),
                    static_cast<unsigned char>( byte_from_hex( spec[24], spec[25] ) ),
                    static_cast<unsigned char>( byte_from_hex( spec[26], spec[27] ) ),
                    static_cast<unsigned char>( byte_from_hex( spec[28], spec[29] ) ),
                    static_cast<unsigned char>( byte_from_hex( spec[30], spec[31] ) ),
                    static_cast<unsigned char>( byte_from_hex( spec[32], spec[33] ) ),
                    static_cast<unsigned char>( byte_from_hex( spec[34], spec[35] ) )
                }
            } )
    {}

    explicit
    Initializable( char const (&spec)[37] )
        : Uuid()
    {
        for( int i = 0;  i < 8;  ++i )
        {
            Uuid::Data1 = (Uuid::Data1 << 4) | nybble_from_hex( spec[i] );
        }
        assert( spec[8] == '-' );
        for( int i = 9;  i < 13;  ++i )
        {
            Uuid::Data2 = (Uuid::Data2 << 4) | nybble_from_hex( spec[i] );
        }
        assert( spec[13] == '-' );
        for( int i = 14; i < 18;  ++i )
        {
            Uuid::Data3 = (Uuid::Data3 << 4) | nybble_from_hex( spec[i] );
        }
        assert( spec[18] == '-' );
        for( int i = 19; i < 23;  i += 2 )
        {
            Uuid::Data4[(i - 19)/2] = byte_from_hex( spec[i], spec[i + 1] );
        }
        assert( spec[23] == '-' );
        for( int i = 24; i < 36;  i += 2 )
        {
            Uuid::Data4[2 + (i - 24)/2] = byte_from_hex( spec[i], spec[i + 1] );
        }
    }
};

The two constructors mainly differ in how easy it is to judge the correctness or not of the code, but the last one (I wrote that first!) also has useful assert statements. I'm not sure how to best do such assertions for the constexpr constructor. Or even whether that is doable, which is one reason why there are two constructors instead of just one.

Oh, the << invocations here are just good old left-shifts, not fancy custom operator-notation output or stream or store operations. :)


The definitions of nybble_from_hex and byte_from_hex are pretty trivial, but the fail function is a bit subtle. In spite of appearances it’s not a constexpr function. Instead, it’s a non-returning function. C++11 has a notation to express that, [[noreturn]], but as far as I know neither Visual C++ nor g++ supports that yet. So instead I use compiler specific annotations, like this:

#if !defined( CPPX_NORETURN )
#   if defined( _MSC_VER )
#       define CPPX_NORETURN    __declspec( noreturn )
#   elif defined( __GNUC__ )
#       define CPPX_NORETURN    __attribute__((noreturn))
#   else
#       define CPPX_NORETURN    [[noreturn]]
#   endif
#endif

and then fail can be coded up simply as e.g.

struct Whatever
{
    template< class Type >
    CPPX_CONSTEXPR operator Type () const { return Type(); }
};

inline
CPPX_NORETURN
auto fail( string const& s )
    -> Whatever
{ throw runtime_error( s ); }

I found it non-trivial (and possibly impossible) to express fail as a constexpr function when it has std::string argument, and as an ordinary function calls of it suppressed the constexpr property. The non-returning variant works OK with g++. However, I’m not sure what the standard has to say about this.

这篇关于我怎样才能为一个类定义一个UUID,并且使用__uuidof,与g ++和Visual C ++一样?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆