BDE 4.14.0 Production release
Loading...
Searching...
No Matches
bslmf_detectnestedtrait.h
Go to the documentation of this file.
1/// @file bslmf_detectnestedtrait.h
2///
3/// The content of this file has been pre-processed for Doxygen.
4///
5
6
7// bslmf_detectnestedtrait.h -*-C++-*-
8#ifndef INCLUDED_BSLMF_DETECTNESTEDTRAIT
9#define INCLUDED_BSLMF_DETECTNESTEDTRAIT
10
11#include <bsls_ident.h>
12BSLS_IDENT("$Id: $")
13
14/// @defgroup bslmf_detectnestedtrait bslmf_detectnestedtrait
15/// @brief Provide a facility for defining traits and detecting legacy traits.
16/// @addtogroup bsl
17/// @{
18/// @addtogroup bslmf
19/// @{
20/// @addtogroup bslmf_detectnestedtrait
21/// @{
22///
23/// <h1> Outline </h1>
24/// * <a href="#bslmf_detectnestedtrait-purpose"> Purpose</a>
25/// * <a href="#bslmf_detectnestedtrait-classes"> Classes </a>
26/// * <a href="#bslmf_detectnestedtrait-description"> Description </a>
27/// * <a href="#bslmf_detectnestedtrait-nested-trait-idiom-vs-c-11-trait-idiom"> Nested Trait Idiom vs. C++11 Trait Idiom </a>
28/// * <a href="#bslmf_detectnestedtrait-writing-a-user-defined-trait"> Writing a User-Defined Trait </a>
29/// * <a href="#bslmf_detectnestedtrait-detecting-legacy-traits"> Detecting Legacy Traits </a>
30/// * <a href="#bslmf_detectnestedtrait-usage"> Usage </a>
31/// * <a href="#bslmf_detectnestedtrait-example-1-defining-a-custom-trait"> Example 1: Defining a Custom Trait </a>
32///
33/// # Purpose {#bslmf_detectnestedtrait-purpose}
34/// Provide a facility for defining traits and detecting legacy traits.
35///
36/// # Classes {#bslmf_detectnestedtrait-classes}
37///
38/// - bslmf::DetectNestedTrait: meta-function to test a nested trait
39///
40/// @see bslmf_nestedtraitdeclaration
41///
42/// # Description {#bslmf_detectnestedtrait-description}
43/// This component defines a meta-function,
44/// `bslmf::DetectNestedTrait`, that facilitates the creation of traits that can
45/// be associated with a type using the nested trait mechanism in
46/// @ref bslmf_declarenestedtrait . Such traits are referred to as "nested traits"
47/// because their association with a type is embedded within the type's
48/// definition. `bslmf::DetectNestedTrait` and can also be used to detect
49/// traits created using older legacy traits definitions mechanisms used at
50/// Bloomberg.
51///
52/// Please note:
53/// 1. The use of `bslmf::DetectNestedTrait` to detect traits is *deprecated*.
54/// Clients should detect traits using the C++11 idiom (see
55/// {Nested Trait Idiom vs. C++11 Trait Idiom} below)
56/// 2. Clients are encouraged to use a C++11 idiom for defining traits.
57/// However, authors of traits who want trait users to be able to take
58/// advantage of @ref bslmf_nestedtraitdeclaration must define traits that
59/// inherit from `bslmf::DetectNestedTrait` (see
60/// {Writing a User-Defined Trait} below).
61///
62/// ## Nested Trait Idiom vs. C++11 Trait Idiom {#bslmf_detectnestedtrait-nested-trait-idiom-vs-c-11-trait-idiom}
63///
64///
65/// BDE supports two idioms for defining traits and associating them with types.
66/// The older idiom uses `bslmf::DetectNestedTrait` to define traits, and the
67/// `BSLMF_NESTED_TRAIT_DECLARATION*` macros to associate traits with types.
68/// This idiom is called the "nested trait" idiom.
69///
70/// The newer idiom is familiar to users of C++11 traits, and is referred to
71/// here as the "C++11 trait" idiom. In the C++11 trait idiom, a trait is a
72/// template that derives its truth value from `bsl::true_type` or
73/// `bsl::false_type`, and is associated with a type by providing a
74/// specialization of the trait for the associated type.
75///
76/// For example, a minimal C++11 trait, `abcd::C11Trait`, could be defined as:
77/// @code
78/// namespace abcd {
79///
80/// template <class t_TYPE>
81/// struct C11Trait : bsl::false_type {
82/// };
83///
84/// } // close namespace abcd
85/// @endcode
86/// `abcd::C11Trait` would then be associated with a class, `xyza::SomeClass`,
87/// by specializing the trait for that class:
88/// @code
89/// namespace xyza {
90///
91/// class SomeClass {
92/// // The definition of 'SomeClass' does not affect the trait mechanism.
93///
94/// // ...
95/// };
96///
97/// } // close namespace xyza
98///
99/// namespace abcd {
100///
101/// template <>
102/// struct C11Trait<xyza::SomeClass> : bsl::true_type {
103/// };
104///
105/// } // close namespace abcd
106/// @endcode
107/// Note that the specialization is defined in the same namespace as the
108/// original trait.
109///
110/// Both idioms detect the association of a trait with a class in the same way:
111/// by inspecting the trait's `value` member.
112/// @code
113/// assert(true == abcd::C11Trait<xyza::SomeClass>::value);
114/// assert(false == abcd::C11Trait<xyza::Foo>::value);
115/// assert(true == abcd::BarTrait<xyza::Foo>::value);
116/// assert(false == abcd::BarTrait<xyza::SomeClass>::value);
117/// @endcode
118/// The C++11 trait idiom is the standard idiom for new code in BDE.
119///
120/// ## Writing a User-Defined Trait {#bslmf_detectnestedtrait-writing-a-user-defined-trait}
121///
122///
123/// On systems that do not require compatibility with the nested trait idiom,
124/// new traits should be written according to the C++11 trait idiom.
125///
126/// On systems that support the nested trait idiom, any new user-defined trait
127/// should derive its truth value from `bslmf::DetectNestedTrait` following the
128/// Curiously Recurring Template Pattern. This will allow the trait to be
129/// detected by directly inspecting the trait's `value` member, regardless of
130/// whether the trait is associated with a type through the nested trait idiom
131/// or through the C++11 trait idiom.
132///
133/// Therefore, the simplest maximally-compatible trait would look like this:
134/// @code
135/// template <class t_TYPE>
136/// struct MyTrait : bslmf::DetectNestedTrait<t_TYPE, MyTrait>::type {};
137/// @endcode
138/// A trait having more complex default logic could derive from
139/// `bsl::integral_constant` using the `value` member of
140/// `bslmf::DetectNestedTrait`, such as:
141/// @code
142/// template <class t_TYPE>
143/// struct ComplexTrait : bsl::integral_constant<bool,
144/// bslmf::DetectNestedTrait<t_TYPE, ComplexTrait>::value
145/// || SomeOtherTrait<t_TYPE>::value> {
146/// };
147/// @endcode
148/// These are the only recommended uses of
149/// `bslmf::DetectNestedTrait<t_TYPE, t_TRAIT>::type` and
150/// `bslmf::DetectNestedTrait<t_TYPE, t_TRAIT>::value`.
151///
152/// ## Detecting Legacy Traits {#bslmf_detectnestedtrait-detecting-legacy-traits}
153///
154///
155/// If a trait, `t_TRAIT`, has been associated with a type, `t_TYPE`, using one
156/// of the `BSLMF_NESTED_TRAIT_DECLARATION*` macros then
157/// `bslmf::DetectNestedTrait<t_TYPE, t_TRAIT>` derives from `bsl::true_type`.
158/// Otherwise, `bslmf::DetectNestedTrait<t_TYPE, t_TRAIT>` derives from
159/// `bsl::false_type`.
160///
161/// Therefore, if a trait `abcd::BarTrait` has been associated with a class
162/// `xyza::Foo` in the following way:
163/// @code
164/// namespace xyza {
165///
166/// class Foo {
167/// // ... various implementation details ...
168///
169/// public:
170/// // TRAITS
171/// BSLMF_NESTED_TRAIT_DECLARATION(Foo, abcd::BarTrait);
172///
173/// // ... the rest of the public interface ...
174/// };
175///
176/// } // close namespace xyza
177/// @endcode
178/// then `bslmf::DetectNestedTrait<t_TYPE, t_TRAIT>::value` will evaluate to
179/// `true` and `bslmf::DetectNestedTrait<t_TYPE, t_TRAIT>::type` will be
180/// `bsl::true_type`.
181///
182/// ## Usage {#bslmf_detectnestedtrait-usage}
183///
184///
185/// This section illustrates intended use of this component.
186///
187/// ### Example 1: Defining a Custom Trait {#bslmf_detectnestedtrait-example-1-defining-a-custom-trait}
188///
189///
190/// When writing generic infrastructure code, we often need to choose among
191/// multiple code paths based on the capabilities of the types on which we are
192/// operating. If those capabilities are reflected in a type's public
193/// interface, we may be able to use techniques such as SFINAE to choose the
194/// appropriate code path. However, SFINAE cannot detect all of a type's
195/// capabilities. In particular, SFINAE cannot detect constructors, memory
196/// allocation, thread-safety characteristics, and so on. Functions that depend
197/// on these capabilities must use another technique to determine the correct
198/// code path to use for a given type. We can solve this sort of problem by
199/// associating types with custom traits that indicate what capabilities are
200/// provided by a given type.
201///
202/// First, in package `abcd`, define a trait, `RequiresLockTrait`, that
203/// indicates that a type's methods must not be called unless a known lock it
204/// first acquired:
205/// @code
206/// namespace abcd {
207///
208/// template <class t_TYPE>
209/// struct RequiresLockTrait :
210/// bslmf::DetectNestedTrait<t_TYPE, RequiresLockTrait>::type {
211/// };
212///
213/// } // close package namespace
214/// @endcode
215/// Notice that `RequiresLockTrait` derives from
216/// `bslmf::DetectNestedTrait<t_TYPE, RequiresLockTrait>::type` using the
217/// curiously recurring template pattern.
218///
219/// Then, in package `xyza`, we declare a type, `DoesNotRequireALockType`, that
220/// can be used without acquiring the lock:
221/// @code
222/// namespace xyza {
223///
224/// class DoesNotRequireLockType {
225/// // ...
226///
227/// public:
228/// // CREATORS
229/// DoesNotRequireLockType();
230/// // ...
231/// };
232/// @endcode
233/// Next, we declare a type, `RequiresLockTypeA`, that does require the lock.
234/// We use the `BSLMF_NESTED_TRAIT_DECLARATION` macro to associate the type with
235/// the `abcd::RequiresLockTrait` trait:
236/// @code
237/// class RequiresLockTypeA {
238/// // ...
239///
240/// public:
241/// // TRAITS
242/// BSLMF_NESTED_TRAIT_DECLARATION(RequiresLockTypeA,
243/// abcd::RequiresLockTrait);
244///
245/// // CREATORS
246/// RequiresLockTypeA();
247/// // ...
248///
249/// };
250/// @endcode
251/// Notice that the macro declaration is performed within the scope of the class
252/// declaration, and must be done with public scope.
253///
254/// Then, we declare a templatized container type, `Container`, that is
255/// parameterized on some `ELEMENT` type. If `ELEMENT` requires a lock, then a
256/// `Container` of `ELEMENT`s will require a lock as well. This can be
257/// expressed using the `BSLMF_NESTED_TRAIT_DECLARATION_IF` macro, by providing
258/// `abcd::RequiresLockTrait<ELEMENT>::value` as the condition for associating
259/// the trait with `Container`.
260/// @code
261/// template <class ELEMENT>
262/// struct Container {
263/// // ...
264///
265/// public:
266/// // TRAITS
267/// BSLMF_NESTED_TRAIT_DECLARATION_IF(Container, abcd::RequiresLockTrait,
268/// abcd::RequiresLockTrait<ELEMENT>::value);
269///
270/// // ...
271/// };
272/// @endcode
273/// Next, we show that traits based on `bslmf::DetectNestedTrait` can be
274/// associated with a type using "C++11-style" trait association. To do this,
275/// we declare a type, `RequiresLockTypeB`, that also requires the lock, but
276/// does not used the `BSLMF_NESTED_TRAIT_DECLARATION` macro:
277/// @code
278/// class RequiresLockTypeB {
279/// // ...
280///
281/// public:
282/// // CREATORS
283/// RequiresLockTypeB();
284/// // ...
285///
286/// };
287///
288/// } // close package namespace
289/// @endcode
290/// Then, we associate `RequiresLockTypeB` with `abcd::RequiresLockTrait` by
291/// directly specializing `abcd::RequiresLockTrait<xyza::RequiresLockTypeB>`.
292/// This is the standard way of associating a type with a trait since C++11:
293/// @code
294/// namespace abcd {
295///
296/// template <>
297/// struct RequiresLockTrait<xyza::RequiresLockTypeB> : bsl::true_type {
298/// };
299///
300/// } // close namespace abcd
301/// @endcode
302/// Now, we can write a function that inspects
303/// `abcd::RequiresLockTrait<t_TYPE>::value` to test whether or not various
304/// types are associated with `abcd::RequiresLockTrait`:
305/// @code
306/// void example1()
307/// {
308/// assert(false ==
309/// abcd::RequiresLockTrait<xyza::DoesNotRequireLockType>::value);
310///
311/// assert(true ==
312/// abcd::RequiresLockTrait<xyza::RequiresLockTypeA>::value);
313///
314/// assert(true ==
315/// abcd::RequiresLockTrait<xyza::RequiresLockTypeB>::value);
316///
317/// assert(false ==
318/// abcd::RequiresLockTrait<
319/// xyza::Container<xyza::DoesNotRequireLockType> >::value);
320///
321/// assert(true ==
322/// abcd::RequiresLockTrait<
323/// xyza::Container<xyza::RequiresLockTypeA> >::value);
324///
325/// assert(true ==
326/// abcd::RequiresLockTrait<
327/// xyza::Container<xyza::RequiresLockTypeB> >::value);
328///
329/// // ...
330/// }
331/// @endcode
332/// Finally, we demonstrate that the trait can be tested at compilation time, by
333/// writing a function that tests the trait within the context of a compile-time
334/// `BSLMF_ASSERT`:
335/// @code
336/// void example2()
337/// {
338/// BSLMF_ASSERT(false ==
339/// abcd::RequiresLockTrait<xyza::DoesNotRequireLockType>::value);
340///
341/// BSLMF_ASSERT(true ==
342/// abcd::RequiresLockTrait<xyza::RequiresLockTypeA>::value);
343///
344/// BSLMF_ASSERT(true ==
345/// abcd::RequiresLockTrait<xyza::RequiresLockTypeB>::value);
346///
347/// BSLMF_ASSERT(false ==
348/// abcd::RequiresLockTrait<
349/// xyza::Container<xyza::DoesNotRequireLockType> >::value);
350///
351/// BSLMF_ASSERT(true ==
352/// abcd::RequiresLockTrait<
353/// xyza::Container<xyza::RequiresLockTypeA> >::value);
354///
355/// BSLMF_ASSERT(true ==
356/// abcd::RequiresLockTrait<
357/// xyza::Container<xyza::RequiresLockTypeB> >::value);
358///
359/// }
360/// @endcode
361/// @}
362/** @} */
363/** @} */
364
365/** @addtogroup bsl
366 * @{
367 */
368/** @addtogroup bslmf
369 * @{
370 */
371/** @addtogroup bslmf_detectnestedtrait
372 * @{
373 */
374
375#include <bslscm_version.h>
376
379#include <bslmf_matchanytype.h>
381#include <bslmf_voidtype.h>
382
383
384namespace bslmf {
385
386 // ===========================
387 // class DetectNestedTrait_Imp
388 // ===========================
389
390/// Metafunction to detect whether the specified `t_TRAIT` parameter is
391/// associated with the specified `t_TYPE` parameter using the nested type
392/// trait mechanism. The nested `type` is an alias for `bsl::false_type`
393/// unless `t_TYPE` is a class type that the user has associated with the
394/// specified trait using the `BSLMF_NESTED_TRAIT_DECLARATION` macro. Class
395/// types detect such association in the specialization below.
396template <class t_TYPE, template <class> class t_TRAIT, class = void>
398
399 // PUBLIC TYPES
401};
402
403/// This template specialization detects whether the specified `t_TRAIT`
404/// parameter is associated with the specified `t_TYPE` parameter, which is
405/// constrained to be a class type, using the nested type trait mechanism.
406/// The nested `type` is an alias for `bsl::true_type` if and only if
407/// `t_TYPE` is associated with `t_TRAIT` using the
408/// `BSLMF_NESTED_TRAIT_DECLARATION` macro, and for `bsl::false_type`
409/// otherwise. Note that nested trait associations are not inherited by
410/// derived classes, which must be associated in their own right.
411template <class t_TYPE, template <class> class t_TRAIT>
412struct DetectNestedTrait_Imp<t_TYPE, t_TRAIT, BSLMF_VOIDTYPE(int t_TYPE::*)> {
413
414 private:
415 // PRIVATE CLASS METHODS
416
417 /// Declared but not defined. This overload is selected if called with
418 /// a type convertible to `NestedTraitDeclaration<t_TYPE, t_TRAIT>`
419 static char check(NestedTraitDeclaration<t_TYPE, t_TRAIT>, int);
420
421 /// Declared but not defined. This overload is selected if called with
422 /// a type not convertible to `NestedTraitDeclaration<t_TYPE, t_TRAIT>`
423 static int check(MatchAnyType, ...);
424
425 private:
426 // PRIVATE TYPES
427 enum { k_ENSURE_TYPE_IS_COMPLETE = sizeof(t_TYPE) };
428
429 enum {
430 k_CONVERTIBLE_TO_NESTED_TRAIT = sizeof(check(TypeRep<t_TYPE>::rep(),
431 0)) == sizeof(char),
432 k_CONVERTIBLE_TO_ANY_TYPE = IsConvertibleToAny<t_TYPE>::value
433 };
434
435 enum {
436 k_VALUE = k_CONVERTIBLE_TO_NESTED_TRAIT && !k_CONVERTIBLE_TO_ANY_TYPE
437 // Non-zero if 't_TRAIT' is associated with 't_TYPE' using the nested
438 // type trait mechanism; otherwise zero.
439 };
440
441 public:
442 // PUBLIC TYPES
443
444 /// Type representing the result of this metafunction. An alias for
445 /// `bsl::true_type` if `t_TRAIT` is associated with `t_TYPE` using the
446 /// nested type trait mechanism; otherwise an alias for
447 /// `bsl::false_type`.
449};
450
451 // =======================
452 // class DetectNestedTrait
453 // =======================
454
455/// This `struct` template metafunction detects whether the specified
456/// `t_TRAIT` parameter is associated with the specified `t_TYPE` parameter
457/// using the nested type trait mechanism. This trait derives from
458/// `bsl::true_type` if and only if `t_TYPE` is a class type that associated
459/// with the specified trait using the `BSLMF_NESTED_TRAIT_DECLARATION`
460/// macro, and from `bsl::false_type` otherwise. Users should not
461/// specialize this trait directly for their types, but should always use
462/// the macro to make a nested trait association.
463template <class t_TYPE, template <class> class t_TRAIT>
464struct DetectNestedTrait : DetectNestedTrait_Imp<t_TYPE, t_TRAIT>::type {
465};
466
467template <class t_TYPE, template <class> class t_TRAIT>
468struct DetectNestedTrait<const t_TYPE, t_TRAIT>
469: DetectNestedTrait_Imp<t_TYPE, t_TRAIT>::type {
470};
471template <class t_TYPE, template <class> class t_TRAIT>
472struct DetectNestedTrait<volatile t_TYPE, t_TRAIT>
473: DetectNestedTrait_Imp<t_TYPE, t_TRAIT>::type {
474};
475template <class t_TYPE, template <class> class t_TRAIT>
476struct DetectNestedTrait<const volatile t_TYPE, t_TRAIT>
477: DetectNestedTrait_Imp<t_TYPE, t_TRAIT>::type {
478};
479 // Partial specializations to ensure that cv-qualified types have the same
480 // association with a nested trait as their unqualified counterpart. Note
481 // that we delegate directly to the '_Imp' class template, rather than back
482 // to the primary template, to avoid an infinite recursion bug on platforms
483 // that do not correctly implement "abominable" function types with
484 // cv-qualifiers.
485
486} // close package namespace
487
488
489#endif
490
491// ----------------------------------------------------------------------------
492// Copyright 2019 Bloomberg Finance L.P.
493//
494// Licensed under the Apache License, Version 2.0 (the "License");
495// you may not use this file except in compliance with the License.
496// You may obtain a copy of the License at
497//
498// http://www.apache.org/licenses/LICENSE-2.0
499//
500// Unless required by applicable law or agreed to in writing, software
501// distributed under the License is distributed on an "AS IS" BASIS,
502// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
503// See the License for the specific language governing permissions and
504// limitations under the License.
505// ----------------------------- END-OF-FILE ----------------------------------
506
507/** @} */
508/** @} */
509/** @} */
Definition bslmf_nestedtraitdeclaration.h:214
#define BSLMF_VOIDTYPE(ARG)
Definition bslmf_voidtype.h:335
#define BSLS_IDENT(str)
Definition bsls_ident.h:195
Definition bdlbb_blob.h:576
Definition bslmf_integralconstant.h:244
bsl::integral_constant< bool, k_VALUE > type
Definition bslmf_detectnestedtrait.h:448
Definition bslmf_detectnestedtrait.h:397
bsl::false_type type
Definition bslmf_detectnestedtrait.h:400
Definition bslmf_detectnestedtrait.h:464
Definition bslmf_isconvertibletoany.h:121
Any type can be converted into this type.
Definition bslmf_matchanytype.h:150
Generate a reference to t_TYPE for use in meta-functions.
Definition bslmf_matchanytype.h:189