vmd/doc/vmd_equality.qbk

198 lines
8.6 KiB
Plaintext

[/
(C) Copyright Edward Diener 2011-2015
Distributed under the Boost Software License, Version 1.0.
(See accompanying file LICENSE_1_0.txt or copy at
http://www.boost.org/LICENSE_1_0.txt).
]
[section:vmd_assert Testing for equality and inequality]
VMD allows the programmer to test generically for the equality or inequality
of any value which VMD can parse. This includes emptiness, identifiers, numbers,
types, arrays, lists, seqs, tuples, and multi-element sequences.
The macro to test for equality is called BOOST_VMD_EQUAL and it has two required
parameters which are the two values against which to test. The values can be any
VMD data type.
For the composite data types of array, list, seq, and tuple, or any of those types
in a multi-element sequence, the elements of those types must also be a data type
which VMD can parse. BOOST_VMD_EQUAL recursively parses the elements in a composite data type
for equality, up to a level of 16 inner types, to test that one composite type
equals another composite type. The requirement, that composite elements must also
be a data type which VMD can parse, is different from most other macros
in the VMD library, where only the top-level composite type need be parsed enough to
determine the type of the data. If BOOST_VMD_EQUAL encounters a data type which it
cannot parse the result will be UB.
VMD identifiers used in equality testing must be registered and pre-detected.
All numbers and v-types are already registered/pre-detected for equality testing
so it is only user-defined identifiers which must be registered and pre-detected.
If an identifier has not been both registered and predetected it will never be
equal to the same identifier value, so it will always fail equality testing,
although it will not give a preprocessing error doing so.
The BOOST_VMD_EQUAL macro returns 1 if both parameters are equal and 0 if the
parameters are not equal.
Conversely to test for inequality, of the same values as are required in testing
for equality, the VMD library has the macro BOOST_VMD_NOT_EQUAL. This macro is simply
a complement of the BOOST_VMD_EQUAL macro. If BOOST_VMD_EQUAL returns 1 then
BOOST_VMD_NOT_EQUAL returns 0 and if BOOST_VMD_EQUAL returns 0 then
BOOST_VMD_NOT_EQUAL returns 1.
The BOOST_VMD_EQUAL and BOOST_VMD_NOT_EQUAL macros are called "equality macros".
#include <boost/vmd/equal.hpp>
#define BOOST_VMD_REGISTER_AN_ID1 (AN_ID1)
#define BOOST_VMD_REGISTER_AN_ID2 (AN_ID2)
#define BOOST_VMD_DETECT_AN_ID1_AN_ID1
#define BOOST_VMD_DETECT_AN_ID2_AN_ID2
#define AN_IDENTIFIER1 AN_ID1
#define AN_IDENTIFIER2 AN_ID2
#define AN_IDENTIFIER3 AN_ID1 // same as AN_IDENTIFIER1 = AN_ID1
#define A_NUMBER1 33
#define A_NUMBER2 145
#define A_NUMBER3 33 // same as A_NUMBER1 = 33
#define A_TUPLE1 (AN_IDENTIFIER1,A_NUMBER1)
#define A_TUPLE2 (AN_IDENTIFIER1,A_NUMBER2)
#define A_TUPLE3 (AN_IDENTIFIER3,A_NUMBER3) // same as A_TUPLE1 = (AN_ID1,33)
#define A_SEQ1 (A_NUMBER1)(A_TUPLE1)
#define A_SEQ2 (A_NUMBER2)(A_TUPLE2)
#define A_SEQ3 (A_NUMBER3)(A_TUPLE3) // same as A_SEQ1 = (33)((AN_ID1,33))
BOOST_VMD_EQUAL(AN_IDENTIFIER1,AN_IDENTIFIER2) will return 0
BOOST_VMD_EQUAL(AN_IDENTIFIER1,AN_IDENTIFIER3) will return 1
BOOST_VMD_EQUAL(A_NUMBER1,A_NUMBER2) will return 0
BOOST_VMD_EQUAL(A_NUMBER1,A_NUMBER3) will return 1
BOOST_VMD_EQUAL(A_TUPLE1,A_TUPLE2) will return 0
BOOST_VMD_EQUAL(A_TUPLE1,A_TUPLE3) will return 1
BOOST_VMD_EQUAL(A_SEQ1,A_SEQ2) will return 0
BOOST_VMD_EQUAL(A_SEQ1,A_SEQ3) will return 1
When BOOST_VMD_EQUAL tests for equality it always parses data for their most
specific types. The reason for this is that a valid tuple, which is also an invalid
list or array, can never be compared completely because all elements of that tuple
are not data types which VMD can parse. Therefore VMD always tests equality based
on the most specific type for any value being tested, which speeds up testing for
the more specific tuple data types such as lists and arrays.
#define TUPLE_IS_ARRAY1 (2,(3,4))
#define TUPLE_IS_ARRAY2 (2,(4,5))
#define TUPLE_IS_ARRAY3 (2,(3,4))
#define TUPLE_IS_LIST1 (55,BOOST_PP_NIL)
#define TUPLE_IS_LIST2 (135,BOOST_PP_NIL)
#define TUPLE_IS_LIST3 (55,BOOST_PP_NIL)
#define TUPLE_IS_LIST_OR_ARRAY1 (2,(3,BOOST_PP_NIL))
#define TUPLE_IS_LIST_OR_ARRAY2 (2,(4,BOOST_PP_NIL))
#define TUPLE_IS_LIST_OR_ARRAY3 (2,(3,BOOST_PP_NIL))
#define TUPLE_BUT_INVALID_ARRAY1 (&2,(3,4))
#define TUPLE_BUT_INVALID_ARRAY2 (&2,(4,4))
#define TUPLE_BUT_INVALID_ARRAY3 (&2,(3,4))
#define TUPLE_BUT_INVALID_LIST1 (55,^BOOST_PP_NIL)
#define TUPLE_BUT_INVALID_LIST2 (135,^BOOST_PP_NIL)
#define TUPLE_BUT_INVALID_LIST3 (55,^BOOST_PP_NIL)
All of the constructs above are valid tuples.
The first three are valid arrays, so they will be parsed and compared
as arrays, so that they can be used as in:
#include <boost/vmd/equal.hpp>
BOOST_VMD_EQUAL(TUPLE_IS_ARRAY1,TUPLE_IS_ARRAY2) will return 0
BOOST_VMD_EQUAL(TUPLE_IS_ARRAY1,TUPLE_IS_ARRAY3) will return 1
The next three are valid lists, so they will be parsed and compared
as lists, so that they can be used as in:
#include <boost/vmd/equal.hpp>
BOOST_VMD_EQUAL(TUPLE_IS_LIST1,TUPLE_IS_LIST2) will return 0
BOOST_VMD_EQUAL(TUPLE_IS_LIST1,TUPLE_IS_LIST3) will return 1
The next three are valid lists or arrays but will be parsed as lists
because lists are more specific than arrays. They can be used as in:
#include <boost/vmd/equal.hpp>
BOOST_VMD_EQUAL(TUPLE_IS_LIST_OR_ARRAY1,TUPLE_IS_LIST_OR_ARRAY2) will return 0
BOOST_VMD_EQUAL(TUPLE_IS_LIST_OR_ARRAY1,TUPLE_IS_LIST_OR_ARRAY3) will return 1
The next three are valid tuples but invalid arrays. The BOOST_VMD_EQUAL
macro attempts to parse them as the most specific type they can be, which is an
array. But the attempt to parse them as arrays will lead to UB
because the number which signifies the size of the array is invalid as
a number. Now let us suppose we should parse them as the less specific type
of a tuple instead of as an array. This will still give UB
if we will attempt to compare the first tuple element against a corresponding
first tuple element of another tuple, and when we do will again encounter UB
because it is not a data type VMD can parse.
#include <boost/vmd/equal.hpp>
BOOST_VMD_EQUAL(TUPLE_BUT_INVALID_ARRAY1,TUPLE_BUT_INVALID_ARRAY1) will generate UB
BOOST_VMD_EQUAL(TUPLE_BUT_INVALID_ARRAY1,TUPLE_BUT_INVALID_ARRAY1) will generate UB
The next three are valid tuples but invalid lists. The BOOST_VMD_EQUAL
macro attempts to parse them as the most specific type they can be, which is
a list. But the attempt to parse them as lists will lead to UB
because the identifier which signifies the end-of-list is invalid as
an identifier. Now let us suppose we should parse them as the less specific type
of a tuple instead of as a list. This will still give UB
if we will attempt to compare the second tuple element against a corresponding
second tuple element of another tuple, and when we do will again encounter UB
because it is not a data type VMD can parse.
#include <boost/vmd/equal.hpp>
BOOST_VMD_EQUAL(TUPLE_BUT_INVALID_LIST1,TUPLE_BUT_INVALID_LIST2) will generate UB
BOOST_VMD_EQUAL(TUPLE_BUT_INVALID_LIST1,TUPLE_BUT_INVALID_LIST3) will generate UB
It is possible that a composite data type which has an element which VMD cannot parse
will not give UB when compared for equality, but rather just the test for equality
will fail. This can occur if the algorithm which tests for equality tests false before parsing of
the particular element. Such a situation might be:
#include <boost/vmd/equal.hpp>
#define A_TUPLE1 (3,4,"astring")
#define A_TUPLE2 (3,4)
BOOST_VMD_EQUAL(A_TUPLE1,A_TUPLE2) will return 0 rather than generate UB
The reason the above correctly returns 0, rather than generate UB when
VMD attempts to parse '"astring"', which is not a data type VMD can parse, is because the
algorithm for testing equality tests whether or not the tuples have the same number of elements
before it tests for the equality of each element. This is just one example where testing for
equality may fail before UB is generated when BOOST_VMD_EQUAL attempts to
parse a data type which it cannot handle. Nevertheless the general rule should still be considered
that for BOOST_VMD_EQUAL/BOOT_VMD_NOT_EQUAL all data types, even an element of a composite data
type, must be a VMD data type if the macro is to work properly, else UB could occur.
[heading Usage]
You can use the general header file:
#include <boost/vmd/vmd.hpp>
or you can use the individual header files:
#include <boost/vmd/equal.hpp> for the BOOST_VMD_EQUAL macro
#include <boost/vmd/not_equal.hpp> for the BOOST_VMD_NOT_EQUAL macro
[endsect]