Warning to those who got here via a search engine: Don’t use this version. Keep reading to the end of the series.
Suppose you want to be able to detect in C++ whether a type has been defined. For example, maybe you want to use a type if it exists. This can happen if, say, you are a library like React Native for Windows, and you need to be able to run with different versions of the Windows SDK. Or you’re writing a library where the client can customize the behavior by defining another class with a well-known name. Perhaps you’re trying to mimic C# partial classes.
My initial idea was to take advantage of unqualified name lookup by creating an alternate definition for the type that sits at a lower priority than the one we’re looking for.
// awesome.h
namespace awesome
{
// might or might not contain
struct special { ... };
}
// your code
namespace detect::impl
{
struct not_implemented {};
using special = not_implemented;
}
namespace awesome::detect
{
using namespace ::detect::impl;
constexpr bool is_special_defined =
!std::is_same_v<special, ::detect::impl::not_implemented>;
}
The idea here is that I declare an alternate version of the special structure in the detect::impl namespace, and place it in the search order at a location that comes after searching in the awesome namespace.
The using namespace ::detect::impl; directive makes the names from the detect::impl visible as if they had been declared in the global namespace. Why the global namespace? Because the rule for using namespace is that the names from the imported-from namespace are treated as if they had been declared in the namespace which is the nearest common ancestor of the importing namespace and the imported-from namespace. In our case, the imported-from namespace is ::detect::impl and the importing namespace is ::awesome::detect. Since they don’t even share a common top-level namespace, the nearest common ancestor is the global namespace.
Next, I check the name special. The unqualified name lookup searches in the following order:
::awesome::detect::special::awesome::special::special(which, thanks to ourusing namespace ::detect::impl;directive also searches in::detect::impl.)
There is definitely no special declared in the ::awesome::detect namespace, so it comes down to the other two. If it exists in the ::awesome namespace, then the unqualified lookup will find that type; otherwise, it will find the one in the ::detect::impl namespace.
We then use std::is_same_v to see whether the type we found is our fake one.
This works, but it’s awkward because you have to do the detection from inside the ::awesome::detect namespace, since that’s where we set up the search order. For every type you want to detect, you need to create an alias in the ::detect::impl namespace and a custom is_whatever_defined constant.
Next time, we’ll look at my second attempt.
Too bad the blog ratings are gone since this is going to be one of those 5 star series.
With a some changes I got this working on VS2005, anyone dare to go older?
In the next-to-last paragraph, I think you meant “from inside the ::awesome::detect namespace”.
Small nit-pick: this could fail if someone declared a special in the global namespace. Depending how unique the “special “identifier is, this could be a problem.
Yup, thanks, and the second half of that paragraph was also wrong. In the case where somebody declared a
specialin the global namespace, you will at least get a compiler error telling about the problem.