Reputation: 938
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
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
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
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:
double
, &
, in
and vec
and bound to argument Z
{
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
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