Che*_*Alf 3 c++ winapi uuid g++ visual-c++
注意:这是一个有问必答的问题,目的是记录其他人可能会发现有用的技术,并可能了解其他人的更好解决方案。请随意添加批评或问题作为评论。也可以随意添加其他答案。:)
Visual C++ 一直有一个语言扩展__uuidof(类名),可以检索UUID,一个 128 位通用唯一标识符,前提是 UUID 通过 与类关联__declspec,这也是一个 Visual C++ 语言扩展:
#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
}
Run Code Online (Sandbox Code Playgroud)
MinGW g++ 4.8.2(可能还有一些早期版本)支持__uuidof,但不支持 MSVC 的__declspec. 因此,使用 g++ 4.8.2 编译上述内容会失败,至少在我使用的 Nuwen 发行版中是这样。首先 g++ 发出警告“'uuid'属性指令被忽略”,然后一个链接器错误“未定义引用到_GUID const& __mingw_uuidof<Foo>()”。
该错误提示 UUID 与 g++ 的类相关联的方式,即通过专门化__mingw_uuidof该类的函数模板。
不幸的是,UUID 规范的形式与 Visual C++ 形式完全不同。是数字,不是字符串。它没有隐藏在classorstruct关键字之后,而是遵循类的声明:
#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
}
Run Code Online (Sandbox Code Playgroud)
如何将 UUID 与类相关联,以便它可以与两个编译器一起使用__uuidof,而没有冗余,并且最好将 UUID 作为直接的数字序列(如 Visual C++)?
预先免责声明:这些都没有经过广泛的测试或审查。我刚写的。
这一事实提出了一种可能的统一方法:
__declspec( uuid )不需要提供类的第一个声明:它可以在第一个声明之后应用。例如,Visual C++ 代码可能如下所示:
class Foo
{};
class __declspec( uuid( "290ff5cb-3a21-4740-bfda-2697ca13deae" ) ) Foo;
Run Code Online (Sandbox Code Playgroud)
因此,哪个编译器是它的嗅探宏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
Run Code Online (Sandbox Code Playgroud)
并在类的第一个声明之后调用:
#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;
}
Run Code Online (Sandbox Code Playgroud)
Visual C++ 的宏实现是微不足道的:
#define CPPX_MSVC_UUID_FOR( name, spec ) \
class __declspec( uuid( spec ) ) name
Run Code Online (Sandbox Code Playgroud)
g++ 的宏实现有点复杂:
#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, "" )
Run Code Online (Sandbox Code Playgroud)
...static_assert唯一的作用是支持调用中的最后一个分号。
在字面定义的用户不是绝对必要的,但我认为这是件有趣的事情吧。
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(
"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
Run Code Online (Sandbox Code Playgroud)
cppx::uuid::from在命名空间中更早地定义了where cppx::uuid:
inline CPPX_CONSTEXPR
auto from( char const (&spec)[37] )
-> Uuid
{ return Initializable( ce, spec ); }
Run Code Online (Sandbox Code Playgroud)
wherece只是一个构造函数标记,枚举类型Const_expr,它选择类的constexpr构造函数uuid::Initializable:
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] );
}
}
};
Run Code Online (Sandbox Code Playgroud)
两个构造函数的区别主要在于判断代码正确与否的难易程度,但最后一个(我先写的!)也有有用的assert语句。我不确定如何最好地assert为constexpr构造函数做这样的离子。或者甚至这是否可行,这就是为什么有两个构造函数而不是一个构造函数的原因之一。
哦,<<这里的调用只是旧的左移,而不是花哨的自定义运算符符号输出或流或存储操作。:)
的定义nybble_from_hex和byte_from_hex相当琐碎,但fail功能有点微妙。尽管外观,它不是 一个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
Run Code Online (Sandbox Code Playgroud)
然后fail可以简单地编码为例如
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 ); }
Run Code Online (Sandbox Code Playgroud)
我发现当它有参数时将其表达fail为constexpr函数是不平凡的(并且可能是不可能的)std::string,并且作为它的普通函数调用抑制了该constexpr属性。非返回变体与 g++ 一起工作正常。但是,我不确定标准对此有何看法。
| 归档时间: |
|
| 查看次数: |
4532 次 |
| 最近记录: |