How can I define an UUID for a class, and use __uuidof, in the same way for g++ and Visual C++?

Cheers and hth. - Alf

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++)?

Cheers and hth. - Alf

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.

本文收集自互联网,转载请注明来源。

如有侵权,请联系[email protected] 删除。

编辑于
0

我来说两句

0条评论
登录后参与评论

相关文章

来自分类Dev

如何为类定义UUID并使用__uuidof,以与g ++和Visual C ++相同的方式?

来自分类Dev

How can I run several projects on the same solution in Visual Studio

来自分类Dev

How can I define multiple url helpers for same route entry in Rails?

来自分类Dev

Why can't I define a variable inside a for loop in this way?

来自分类Dev

How can I embed C# assembly resources in the same assembly?

来自分类Dev

How do I use an extension function in another class? C#

来自分类Dev

C 主卷窗口中的 __uuidof

来自分类Dev

How can I apply a jQuery function to all elements with the same class.?

来自分类Dev

How can I do to use a email service for a same domain on other server

来自分类Dev

I can "pickle local objects" if I use a derived class?

来自分类Dev

查找imapi2 com对象的uuid /标题或使__uuidof在mingw上工作

来自分类Dev

Can I use the same DB for multiple Meteor apps?

来自分类Dev

How can I tell if an UUID generated by QUuid::createUuid() is based on entrophy of cryptographic quality?

来自分类Dev

Can I / Should I serialize a constant variable in a C# class?

来自分类Dev

How can I use the constraints in native webrtc?

来自分类Dev

How can I save a <template> for a second use?

来自分类Dev

SWIFT: How I can use subscriberCellularProviderDidUpdateNotifier with Swift

来自分类Dev

How can I change the keyboard shortcuts in Visual Studio?

来自分类Dev

How can I run SpecFlow tests outside of visual studio

来自分类Dev

How can I combine a line and scatter on same plotly chart?

来自分类Dev

Why can't I use alias from a base class in a derived class with templates?

来自分类Dev

How can I determine the default encoding in a portable class library?

来自分类Dev

How can I copy a templated instance with a virtual base class?

来自分类Dev

In PHPUnit, how can I mock a function that is not part of a class?

来自分类Dev

How can I zero just the padding bytes of a class?

来自分类Dev

How can I test a final class with private constructor?

来自分类Dev

How can I get the application path in C?

来自分类Dev

How can I use Laravel form model binding with bootstrap?

来自分类Dev

How can I use a ternary operator for Graphql frontmatter?

Related 相关文章

  1. 1

    如何为类定义UUID并使用__uuidof,以与g ++和Visual C ++相同的方式?

  2. 2

    How can I run several projects on the same solution in Visual Studio

  3. 3

    How can I define multiple url helpers for same route entry in Rails?

  4. 4

    Why can't I define a variable inside a for loop in this way?

  5. 5

    How can I embed C# assembly resources in the same assembly?

  6. 6

    How do I use an extension function in another class? C#

  7. 7

    C 主卷窗口中的 __uuidof

  8. 8

    How can I apply a jQuery function to all elements with the same class.?

  9. 9

    How can I do to use a email service for a same domain on other server

  10. 10

    I can "pickle local objects" if I use a derived class?

  11. 11

    查找imapi2 com对象的uuid /标题或使__uuidof在mingw上工作

  12. 12

    Can I use the same DB for multiple Meteor apps?

  13. 13

    How can I tell if an UUID generated by QUuid::createUuid() is based on entrophy of cryptographic quality?

  14. 14

    Can I / Should I serialize a constant variable in a C# class?

  15. 15

    How can I use the constraints in native webrtc?

  16. 16

    How can I save a <template> for a second use?

  17. 17

    SWIFT: How I can use subscriberCellularProviderDidUpdateNotifier with Swift

  18. 18

    How can I change the keyboard shortcuts in Visual Studio?

  19. 19

    How can I run SpecFlow tests outside of visual studio

  20. 20

    How can I combine a line and scatter on same plotly chart?

  21. 21

    Why can't I use alias from a base class in a derived class with templates?

  22. 22

    How can I determine the default encoding in a portable class library?

  23. 23

    How can I copy a templated instance with a virtual base class?

  24. 24

    In PHPUnit, how can I mock a function that is not part of a class?

  25. 25

    How can I zero just the padding bytes of a class?

  26. 26

    How can I test a final class with private constructor?

  27. 27

    How can I get the application path in C?

  28. 28

    How can I use Laravel form model binding with bootstrap?

  29. 29

    How can I use a ternary operator for Graphql frontmatter?

热门标签

归档