Testing for equality and inequality

| Home | Libraries | People | FAQ | More

### 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.

##### 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