c++openmpconstexpr

OpenMP directive in constexpr functions


According to OpenMP specification (2.1. Directive Format)

Directives may not appear in constexpr functions or in constant expressions. Variadic parameter packs cannot be expanded into a directive or its clauses except as part of an expression argument to be evaluated by the base language, such as into a function call inside an if clause.

I extensively use OpenMP in constexpr functions until g++14.2 without compiler made a warning. But using g++15 is an error.

The following is not a g++14.2 error but it is a g++15 error.

error: OpenMP directives may not appear in 'constexpr' functions [-Wtemplate-body]

constexpr void func(size_t sz) noexcept
{
     #ifdef _OPENMP
     #pragma omp parallel for
     #endif
     for (size_t i = 0; i < sz; ++i) something(i);
}

Which is the reason for this restriction? Compiler has the ability to execute OpenMP-aware code without enabling OpenMP. Did I miss something?

I am trying to patch my code with the following workaround. g++15 does not forbid this but I don't know if it breaks again the OpenMP standard and in e.g. g++16 I must again patch my code.

Is the following accepted from OpenMP specification? It is accepted from g++15 (and previous).

constexpr void func(size_t sz) noexcept
{
    auto fcommon = [](size_t i) {};
    auto fomp    = [sz]()
    {
         #ifdef _OPENMP
         #pragma omp parallel for
         #endif
         for (size_t i = 0; i < sz; ++i) fcommon(i);
    };
    auto fseq    = [sz]() { for (size_t i = 0; i < sz; ++i) fcommon(i); };

    if (std::is_constant_evaluated()) fseq(); else fomp();
}

Solution

  • Generally, an implementation can give you more than what the specs guarantee. A problem it is when it gives you less. That there was no error before shouldn't come as a surprise. You broke the rules, so the specs cannot help you to tell if the code is ok or not.

    On the other hand, as you used it and it worked as expected I suppose also your new variant would work as expected. You just cannot count on it. It may break on the next release of openmp or the compiler.

    In your new code you are already branching to call two different functions. You can go one step further by writing one function with the OpenMp directives that is not constexpr and another function that is constexpr without OpenMp. In the actual function you branch depending on std::is_constant_evaluated() to call either of the two. Then you are in line with the OpenMp specifications.