BDE 4.14.0 Production release
|
Provide a type trait for bitwise equality.
This component provides a single 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.
For a type to be bitwise EqalityComparable, each bit of its object representation must be significant in the value representation, and distinct sequences of bits represent different values, i.e., this trait is an assertion that the specified 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.
Note that as arrays are not allowed to introduce padding, arrays of a bitwise EqualityComparable 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.
Suppose we want to compare two sequences of the same object type to determine whether or not they hold the same values. The simplest solution would be to iterate over both sequences, comparing each member, and return 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.
First, we create a simple 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:
Then, we create another 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:
We associate the bitwise EqualityComparable trait with SecondType
using the BDE nested trait declaration facility:
Next, we create another 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:
We associate the bitwise EqualityComparable trait with ThirdType
by explicitly specializing the trait:
Now, we write a function template to compare two arrays of the same type:
If we detect the bitwise EqualityComparable trait, we rely on the optimized memcmp
function:
Otherwise we iterate over the range directly until we find a pair of elements that do not have the same value, and return true
if we reach the end of the range.
Finally, we write a test to confirm that two arrays containing different values do not compare as equal (using our array comparison function), and that an array compares equal to itself, as it does comprise elements all having the same value. By inspecting the static data members provided for telemetry, we can confirm that operator==
is called only for SimpleType
as the other two struct
s dispatch to memcmp
instead:
We confirm the initial state of the telemetry:
Then we create zero-initialized arrays for each of the types to be tested, and a second array for each type with a set of values distinct from all zeroes:
Next we confirm that the two arrays (of each type) do not compare equal, and inspect the telemetry to confirm that the comparison operator was called for only the SimpleType
without the bitwise EqualityComparable trait:
Then we reset the telemetry and confirm that an array of each type compares equal to itself, and inspect the telemetry to confirm that the comparison operator was called for only the SimpleType
without the bitwise EqualityComparable trait:
In this example, we associate a trait not with a class, but with a class template. We create a "control" template that is not bitwise EqualityComparable, and two class templates, each of which uses a different mechanisms for being associated with the IsBitwiseEqualityComparable
trait. First, we define a class template that is not bitwise EqualityComparable, NotComparable
:
Then, we define the class template PotentiallyComparable1
, which uses partial template specialization to associate the IsBitwiseEqualityComparable
trait with each instantiation on a t_TYPE
that is itself bitwise EqualityComparable:
Next, we define the class template'PotentiallyComparable2', which uses the BSLMF_NESTED_TRAIT_DECLARATION
macro to associate the IsBitwiseEqualityComparable
trait with each instantiation on a t_TYPE
that is itself bitwise EqualityComparable:
Finally, we check that the traits are correctly associated by instantiating each template with types that are bitwise EqualityComparable and with types that are not not bitwise EqualityComparable, verifying the value of IsBitwiseEqualityComparable<T>::value
in each case: