Quick Links: |
Provide a type trait for bitwise equality. More...
Namespaces | |
namespace | bslmf |
bslmf::IsBitwiseEqualityComparable | trait metafunction |
bslmf::IsBitwiseEqualityComparable
, which allows generic code to determine whether objects of the specified t_TYPE
can be compared using memcmp
. Such types are said to be bitwise EqualityComparable. Observe that this trait may be true
only for object types, even though, for example, reference types may be guaranteed identical bit representations when they refer to the same object, just as the corresponding pointer type would be bitwise EqualityComparable. t_TYPE
has unique representations for each possible value, and no padding bits. This property is deemed to hold for bool
and enumerations where, in practice, the compiler will enforce a value representation over all the seemingly unused bits. For a C++17 tool chain, this trait should be equivalent to the std::has_unique_object_representation
trait. t_TYPE
are also bitwise EqualityComparable, even though they do not provide an overloaded operator==
. While transforming comparisons of a single object using this trait into calls to memcmp
is unlikely to be profitable, transforming comparisons of a whole array into a single memcmp
call is more likely to be beneficial. false
as soon as any pair of elements do not compare equal; if we walk all the way to the end of both sequences, then they hold the same values. If we want to perform this comparison most efficiently though, we would rather not invoke operator==
on each member, and instead defer to the memcmp
function in the standard library that is highly optimized (often to take advantage of platform-specific instructions) for comparing ranges of raw memory. We can switch to this other technique only if we know that the value representations of a type are unique, rely on all of the bits in their representation, and do not have strange values like NaN
that self-compare as false
. This property is denoted by the IsBitwiseEqualityComparable
trait. struct
that contains a char
and a short
as its two data members, and supported comparison with operator==
. Note that there will be a byte of padding between the char
and the short
members to ensure proper alignment. We insert telemetry to count the number of times operator==
is called: namespace BloombergLP { struct SimpleType { // This 'struct' holds two data members with a byte of padding, and can // be compared using the overloaded 'operator=='. char d_dataC; short d_dataS; static int s_comparisons; friend bool operator==(const SimpleType& a, const SimpleType& b) // Return 'true' if the specified 'a' has the same value as the // specified 'b'. Two 'SimpleType' objects have the same value if // their corresponding 'd_dataC' and 'd_dataS' members have the // same value. The static data member 's_comparisons' is // incremented by one each time this function is called. { ++s_comparisons; return a.d_dataC == b.d_dataC && a.d_dataS == b.d_dataS; } friend bool operator!=(const SimpleType& a, const SimpleType& b) // Return 'true' if the specified 'a' does not have the same value // as the specified 'b'. Two 'SimpleType' objects do not have the // same value if their corresponding 'd_dataC' and 'd_dataS' // members do not have the same value. The static data member // 's_comparisons' is incremented by one each time this function is // called. { ++s_comparisons; return a.d_dataC != b.d_dataC || a.d_dataS != b.d_dataS; } }; int SimpleType::s_comparisons = 0;
struct
that wraps a single int
as its only data member, and supports comparison with operator==
, inserting telemetry to count the number of times operator==
is called: struct SecondType { // This 'struct' holds a single 'int' member, 'd_data', and can be // compared using the overloaded 'operator=='.
SecondType
using the BDE nested trait declaration facility: BSLMF_NESTED_TRAIT_DECLARATION(SecondType, bslmf::IsBitwiseEqualityComparable); int d_data; static int s_comparisons; friend bool operator==(const SecondType& a, const SecondType& b) // Return 'true' if the specified 'a' has the same value as the // specified 'b'. Two 'SecondType' objects have the same value if // their corresponding 'd_data' elements have the same value. The // static data member 's_comparisons' is incremented by one each // time this function is called. { ++s_comparisons; return a.d_data == b.d_data; } friend bool operator!=(const SecondType& a, const SecondType& b) // Return 'true' if the specified 'a' does not have the same value // as the specified 'b'. Two 'SecondType' objects do not have the // same value if their corresponding 'd_data' elements do not have // the same value. The static data member 's_comparisons' is // incremented by one each time this function is called. { ++s_comparisons; return a.d_data != b.d_data; } }; int SecondType::s_comparisons = 0;
struct
that wraps a single int
as its only data member, and supports comparison with operator==
, inserting telemetry to count the number of times operator==
is called: struct ThirdType { // This 'struct' holds a single 'int' member, 'd_data', and can be // compared using the overloaded 'operator=='. int d_data; static int s_comparisons; friend bool operator==(const ThirdType& a, const ThirdType& b) // Return 'true' if the specified 'a' has the same value as the // specified 'b'. Two 'SecondType' objects have the same value if // their corresponding 'd_data' elements have the same value. The // static data member 's_comparisons' is incremented by one each // time this function is called. { ++s_comparisons; return a.d_data == b.d_data; } friend bool operator!=(const ThirdType& a, const ThirdType& b) // Return 'true' if the specified 'a' does not have the same value // as the specified 'b'. Two 'ThirdType' objects do not have the // same value if their corresponding 'd_data' elements do not have // the same value. The static data member 's_comparisons' is // incremented by one each time this function is called. { ++s_comparisons; return a.d_data != b.d_data; } }; int ThirdType::s_comparisons = 0;
ThirdType
by explicitly specializing the trait: namespace bslmf { template <> struct IsBitwiseEqualityComparable<ThirdType> : bsl::true_type {}; } // close namespace bslmf
template <class t_TYPE> bool rangeCompare(const t_TYPE *start, size_t length, const t_TYPE *other) {
memcmp
function: if (bslmf::IsBitwiseEqualityComparable<t_TYPE>::value) { return 0 == memcmp(start, other, length * sizeof(t_TYPE)); // RETURN }
true
if we reach the end of the range. if (0 != length) { while (*start++ == *other++) { if (!--length) { return true; // RETURN } } } return false; }
operator==
is called only for SimpleType
as the other two struct
s dispatch to memcmp
instead: int usageExample1()
{
assert(0 == SimpleType::s_comparisons); assert(0 == SecondType::s_comparisons); assert(0 == ThirdType ::s_comparisons);
const SimpleType simpleZeroes[10] = { }; const SecondType secondZeroes[10] = { }; const ThirdType thirdZeroes [10] = { }; const SimpleType simpleValues[10] = { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 }; const SecondType secondValues[10] = { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 }; const ThirdType thirdValues [10] = { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 };
SimpleType
without the bitwise EqualityComparable trait: assert(!rangeCompare(simpleZeroes, 10u, simpleValues) ); assert(!rangeCompare(secondZeroes, 10u, secondValues) ); assert(!rangeCompare(thirdZeroes, 10u, thirdValues) ); assert(0 < SimpleType::s_comparisons); assert(0 == SecondType::s_comparisons); assert(0 == ThirdType ::s_comparisons);
SimpleType
without the bitwise EqualityComparable trait: SimpleType::s_comparisons = 0; assert( rangeCompare(simpleValues, 10u, simpleValues) ); assert( rangeCompare(secondValues, 10u, secondValues) ); assert( rangeCompare(thirdZeroes, 10u, thirdZeroes) ); assert(0 < SimpleType::s_comparisons); assert(0 == SecondType::s_comparisons); assert(0 == ThirdType ::s_comparisons); return 0; } } // close enterprise namespace
IsBitwiseEqualityComparable
trait. First, we define a class template that is not bitwise EqualityComparable, NotComparable
: namespace BloombergLP { template <class t_TYPE> struct NotComparable { t_TYPE d_value; };
PotentiallyComparable1
, which uses partial template specialization to associate the IsBitwiseEqualityComparable
trait with each instantiation on a t_TYPE
that is itself bitwise EqualityComparable: template <class t_TYPE> struct PotentiallyComparable1 { t_TYPE d_value; }; namespace bslmf { template <class t_TYPE> struct IsBitwiseEqualityComparable<PotentiallyComparable1<t_TYPE> > : IsBitwiseEqualityComparable<t_TYPE>::type { }; } // close namespace bslmf
BSLMF_NESTED_TRAIT_DECLARATION
macro to associate the IsBitwiseEqualityComparable
trait with each instantiation on a t_TYPE
that is itself bitwise EqualityComparable: template <class t_TYPE> struct PotentiallyComparable2 { t_TYPE d_value; BSLMF_NESTED_TRAIT_DECLARATION_IF( PotentiallyComparable2, bslmf::IsBitwiseEqualityComparable, bslmf::IsBitwiseEqualityComparable<t_TYPE>::value); };
IsBitwiseEqualityComparable<T>value
in each case: int usageExample2() { using namespace bslmf; assert(!IsBitwiseEqualityComparable<NotComparable<int> >::value); assert(!IsBitwiseEqualityComparable< NotComparable<NotComparable<int> > >::value); assert( IsBitwiseEqualityComparable< PotentiallyComparable1<int> >::value); assert(!IsBitwiseEqualityComparable< PotentiallyComparable1<NotComparable<int> > >::value); assert( IsBitwiseEqualityComparable< PotentiallyComparable2<int> >::value); assert(!IsBitwiseEqualityComparable< PotentiallyComparable2<NotComparable<int> > >::value); return 0; } } // close enterprise namespace