mardi 24 février 2015

Where does nullptr_t reside?

A bit of prehistory.


I've been writing a game engine for quite some time. It's divided into several static libraries, like "utils", "rsbin" (resource system), "window", which are then linked into a single executable.


It is a crossplatform engine, being compiled for Windows and for Android. Under Windows, I compile it with MinGW. Under Android, with CCTools, which is an interface to native gcc.


One of the base classes is utils::RefObject, which represents a concept similar to Windows's IUnknown: it provides a reference counter to determine its lifetime and a method for querying a specific interface from base class pointer. There's also template< typename T > utils::Ref, designed specifically for this kind of objects. It holds an std::atomic< utils::RefObject* > and automatically updates its object's refcount upon construction, assignment and destruction, analogously to std::shared_ptr. It also allows to implicitly convert RefObjects of different types through their querying methods. Though, it's inefficient to query an object for its own type, so, utils::Ref overloads most of its operators, e. g. there's specific utils::Ref< T >::Ref( T* ptr ) constructor, which simply increments the passed object's refcount, and general utils::Ref< T >::Ref( RefObject* ptr ), which queries its argument for instance of T and throws an exception on failure (don't worry, though, there's of course a method for the soft cast).


But having just these two methods introduces a problem: you cannot explicitly initialize utils::Ref with a null pointer, as it is ambiguous; so there's also utils::Ref< T >::Ref( nullptr_t ) to provide a way to do it.


Now, we are getting to the problem at hand. In the header file, the prototype it spelled exactly as abouve, without any preceding std::. Note that I don't use using namespace either. For a long time, this worked.


Now, I'm working on a graphics system. It existed before, but it was rather rudimentary, so I didn't even noticed that <gl.h> actually defines only OpenGL 1.1, while for newer versions you should go through <glext.h>. Now, it became necessary to use the latter. But including it broke the old reference class.


Judging from error messages, MinGW now has problems with that nullptr_t in prototypes. I've done a quick search on the Web and found that often it's referred as std::nullptr_t. Though, not everywhere.


Quick sumup: I had nullptr_t without either std:: or using namespace compiling fine until I included <glext.h> before the header.


The site I've been using so far, http://ift.tt/1D7fClR, suggests that global ::nullptr_t is exactly how it should be. On the other hand, en.cppreference.com wiki tells that it's actually std::nullptr_t.


A quick test program, a helloworld with void foo( int ) and void foo( nullptr_t ), failed to compile and the reason now is explicit "error: 'nullptr_t' was not declared in this scope" with suggestion to use std::nullptr_t instead.


It won't be hard to add std:: where needed; but this case left me rather curious.


cplusplus.com was actually lying?


Then, if nullptr_t actually resides in namespace std, why did utils::Ref compile?


Why inclusion of <glext.h> breaks it?


Here are the sources, defining the class in question:



(the latter 2 are included and so may affect too)


Aucun commentaire:

Enregistrer un commentaire