I think the best thing to do is to simply try it out. You can look at the tests I've written in test_pp_list.cpp as an example of how to use the more complicated macros.

Most of the macros behave like their Python counterparts. DYND_PP_RANGE(4) returns (0, 1, 2, 3). DYND_PP_MAP(MAC, (0, 1, 2, 3)) returns (MAC(0), MAC(1), MAC(2), MAC(3)). DYND_PP_REPEAT(TOK, 4) returns (TOK, TOK, TOK, TOK).

DYND_PP_ELWISE and DYND_PP_OUTER behave like the dynd functions we are implementing. DYND_PP_ELWISE(MAC, (0, 1, 2), (3, 4, 5)) returns (MAC(0, 3), MAC(1, 4), MAC(2, 5)). DYND_PP_OUTER(MAC, (0, 1, 2), (3, 4, 5)) returns (MAC(0, 3), MAC(0, 4), MAC(0, 5), MAC(1, 3), MAC(1, 4), MAC(1, 5), MAC(2, 3), MAC(2, 4), MAC(2, 5)).

The JOIN versions of the above take an arbitrary separator in parentheses, like (,), (;), or () for nothing, and apply the same operation except with enclosing the final result in parentheses. The JOIN versions are needed for doing the metaprogramming.