Rames
Rames

Reputation: 938

Defining macro improving syntax of specific function

I've 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 difficult to guess what it is supposed to do. I'd like to create a macro simplifying the syntax of this function and making the its syntax "loop-like". I've come up with an idea:

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

Where usage as:

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

works as expected, but this one:

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

gives an error

macro "pforeach" requires 3 arguments, but only 2 given

Do you have any idea how to write a macro allowing even "nicer" syntax? Why "in" doesn't stand for comma in my code?

Upvotes: 3

Views: 174

Answers (4)

Rames
Rames

Reputation: 938

Trying to improve the idea of @Columbo :

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 doesn't rely on __COUNTER__ and doesn't require defining DEC_x macros. Any feedback is most welcome!

Upvotes: 0

Columbo
Columbo

Reputation: 60999

The reason that in is not replaced is that it appears inside an argument to your function-like macro, but for it to be replaced, those arguments have to be propagated to another macro first: 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: Defining in as , is not gonna end well!


An idea to add "nicer" 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)

Usable as

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.
Has several disadvantages though. I'd just stick with what you've got so far.

Upvotes: 4

myaut
myaut

Reputation: 11504

Why "in" doesn't stand for comma in my code?

Because that replacement is performed after macro arguments are determined. Quoting standard draft N3797, § 16.3.1 Argument substitution:

After the arguments for the invocation of a function-like macro have been identified, argument substitution takes place. ... Before being substituted, each argument’s preprocessing tokens are completely macro replaced as if they formed the rest of the preprocessing file; no other preprocessing tokens are available.

So preprocessor identifies pforeach(double &element in vec, {}) as a function-like macro call with two arguments:

  1. First consists of tokens double, &, in and vec and bound to argument Z
  2. Second consists of tokens { and } and bound to argument X

You're obviously miss argument Y

Do you have any idea how to write a macro allowing even "nicer" syntax?

It is hard to answer and it is matter of taste. Anyway, C++ has rich capabilities of patching syntax with operator overload, but you can't build DSL with that, so it is better to use default syntax, it is not that ugly (and also makes it easy to read):

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

Upvotes: 1

harper
harper

Reputation: 13690

There is no macro langugae. Macros are handled by the C/C++ preprocessor. The implementation of the preprocessors may vary.

Most preprocessors expect that you pass the exact number of parameters. I found that the GNU preprocessor has a less strict checking of parameters what allows a kind of variadic list. But in general a macro won't help you with your task.

I recommend to write the short statement in a function instead of a macro. An inline function is as fast and short as a macro, but type safe. Further the function allows default parameter values. So you can skip something.

Upvotes: 0

Related Questions