yu lei
yu lei

Reputation: 47

Is there approach to do cartesian product on two lists with C Macro?

For example, I have this code:

#define LIST_A \
    Element1 \
    Element2 \
    Element3

#define LIST_B \
    Element4 \
    Element5

// I'd like to perform some operations on tuples of LIST_A * LIST_B
// That is a set of tuples {(1, 4), (1, 5), (2, 4), (2, 5), (3, 4), (3, 5)}

The real problem I'm facing is that I have some C++ template functions like:

// Left, Right, Result can be any numeric types
template <typename Left, typename Right, typename Result>
struct Function
{
    void apply(const Left & lhs, const Right & rhs, Result & res);
};

And I'd like to exhaust the specialization space of Function.

For example I have following input data type:

#define LEFT_TYPE_LIST\
    int32_t \
    int64_t 

#define RIGHT_TYPE_LIST\
    uint32_t \
    uint64_t

I want to apply LEFT_TYPE_LIST and RIGHT_TYPE_LIST to Function<Left, Right, Result>.

Is there a practical way to implement this?

Upvotes: 1

Views: 336

Answers (2)

Jarod42
Jarod42

Reputation: 218148

Without macro, you might do something like:

template <typename T> struct tag { using type = T; };

using LEFT_TYPE_LIST = std::tuple<tag<int32_t>, tag<int64_t>>;
using RIGHT_TYPE_LIST = std::tuple<tag<uint32_t, tag<int64_t>>; 

and then

std::apply([](auto... lefts){
    auto f = [](auto left){
        std::apply([left](auto... rights){
            (Function<typename decltype(left)::type,
                      typename decltype(rights)::type>(),
             ...);
        }, RIGHT_TYPE_LIST{});
    };
    (f(lefts), ...);
}, LEFT_TYPE_LIST{});

Demo

Upvotes: 1

M Oehm
M Oehm

Reputation: 29126

If you don't mind the awkward syntax, you can use X macros:

#define TYPES1(WITH, _)  \
    _(WITH, Apple)       \
    _(WITH, Orange)      \
    _(WITH, Pear)

#define TYPES2(WITH, _)  \
    _(WITH, Apple)       \
    _(WITH, Kumquat)

Now you can define macros like this:

#define CMP(A, B) compare(A, B);
#define COMPARE(W, T) W(T, CMP)

and apply lists to each other (but unfortunalely not to themselves):

TYPES1(TYPES2, COMPARE)
TYPES2(TYPES1, COMPARE)

How does this work? The _ parameter passed to the macro list is a macro itself and it expands according to its arguments. Here, the _ macro must be a macro that takes two parameters.

This is usually done to establish different contexts with different macros, for example to have the argument expand to an enum constant once and to a stringized names later. Here, the different contexts are the levels of invocation. The WITH parameter is either the first object (at the lowest level) or anoter X macro.

For completeness sake: Macros will get you only so far. Sometimes it is better to prepare such data externally by a script or program that creates a file you can include. You can make that "code generation" part of your build process. If you have a lot of such macros or if the macros get bizarre, that might be a better option.

Upvotes: 5

Related Questions