Can I interface with C++ objects from C without using an opaque pointer?

Viewed 79

This is an idea I thought of that maybe I could apply to my C project that currently interfaces with some C++ objects through opaque pointers. That's the usual way to do it, but dynamic allocation is unavoidable.

The following code shows my idea by using std::mt19937 as an example. No dynamic allocation is needed, but some #ifdefs might be required for different language implementations.

// C interface

typedef struct {
  // match sizeof and alignof std::mt19937
  // `#ifdef`s might be required
  alignas(8) char raw[5000];
} t_rand;

void rand_init(t_rand *, uint32_t);
void rand_fin(t_rand *);
uint32_t rand_gen_uint32(t_rand *);
// C++ implementation

#include <memory>
#include <random>

void rand_init(t_rand *r, uint32_t seed) {
  static_assert(
    sizeof(t_rand) == sizeof(std::mt19937)
      && alignof(t_rand) == alignof(std::mt19937));
  std::construct_at(reinterpret_cast<std::mt19937 *>(r), seed);
}

void rand_fin(t_rand *r) {
  std::destroy_at(reinterpret_cast<std::mt19937 *>(r));
}

uint32_t rand_gen_uint32(t_rand *r) {
  return (*reinterpret_cast<std::mt19937 *>(r))();
}

Is this a doable approach? If not, what are the problems, and what would be a better way? If it's okay, I'd also like to know if there's an actual project that uses the same pattern.


BTW, I think it was the wrong decision to set result_type or std::mt19937 as uint_fast32_t, which is usually uint64_t on 64-bit platforms, to avoid zero/sign-extension. It should have been uint_least32_t to consume less memory and provide better L1 cache hits.

0 Answers
Related