Quick Links: |
Provide clean compile-time dispatch based on multiple traits. More...
ScalarPrimitives::copyConstruct
, that takes an original object and an allocator constructs a copy of original
using the most efficient valid mechanism. The function should take into account that the original type might be bitwise copyable, or have an allocator that can be different in the copy than in the original object, or that the original might be a pair type, where the correct method of copying first
and second
is (recursively) governed by the same concerns. bsls::HasTrait
mechanism has a clumsy mechanism for dispatching on multiple traits at once. For example, the bslalg::scalarprimitives::copyConstruct
, function uses four different implementations, depending on the traits of the object being copied. The existing code looks like this: template <class TARGET_TYPE> inline void ScalarPrimitives::copyConstruct(TARGET_TYPE *address, const TARGET_TYPE& original, bslma::Allocator *allocator) { BSLS_ASSERT_SAFE(address); enum { VALUE = HasTrait<TARGET_TYPE, TypeTraitUsesBslmaAllocator >::VALUE ? Imp::USES_BSLMA_ALLOCATOR_TRAITS : HasTrait<TARGET_TYPE, TypeTraitBitwiseCopyable >::VALUE ? Imp::BITWISE_COPYABLE_TRAITS : HasTrait<TARGET_TYPE, TypeTraitPair >::VALUE ? Imp::PAIR_TRAITS : Imp::NIL_TRAITS }; Imp::copyConstruct(address, original, allocator, (bsl::integral_constant<int, VALUE>*)0); }
?:
operations with a clean mechanism for producing one of four different types based on the first matching trait. template <class t_TYPE> struct UsesBslmaAllocator : bsl::false_type { }; template <class t_TYPE> struct IsBitwiseCopyable : bsl::false_type { }; template <class t_TYPE> struct IsPair : bsl::false_type { };
bslmf::DetectNestedTrait
instead of from bsl::false_type
. bslma::Allocator
and bslalg::scalarprimitives::copyConstruct
: namespace bslma { class Allocator; } namespace bslalg { struct ScalarPrimitives { template <class TARGET_TYPE> static void copyConstruct(TARGET_TYPE *address, const TARGET_TYPE& original, bslma::Allocator *allocator); };
Imp::copyConstruct
, each taking a different trait specialization. A fourth overload takes false_type
instead of a trait specialization, for those types that don't match any traits. For testing purposes, in addition to copying the data member, each overload also increments a separate counter. These implementations are slightly simplified for readability: struct Imp { // Counters for counting overload calls static int s_noTraitsCounter; static int s_usesBslmaAllocatorCounter; static int s_isPairCounter; static int s_isBitwiseCopyableCounter; static void clearCounters() { s_noTraitsCounter = 0; s_usesBslmaAllocatorCounter = 0; s_isPairCounter = 0; s_isBitwiseCopyableCounter = 0; } template <class TARGET_TYPE> static void copyConstruct(TARGET_TYPE *address, const TARGET_TYPE& original, bslma::Allocator *allocator, bslmf::SelectTraitCase<UsesBslmaAllocator>) { new (address) TARGET_TYPE(original, allocator); ++s_usesBslmaAllocatorCounter; } template <class TARGET_TYPE> static void copyConstruct(TARGET_TYPE *address, const TARGET_TYPE& original, bslma::Allocator *allocator, bslmf::SelectTraitCase<IsPair>) { ScalarPrimitives::copyConstruct(&address->first, original.first, allocator); ScalarPrimitives::copyConstruct(&address->second, original.second, allocator); ++s_isPairCounter; } template <class TARGET_TYPE> static void copyConstruct(TARGET_TYPE *address, const TARGET_TYPE& original, bslma::Allocator *, bslmf::SelectTraitCase<IsBitwiseCopyable>) { memcpy(address, &original, sizeof(original)); ++s_isBitwiseCopyableCounter; } template <class TARGET_TYPE> static void copyConstruct(TARGET_TYPE *address, const TARGET_TYPE& original, bslma::Allocator *, bslmf::SelectTraitCase<>) { new (address) TARGET_TYPE(original); ++s_noTraitsCounter; } }; int Imp::s_noTraitsCounter = 0; int Imp::s_usesBslmaAllocatorCounter = 0; int Imp::s_isPairCounter = 0; int Imp::s_isBitwiseCopyableCounter = 0;
ScalarPrimitives::copyConstruct
: template <class TARGET_TYPE> inline void ScalarPrimitives::copyConstruct(TARGET_TYPE *address, const TARGET_TYPE& original, bslma::Allocator *allocator) {
bslmf::SelectTrait
to declare Selection
as a specialization of the first match of the specified traits: typedef typename bslmf::SelectTrait<TARGET_TYPE, UsesBslmaAllocator, IsBitwiseCopyable, IsPair>::Type Selection;
Selection
to choose (at compile time), one of the Imp::copyConstruct
overloads defined above: Imp::copyConstruct(address, original, allocator, Selection()); } // end copyConstruct() } // Close namespace bslalg
UsesBslmaAllocator
trait: class TypeWithAllocator { int d_value; bslma::Allocator *d_alloc; public: TypeWithAllocator(int v = 0, bslma::Allocator *a = 0) // IMPLICIT : d_value(v), d_alloc(a) { } TypeWithAllocator(const TypeWithAllocator& other, bslma::Allocator *a = 0) : d_value(other.d_value), d_alloc(a) { } int value() const { return d_value; } bslma::Allocator *allocator() const { return d_alloc; } }; template <> struct UsesBslmaAllocator<TypeWithAllocator> : bsl::true_type { };
IsBitwiseCopyable
trait: class BitwiseCopyableType { int d_value; public: BitwiseCopyableType(int v = 0) : d_value(v) { } // IMPLICIT int value() const { return d_value; } }; template <> struct IsBitwiseCopyable<BitwiseCopyableType> : bsl::true_type { };
IsPair
trait: struct PairType { TypeWithAllocator first; BitwiseCopyableType second; PairType(int a, int b) : first(a), second(b) { } }; template <> struct IsPair<PairType> : bsl::true_type { };
IsPair
and IsBitwiseCopyable
traits: struct BitwiseCopyablePairType { BitwiseCopyableType first; BitwiseCopyableType second; BitwiseCopyablePairType(int a, int b) : first(a), second(b) { } }; template <> struct IsPair<BitwiseCopyablePairType> : bsl::true_type { }; template <> struct IsBitwiseCopyable<BitwiseCopyablePairType> : bsl::true_type { };
class TypeWithNoTraits { int d_value; public: TypeWithNoTraits(int v = 0) : d_value(v) { } // IMPLICIT int value() const { return d_value; } };
ScalarPrimitives::copyConstruct
and verify that the most efficient copy operation that is valid for each type is applied: int usageExample1() { using bslalg::Imp; // This buffer is properly aligned and big enough to hold any of the // test types. void *buffer[4]; char dummy[2]; // Dummy addresses bslma::Allocator *a1 = (bslma::Allocator*) &dummy[0]; bslma::Allocator *a2 = (bslma::Allocator*) &dummy[1];
ScalarPrimitives::copyConstruct
for an object of TypeWithAllocator
, we expect that the copy will have the same value but a different allocator than the original and that the UsesBslmaAllocator
copy implementation will be called once: Imp::clearCounters(); TypeWithAllocator twa(1, a1); TypeWithAllocator *twaptr = (TypeWithAllocator*) buffer; bslalg::ScalarPrimitives::copyConstruct(twaptr, twa, a2); assert(1 == Imp::s_usesBslmaAllocatorCounter); assert(1 == twaptr->value()); assert(a2 == twaptr->allocator()); twaptr->~TypeWithAllocator();
ScalarPrimitives::copyConstruct
for an object of BitwiseCopyableType
, we expect that the IsBitwiseCopyable
copy implementation will be called once: Imp::clearCounters(); BitwiseCopyableType bct(2); BitwiseCopyableType *bctptr = (BitwiseCopyableType*) buffer; bslalg::ScalarPrimitives::copyConstruct(bctptr, bct, a2); assert(1 == Imp::s_isBitwiseCopyableCounter); assert(2 == bctptr->value()); bctptr->~BitwiseCopyableType();
ScalarPrimitives::copyConstruct
for an object of PairType
, we expect that the IsPair
copy implementation will be called once for the pair as whole and that the UsesBslmaAllocator
and IsBitwiseCopyable
implementations will be called for the first
and second
members, respectively: Imp::clearCounters(); PairType pt(3, 4); PairType *ptptr = (PairType*) buffer; bslalg::ScalarPrimitives::copyConstruct(ptptr, pt, a2); assert(1 == Imp::s_isPairCounter); assert(1 == Imp::s_usesBslmaAllocatorCounter); assert(1 == Imp::s_usesBslmaAllocatorCounter); assert(3 == ptptr->first.value()); assert(a2 == ptptr->first.allocator()); assert(4 == ptptr->second.value()); ptptr->~PairType();
ScalarPrimitives::copyConstruct
for an object of BitwiseCopyablePairType
, the IsBitwiseCopyable
trait takes precedence over the IsPair
trait (because it appears first in the list of traits used to instantiate SelectTrait
). Therefore, we expect to see the IsBitwiseCopyable
copy implementation called once for the whole pair and the IsPair
copy implementation not called at all: Imp::clearCounters(); BitwiseCopyablePairType bcpt(5, 6); BitwiseCopyablePairType *bcptbcptr = (BitwiseCopyablePairType*) buffer; bslalg::ScalarPrimitives::copyConstruct(bcptbcptr, bcpt, a2); // Prefer IsBitwiseCopyable over IsPair trait assert(1 == Imp::s_isBitwiseCopyableCounter); assert(0 == Imp::s_isPairCounter); assert(5 == bcptbcptr->first.value()); assert(6 == bcptbcptr->second.value()); bcptbcptr->~BitwiseCopyablePairType();
ScalarPrimitives::copyConstruct
for an object of TypeWithNoTraits
, we expect none of the specialized copy implementations to be called, thus defaulting to the false_type
copy implementation: Imp::clearCounters(); TypeWithNoTraits twnt(7); TypeWithNoTraits *twntptr = (TypeWithNoTraits*) buffer; bslalg::ScalarPrimitives::copyConstruct(twntptr, twnt, a2); assert(1 == Imp::s_noTraitsCounter); assert(7 == twntptr->value()); twntptr->~TypeWithNoTraits(); return 0; }
SelectTraits
for dispatching using overloading imposes little or no overhead, since the compiler typically generates no code for the constructor or copy constructor of the trait argument to the overloaded functions. When inlining is in effect, the result is very efficient.