BDE 4.14.0 Production release
|
Provide a facility for defining traits and detecting legacy traits.
This component defines a meta-function, bslmf::DetectNestedTrait
, that facilitates the creation of traits that can be associated with a type using the nested trait mechanism in bslmf_declarenestedtrait . Such traits are referred to as "nested traits" because their association with a type is embedded within the type's definition. bslmf::DetectNestedTrait
and can also be used to detect traits created using older legacy traits definitions mechanisms used at Bloomberg.
Please note:
bslmf::DetectNestedTrait
to detect traits is deprecated. Clients should detect traits using the C++11 idiom (see {Nested Trait Idiom vs. C++11 Trait Idiom} below)bslmf::DetectNestedTrait
(see {Writing a User-Defined Trait} below).BDE supports two idioms for defining traits and associating them with types. The older idiom uses bslmf::DetectNestedTrait
to define traits, and the BSLMF_NESTED_TRAIT_DECLARATION*
macros to associate traits with types. This idiom is called the "nested trait" idiom.
The newer idiom is familiar to users of C++11 traits, and is referred to here as the "C++11 trait" idiom. In the C++11 trait idiom, a trait is a template that derives its truth value from bsl::true_type
or bsl::false_type
, and is associated with a type by providing a specialization of the trait for the associated type.
For example, a minimal C++11 trait, abcd::C11Trait
, could be defined as:
abcd::C11Trait
would then be associated with a class, xyza::SomeClass
, by specializing the trait for that class:
Note that the specialization is defined in the same namespace as the original trait.
Both idioms detect the association of a trait with a class in the same way: by inspecting the trait's value
member.
The C++11 trait idiom is the standard idiom for new code in BDE.
On systems that do not require compatibility with the nested trait idiom, new traits should be written according to the C++11 trait idiom.
On systems that support the nested trait idiom, any new user-defined trait should derive its truth value from bslmf::DetectNestedTrait
following the Curiously Recurring Template Pattern. This will allow the trait to be detected by directly inspecting the trait's value
member, regardless of whether the trait is associated with a type through the nested trait idiom or through the C++11 trait idiom.
Therefore, the simplest maximally-compatible trait would look like this:
A trait having more complex default logic could derive from bsl::integral_constant
using the value
member of bslmf::DetectNestedTrait
, such as:
These are the only recommended uses of bslmf::DetectNestedTrait<t_TYPE, t_TRAIT>::type
and bslmf::DetectNestedTrait<t_TYPE, t_TRAIT>::value
.
If a trait, t_TRAIT
, has been associated with a type, t_TYPE
, using one of the BSLMF_NESTED_TRAIT_DECLARATION*
macros then bslmf::DetectNestedTrait<t_TYPE, t_TRAIT>
derives from bsl::true_type
. Otherwise, bslmf::DetectNestedTrait<t_TYPE, t_TRAIT>
derives from bsl::false_type
.
Therefore, if a trait abcd::BarTrait
has been associated with a class xyza::Foo
in the following way:
then bslmf::DetectNestedTrait<t_TYPE, t_TRAIT>::value
will evaluate to true
and bslmf::DetectNestedTrait<t_TYPE, t_TRAIT>::type
will be bsl::true_type
.
This section illustrates intended use of this component.
When writing generic infrastructure code, we often need to choose among multiple code paths based on the capabilities of the types on which we are operating. If those capabilities are reflected in a type's public interface, we may be able to use techniques such as SFINAE to choose the appropriate code path. However, SFINAE cannot detect all of a type's capabilities. In particular, SFINAE cannot detect constructors, memory allocation, thread-safety characteristics, and so on. Functions that depend on these capabilities must use another technique to determine the correct code path to use for a given type. We can solve this sort of problem by associating types with custom traits that indicate what capabilities are provided by a given type.
First, in package abcd
, define a trait, RequiresLockTrait
, that indicates that a type's methods must not be called unless a known lock it first acquired:
Notice that RequiresLockTrait
derives from bslmf::DetectNestedTrait<t_TYPE, RequiresLockTrait>::type
using the curiously recurring template pattern.
Then, in package xyza
, we declare a type, DoesNotRequireALockType
, that can be used without acquiring the lock:
Next, we declare a type, RequiresLockTypeA
, that does require the lock. We use the BSLMF_NESTED_TRAIT_DECLARATION
macro to associate the type with the abcd::RequiresLockTrait
trait:
Notice that the macro declaration is performed within the scope of the class declaration, and must be done with public scope.
Then, we declare a templatized container type, Container
, that is parameterized on some ELEMENT
type. If ELEMENT
requires a lock, then a Container
of ELEMENT
s will require a lock as well. This can be expressed using the BSLMF_NESTED_TRAIT_DECLARATION_IF
macro, by providing abcd::RequiresLockTrait<ELEMENT>::value
as the condition for associating the trait with Container
.
Next, we show that traits based on bslmf::DetectNestedTrait
can be associated with a type using "C++11-style" trait association. To do this, we declare a type, RequiresLockTypeB
, that also requires the lock, but does not used the BSLMF_NESTED_TRAIT_DECLARATION
macro:
Then, we associate RequiresLockTypeB
with abcd::RequiresLockTrait
by directly specializing abcd::RequiresLockTrait<xyza::RequiresLockTypeB>
. This is the standard way of associating a type with a trait since C++11:
Now, we can write a function that inspects abcd::RequiresLockTrait<t_TYPE>::value
to test whether or not various types are associated with abcd::RequiresLockTrait
:
Finally, we demonstrate that the trait can be tested at compilation time, by writing a function that tests the trait within the context of a compile-time BSLMF_ASSERT
: