5.9. Why macros do not work in a #pragma?¶
OpenMP compilers usually expand macros inside #pragma omp
, so why macros do
not work in Mercurium?
The reason is that when Mercurium compiles an OmpSs/OpenMP program we do not
enable OpenMP in the native compiler to reduce the chance of unwanted
interferences. This implies that the #pragma omp
is not known to the native
preprocessor, which leaves the #pragma omp
as is. As a consequence, macros
are not expanded and they become unusable inside pragmas.
5.9.1. Alternatives¶
Although macros are not expanded in clauses, most of the cases they are used for integer constants:
#define N 100
#pragma omp task out([N]a)
void f(int *a);
void g()
{
int a[N];
f(a);
#pragma omp taskwait
}
The example shown above will not work but fortunately in these cases one can avoid macros. A first approach replaces the macro with an enumerator. This is the most portable approach:
enum { N = 100 };
// Rest of the code
Another approach uses an const
integer:
const int N = 100;
// Rest of the code
Note
In C a const
qualified variable is not a constant expression whereas in
C++ it may be (more information).
Mercurium, though, allows this case as a constant expression in both
languages.
In case you need to change the value at compile time, you can use the following
strategy. Here assume that N_
is a macro that you change through the
invocation of the compiler (typically with a compiler flag like -DN_=100
):
enum { N = N_ };
// Rest of the code
Another feasible scenario where macros and pragmas are combined is when we want
to generate the pragma itself via macros. To do that, we could use the
_Pragma
operator introduced in C99
and C++11
:
#define STRINGIFY(X) #X
#define MY_PRAGMA(X) _Pragma(STRINGIFY(X))
#define MY_OPENMP_PARALLEL_LOOP omp parallel for
int main() {
MY_PRAGMA(MY_OPENMP_PARALLEL_LOOP)
for(int i = 0; i < 10; ++i) {
// ...
}
}
5.9.2. Why rely on the native preprocessor, then?¶
We could provide our own preprocessor, but this adds a lot of complication just to support this case (and as shown in Alternatives the typical use case can be worked around).
Complications include, but do not limit to:
- it is not trivial to know where are the system headers.
While
gcc
provides good support to know which paths it is using, not all compiler vendors provide this information. Even worse, some parameters may change the used paths between invocations of the native compiler - subtleties among preprocessor implementations. Not all preprocessors behave the same way and sometimes these differences are relevant for the native compiler headers