Iteration limit in Boost Preprocessor

560 views Asked by At

I am writing a unit test using the Boost::Test framework for a comparison function. For each test case I create a series of input elements and compare them pairwise to check the return value of the comparison function for each pair. I can either write it out manually or write a function. Writing a function is not good in unit tests because we get less useful information when there is a failure. Writing out each check manually results in very long unit tests. So I decided to use macros for iterating through the elements to be checked. It looks like this:

#define CHECK_NODE_LESS(less, more) do {\
        BOOST_CHECK_EQUAL(compareQueueUnderTest((less), (more)), true); \
        BOOST_CHECK_EQUAL(compareQueueUnderTest((more), (less)), false); \
} while (false)

#define CHECK_NODE_ORDER_ELEMENT(r, elem1, elem2) CHECK_NODE_LESS(elem1, elem2);

#define CHECK_NODE_ORDER_INNER_LOOP(r, list, i, elem) \
        BOOST_PP_SEQ_FOR_EACH(CHECK_NODE_ORDER_ELEMENT, elem, \
                        BOOST_PP_SEQ_REST_N(i, list))

#define CHECK_NODE_ORDER(nodes) \
        BOOST_PP_SEQ_FOR_EACH_I(CHECK_NODE_ORDER_INNER_LOOP, \
                        BOOST_PP_SEQ_TAIL(nodes), nodes)

In test cases with partial but no total ordering I use the CHECK_NODE_LESS macro on appropriate elements to check the elements that are ordered, and it works fine. In test cases with total ordering I use the CHECK_NODE_ORDER macro. For example:

CHECK_NODE_ORDER((node1)(rootNode1)(node2)(rootNode2));

Now this one does not compile. The first thing I tested is to comment out the CHECK_NODE_LESS macro and run the preprocessor to see what is generated. It was what I expected: CHECK_NODE_LESS was called for the right elements. Then I reintroduced CHECK_NODE_LESS and ran the preprocessor to see what happens. The result was really ugly because of the macros used in Boost Test, but it could be seen that some macros are not expanded.

Finally, I changed CHECK_NODE_LESS to the following and now it works fine:

#define CHECK_NODE_LESS(less, more) do {\
        BOOST_CHECK(compareQueueUnderTest((less), (more))); \
        BOOST_CHECK(!compareQueueUnderTest((more), (less))); \
} while (false)

I think the problem is that either there is some iteration limit in the Boost Preprocessor library that is exceeded when I use BOOST_CHECK_EQUAL together with the other macros, or there is a preprocessor depth limit in the compiler (I use Clang 3.4). What is this limit and how do I increase it?

1

There are 1 answers

1
seldon On BEST ANSWER

Good Lord.

It is quite possible that no one working on Boost.Test has considered what you're trying to do. The problem is not a compiler limitation, it's that BOOST_PP_SEQ_FOR_EACH is not reentrant and that BOOST_CHECK_EQUAL uses it. Because of this, using BOOST_CHECK_EQUAL inside BOOST_PP_SEQ_FOR_EACH is not possible. BOOST_CHECK works because it does not use BOOST_PP_SEQ_FOR_EACH.

I suggest this workaround:

#define CHECK_NODE_ORDER_INNER_LOOP(r, list, i, elem) \
    BOOST_PP_LIST_FOR_EACH(CHECK_NODE_ORDER_ELEMENT, elem, \
                           BOOST_PP_SEQ_TO_LIST(BOOST_PP_SEQ_REST_N(i, list)))

I am aware that converting the sequence to a list for the sole purpose of nesting loops is rather ugly. In my defense, it is the least ugly approach that I can think of.