Quick Links:

bal | bbl | bdl | bsl

Namespaces | Defines

Component bslmf_voidtype
[Package bslmf]

Provide a helper for implementing SFINAE-based metafunctions. More...

Namespaces

namespace  bslmf

Defines

#define BSLMF_VOIDTYPE(ARG)   typename BloombergLP::bslmf::VoidType_1< ARG >::type
#define BSLMF_VOIDTYPE2(...)   typename BloombergLP::bslmf::VoidType_2<__VA_ARGS__>::type
#define BSLMF_VOIDTYPES(...)   typename BloombergLP::bslmf::VoidType<__VA_ARGS__>::type

Detailed Description

Outline
Purpose:
Provide a helper for implementing SFINAE-based metafunctions.
Classes:
bsl::void_t alias template to help create SFINAE contexts in C++11
bslmf::VoidType class template to emulate bsl::void_t in C++03
Macros:
BSLMF_VOIDTYPE helper macro for SFINAE-based metafunctions
BSLMF_VOIDTYPE2 helper macro for SFINAE-based metafunctions
BSLMF_VOIDTYPES helper macro for SFINAE-based metafunctions
See also:
Component bslmf_resulttype
Description:
This component provides the alias template bsl::void_t, as specified by the C++14 standard, and a metafunction, bslmf::VoidType, to emulate the same functionality for older compilers that do not support alias templates, which are first specified by the C++11 standard. It further provides 3 macros, BSLMF_VOIDTYPE/2/S, that provide a consistent way to use the alias template where supported and the class template otherwise.
All forms of the metafunction, however it is written, produce the result type void. The usefulness of this do-nothing metafunction is that, when it is instantiated, all of its template arguments must be valid. By putting the template instantiation in a SFINAE context, any use of template parameters that name invalid dependent types are discarded by the compiler as non-viable. Thus, VoidType is most commonly used to build metafunctions that test for the existence of a specific nested data type (see Usage).
The bslmf::VoidType class template is intended to provide functionality identical to the C++14 metafunction std::void_t, but without using C++11 alias templates. A use, in C++14-compliant code, of:
  std::void_t<T1, T2, ...>
can be replaced, in BDE-compliant code using any version of standard C++, by:
  typename bslmf::VoidType<T1, T2, ...>::type
Macro Reference:
This section documents the preprocessor macros defined in this component.
Macros for type-dependant SFINAE checks in any C++ dialect:
The following macros are for use only in a type-dependent context. They all expand to a type expression that uses either bsl::void_t<ARGS> if alias templates are supported by the current compiler, and to typename BloombergLP::bslmf::VoidType<ARGS>type otherwise. The alias template form is preferred, as it consumes fewer resources while compiling code; the compiled code will be equivalent, whichever implementation is chosen. These macros support writing simple code that uses the preferred idiom supported by the current tool chain. Note that code targeting only C++11 or later can use bsl::void_t directly with no loss of efficiency or generality. These macros are needed only to support older C++03 tool chains.
The three macros support one, two, or many arguments, and are otherwise identical. The reason for three macros is that the overwhelmingly common use cases need only one, rarely two, type expressions, so for legacy compilers that do not support variadic templates, we can use a simpler class template that has fewer type parameters.
BSLMF_VOIDTYPE( TYPE_EXPRESSION ):
This macro will expand into a type expression that aliases void if the "TYPE EXPRESSION" is valid, and will fail to expand in a SFINAE friendly manner if the type expressions is not valid.
BSLMF_VOIDTYPE2( TYPE_EXPRESSION_1, TYPE_EXPRESSION_2 ):
This macro will expand into a type expression that aliases void if each "TYPE EXPRESSION" is valid, and will fail to expand in a SFINAE friendly manner if either of the type expressions is not valid.
BSLMF_VOIDTYPES( TYPE_EXPRESSIONS... ):
This macro will expand into a type expression that aliases void if each "TYPE EXPRESSION" is valid, and will fail to expand in a SFINAE friendly manner if any of the type expressions is not valid.
Additional concerns when using old compilers:
When compiling with a C++03 tool chain, the typename keyword in the macro expansion, used to extract the nested type from the class template, is not valid syntax unless the whole type expression is type (or value) dependent. Such use will produce an error, even if the supplied type expression is valid. E.g., BSLMF_VOIDTYPE(void) will always be a (non-SFINAE) error in C++03, but is perfectly valid in C++11. Be sure to test your code with a C++03 build when deploying these macros.
The templates and macros in this component aid in creating SFINAE conditions in a conforming C++03 manner. However, several of the compilers still in production have significant issues in their handling of SFINAE. For example, the Solaris CC compiler (prior to the 12.4 release) is very forgiving and will accept most invalid code without creating a SFINAE failure. Idiomatic use of this component for compile-time reflection to detect named members of a class is known to work. Other uses should be carefully tested before deployment.
Usage:
In this section we show intended use of this component.
Usage Example 1:
In this example, we demonstrate the use of VoidType to determine whether a given type T has a member type T::iterator. Our goal is to create a metafunction, HasIteratorType, such that HasIteratorType<T>k_VALUE is true if T::iterator is a valid type and false otherwise. This example is adapted from the paper proposing std::void_t for the C++ Standard, N3911.
First, we define the base-case metafunction that returns false:
  template <class TYPE, class = void>
  struct HasIteratorType {
      enum { k_VALUE = false };
  };
Then, we create a partial specialization that uses VoidType to probe for T::iterator:
  template <class TYPE>
  struct HasIteratorType<TYPE, BSLMF_VOIDTYPE(typename TYPE::iterator)> {
      enum { k_VALUE = true };
  };
Now, we define a class that has an iterator member and apply HasIteratorType to it:
  struct WithIterator {
      typedef short *iterator;
  };

  void usageExample1()
  {
      assert(true == HasIteratorType<WithIterator>::k_VALUE);
As WithIterator::iterator is a valid type, BSLMF_VOIDTYPE(typename TYPE::iterator) will be void and the second HasIteratorType template will be more specialized than the primary template, thus yielding a k_VALUE of true.
Finally, we try to instantiate HasIteratorType<int>. Any use of BSLMF_VOIDTYPE(TYPE::iterator) will result in a substitution failure. Fortunately, the Substitution Failure Is Not An Error (SFINAE) rule applies and the partial specialization is eliminated from consideration, resulting in the primary template being instantiated and yielding a k_VALUE of false:
      assert(false == HasIteratorType<int>::k_VALUE);
  }
Usage Example 2:
This example demonstrates the use of VoidType to probe for more than one type at once. As in the previous example, we are defining a metafunction. We'll define IsTraversable<T>k_VALUE to be true if T::iterator and T::value_type both exist. First, we define a primary template that always yields false:
  template <class TYPE, class = void>
  struct IsTraversable {
      enum { k_VALUE = false };
  };
Then, we create a partial specialization that uses BSLMF_VOIDTYPE2 with two parameters:
  template <class TYPE>
  struct IsTraversable<TYPE,
                       BSLMF_VOIDTYPE2(typename TYPE::iterator,
                                       typename TYPE::value_type)> {
      enum { k_VALUE = true };
  };
Now, we define a type that meets the requirements for being traversable:
  struct MyTraversable {
      typedef int  value_type;
      typedef int *iterator;
  };
Finally, the IsTraversable metafunction yields true for Traversable but not for either WithIterator, which lacks a value_type member, nor int, which lacks both iterator and value_type members:
  int usageExample2()
  {
      assert(true  == IsTraversable<MyTraversable>::k_VALUE);
      assert(false == IsTraversable<WithIterator>::k_VALUE);
      assert(false == IsTraversable<int>::k_VALUE);
  }

Define Documentation

#define BSLMF_VOIDTYPE (   ARG  )     typename BloombergLP::bslmf::VoidType_1< ARG >::type
#define BSLMF_VOIDTYPE2 (   ...  )     typename BloombergLP::bslmf::VoidType_2<__VA_ARGS__>::type
#define BSLMF_VOIDTYPES (   ...  )     typename BloombergLP::bslmf::VoidType<__VA_ARGS__>::type