Defining the syntax for improving a macro for a specific function

I created a function declared as:

template <typename Container, typename Task>
void parallel_for_each(Container &container, Task task,
                       unsigned number_of_threads = std::thread::hardware_concurrency())

      

It's not hard to guess what he should do. I would like to create a macro to simplify the syntax of this function and give it a "loop-like" syntax. I came up with an idea:

#define in ,
#define pforeach(Z,X,Y) parallel_for_each(X,[](Z)->void{Y;})

      

Where used as:

pforeach(double &element, vec,
    {
     element *= 2;
    });

      

works as expected, but this one:

pforeach(double &element in vec,
    {
     element *= 2;
     element /= 2;
    });

      

gives error

the "pforeach" macro requires 3 arguments, but only 2 data

Do you have any idea of โ€‹โ€‹writing a macro that allows for even "nicer" syntax? Why doesn't "in" support comma in my code?

+3


source to share


4 answers


The reason in

not replaced is because it appears inside an argument of your macro file, like a function, but to replace it, those arguments must first propagate to another macro: Try

#define in ,
#define pforeach_(Z,X,Y) parallel_for_each(X,[](Z)->void{Y;})
#define pforeach(Z,X,Y) pforeach_(Z,X,Y)

      

Note : Determining in

How ,

Doesn't End Well!


Idea to add "better" syntax:



template <typename Container>
struct Helper {
    Container&& c;
    template <typename Arg>
    void operator=(Arg&& arg) {
        parallel_for_each(std::forward<Container>(c), std::forward<Arg>(arg));
    }
};

#define CONCAT_(a,b) a##b
#define CONCAT(a,b) CONCAT_(a,b)
// Easier with Boost.PP
#define DEC_1 0
#define DEC_2 1
#define DEC_3 2
#define DEC_4 3
#define DEC_5 4
#define DEC_6 5
#define DEC_7 6
#define DEC_8 7
#define DEC(i) CONCAT(DEC_,i)

#define pforeach(Z, ...) \
  Helper<decltype((__VA_ARGS__))> CONCAT(_unused_obj, __COUNTER__){__VA_ARGS__}; \
  CONCAT(_unused_obj, DEC(__COUNTER__))=[](Z)

      

Used like

int a[] = {1, 2, 3};
pforeach(int i, a) {
    std::cout << i << ", ";
};

pforeach(int i, std::vector<int>{1, 2, 3}) {
    std::cout << -i << ", ";
};

      

Demo .
However, it has several disadvantages. I just stick with what you have.

+4


source


Why doesn't "in" support comma in my code?

Since this replacement is done afterwards , macros are defined. Quoting standard draft N3797, ยง 16.3.1. Argument Substitution:

Once the arguments for calling the macro-like macro have been defined, the argument is replaced .... Before replacement, each argument preprocessing token is completely replaced by the macro, as if they were the rest of the preprocessing file; no other preprocessing tokens are available.

So, the preprocessor is identified pforeach(double &element in vec, {})

as a functionally similar macro call with two arguments:

  • First, tokens consist of double

    , &

    , in

    and vec

    and are linked to the argumentZ

  • The second consists of tokens {

    and }

    and is bound to the argumentX



You are clearly missing an argument Y

Do you have any idea of โ€‹โ€‹writing a macro that allows for even "nicer" syntax?

It is difficult to answer and it is a matter of taste. Anyway, C ++ has rich syntax fixing capabilities with operator overloading, but you can't build a DSL with it, so it's better to use the default syntax, it's not that ugly (and also makes it easier to read):

 parallel_for_each(vec, [](double& el){ el *= 2; })

      

+1


source


There are no macro languages. Macros are processed by the C / C ++ preprocessor. The implementation of preprocessors may vary.

Most preprocessors expect you to pass in the exact number of parameters. I found that the GNU preprocessor has less strict parameter checking, which allows for a kind of variation list. But in general, the macro will not help you with your task.

I recommend writing a short statement in a function instead of a macro. A built-in function is as fast and short as a macro, but it is of type safe. In addition, the function accepts default parameter values. So you might be missing something.

0


source


Trying to improve on @ Columbo's idea:

template <typename Container>
struct __pforeach__helper {
    Container &&c;
    template <typename Arg>
    void operator=(Arg&& arg) {
        parallel_for_each(std::forward<Container>(c), std::forward<Arg>(arg));
    }
};

//additional helper function
template <typename Container>
__pforeach__helper<Container> __create__pforeach__helper(Container &&c)
{
    return __pforeach__helper<Container>(__pforeach__helper<Container>{c});
}

#define pforeach(Z,C) \
  __create__pforeach__helper(C)=[](Z)

      

It does not rely on __COUNTER__

and does not require the definition of macros DEC_x

. Any feedback is greatly appreciated!

0


source







All Articles