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:
__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:
Thus a which-compiler-is-it sniffing macro
CPPX_UUID_FOR
can be defined as …and be invoked after the first declaration of the class:
The macro implementation for Visual C++ is trivial:
The macro implementation for g++ is a bit more involved:
… 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 namespacecppx
:where
cppx::uuid::from
is defined earlier in namespacecppx::uuid
:where
ce
is just a constructor tag, of enumeration typeConst_expr
, that selects theconstexpr
constructor of theuuid::Initializable
class: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 suchassert
ions for theconstexpr
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
andbyte_from_hex
are pretty trivial, but thefail
function is a bit subtle. In spite of appearances it’s not aconstexpr
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:and then
fail
can be coded up simply as e.g.I found it non-trivial (and possibly impossible) to express
fail
as aconstexpr
function when it hasstd::string
argument, and as an ordinary function calls of it suppressed theconstexpr
property. The non-returning variant works OK with g++. However, I’m not sure what the standard has to say about this.