Thomas Schwinge <[email protected]> writes:

> Hi!
>
> On 2026-05-07T13:16:59+0200, I wrote:
>> As much as possible, I'd like to implemented and upstream the necessary
>> changes already in the current C implementation (for example, make 'enum'
>> usage compatible for both C and C++), but that won't be possible for all
>> changes, obviously.  I'll reach out in separate emails for any cases
>> where it's not obvious which way is best to address certain C vs. C++
>> incompatibilities.
>
> So, first item: what do we do with the untyped 'gomp_malloc' etc.?
> 'libgomp/libgomp.h':
>
>     extern void *gomp_malloc (size_t) __attribute__((malloc));
>
> ..., and a good number of similar others.
>
> My current assumption is that we leave these untyped, do not templatize
> them?
>
> In other words, we'll need some kind of type casting at each call site:
>
>     gomp_mutex_t *plock;
>     [...]
>     plock = gomp_malloc (sizeof (gomp_mutex_t));
>
> I could already in the C implementation make that:
>
>     plock = (gomp_mutex_t *) gomp_malloc (sizeof (gomp_mutex_t));
>
> ..., or (preferably?):
>
>     plock = (typeof (plock)) gomp_malloc (sizeof (gomp_mutex_t));
>
> Such changes could go in already now, in the C code.  However, I assume
> that we'd eventually like this to be proper C++:
>
>     plock = static_cast<decltype(plock)>(gomp_malloc (sizeof (gomp_mutex_t)));
>
> ..., or not?  If yes, then all those numerous changes have to be part of
> the C++ switch commit.

No, we can implement an 'operator new' overload for it:

  struct gomp_malloc_tag {};
  gomp_malloc_tag use_gomp_malloc;

  void *
  operator new (std::size_t sz, gomp_malloc_tag)
  { return gomp_malloc (sz); }

  /* called as: new (use_gomp_malloc) T{} */

... , or even just make a:

  template<typename T, typename... Args>
  auto gomp_new (Args&&... args)
  { return new (gomp_malloc (sizeof (T))) T (std::forward<Args> (args)...); }

  /* gomp_new<gomp_thing> () */

... or such.  (didn't try either snippet, but both should be mostly
right.  Maybe even plain 'new' works and we can drop 'gomp_malloc' usage
altogether, given that we should be able to link libsupc++; that should
throw in case of being out of memory, and, since we have no exceptions,
crash, I think?)

See https://en.cppreference.com/cpp/memory/new/operator_new

Note that it's not always correct to do the static_cast you suggested.
The object lifetime does not begin if you do that.  It would also mean
that we lose the possibility of using constructors, which is also among
the biggest benefits of using C++.

We *could* also just do:

  T *x = new (gomp_malloc (sizeof (T))) T;

... of course, but that's clunkier.

> In the external header files ('omp.h', 'openacc.h', etc.), I guess we'll
> just continue to use C-style casts for C/C++ compatibility, or some:
>
>     #ifdef __cplusplus
>     # define STATIC_CAST(T, x) static_cast<T>(x)
>     #else
>     # define STATIC_CAST(T, x) ((T) (x))
>     #endif
>
> ..., but I'd like to avoid that in libgomp implementation and internal
> headers files, and instead make the proper C++?
>
> Anyone have any strong preferences or other yet other suggestions?

What code is there in headers in the existing C API that'd require this?
I do not recall any.

>
> On the other hand, we already have code like this:
>
>     gomp_mutex_t *nlock = (gomp_mutex_t *) gomp_malloc (sizeof 
> (gomp_mutex_t));
>
> ..., which should continue to work for C++ compilation, via the C-style
> cast, but I assume that we'd also switch those to the agreed C++-style?

Yes, these need to be replaced also.  Not just for style.
-- 
Arsen Arsenović

Attachment: signature.asc
Description: PGP signature

Reply via email to