Skip to content

Conversation

@kevinthornberry
Copy link

@kevinthornberry kevinthornberry commented Dec 1, 2025

Add option to generate C++ wrapper classes for use with the C API. These can be used if you intend to use the C API from another C++ environment, eg across an ABI boundary.

The wrapper classes are generated if the --c-wrapper-cpp-namespace CLI argument is provided - this is the namespace for the wrappers.

Example using --c-wrapper-cpp-namespace snap::test::c_wrappers :

my_module.djinni :

Size = record {
  width: i32;
  height: i32;
}

Window = interface +c {
  static create(): Window;
  show();
  hide();
  setSize(size: Size);
  setTitle(title: string);
}

Generated Size.h

// AUTOGENERATED FILE - DO NOT MODIFY!
// This file was generated by Djinni from test.djinni

#pragma once

#include "djinni/cpp/djinni_c.h"

#ifdef __cplusplus
extern "C" {
#endif // __cplusplus

typedef djinni_record_ref test_test_Size_ref;

test_test_Size_ref test_test_Size_new(int32_t width, int32_t height);

int32_t test_test_Size_get_width(test_test_Size_ref instance);
void test_test_Size_set_width(test_test_Size_ref instance, int32_t value);

int32_t test_test_Size_get_height(test_test_Size_ref instance);
void test_test_Size_set_height(test_test_Size_ref instance, int32_t value);

#ifdef __cplusplus
} // extern "C"
#endif // __cplusplus

#ifdef __cplusplus
#include <utility>
namespace snap::test::c_wrappers {

class Size {
public:

    Size(const test_test_Size_ref& ref) : _ref(ref) { djinni_ref_retain(_ref); }
    Size(test_test_Size_ref&& ref) noexcept : _ref(std::move(ref)) {}
    Size(const Size& other) : _ref(other._ref) { djinni_ref_retain(_ref); }
    Size(Size&& other) : _ref(std::move(other._ref)) { other._ref = nullptr; }
    ~Size() { djinni_ref_release(_ref); }
    Size& operator=(const Size& other) {
      if (&other != this) {
          auto old = _ref;
          _ref = other._ref;
          djinni_ref_retain(_ref);
          djinni_ref_release(old);
       }
       return *this;
    }
    Size& operator=(Size&& other) noexcept {
      if (&other != this) {
        auto old = _ref;
        _ref = other._ref;
        other._ref = nullptr;
    
        djinni_ref_release(old);
      }
      return *this;
    }
    Size& operator=(test_test_Size_ref&& ref) {
      auto old = _ref;
      _ref = ref;
      djinni_ref_release(old);
      return *this;
    }
    Size& operator=(const test_test_Size_ref& ref) {
      auto old = _ref;
      _ref = ref;
      djinni_ref_retain(ref);
      djinni_ref_release(old);
      return *this;
    }

    operator const test_test_Size_ref&() const { return _ref; }
    const test_test_Size_ref& _djinni_ref() const { return _ref; }

    int32_t width() const {
        return test_test_Size_get_width(_ref);
    }

    Size& width(int32_t value) {
        test_test_Size_set_width(_ref, value);
        return *this;
    }

    int32_t height() const {
        return test_test_Size_get_height(_ref);
    }

    Size& height(int32_t value) {
        test_test_Size_set_height(_ref, value);
        return *this;
    }

private:
    test_test_Size_ref _ref;
};

} // namespace snap::test::c_wrappers
#endif // __cplusplus

Generated Window.h :

// AUTOGENERATED FILE - DO NOT MODIFY!
// This file was generated by Djinni from test.djinni

#pragma once

#include "djinni/cpp/djinni_c.h"
#include "test/Size.h"
#include "test/Window.h"

#ifdef __cplusplus
extern "C" {
#endif // __cplusplus

typedef djinni_interface_ref test_test_Window_ref;

test_test_Window_ref test_test_Window_create();

void test_test_Window_show(test_test_Window_ref instance);

void test_test_Window_hide(test_test_Window_ref instance);

void test_test_Window_setSize(test_test_Window_ref instance, test_test_Size_ref size);

void test_test_Window_setTitle(test_test_Window_ref instance, djinni_string_ref title);

#ifdef __cplusplus
} // extern "C"
#endif // __cplusplus

#ifdef __cplusplus
#include <utility>
namespace snap::test::c_wrappers {

class Window {
public:

    Window(const test_test_Window_ref& ref) : _ref(ref) { djinni_ref_retain(_ref); }
    Window(test_test_Window_ref&& ref) noexcept : _ref(std::move(ref)) {}
    Window(const Window& other) : _ref(other._ref) { djinni_ref_retain(_ref); }
    Window(Window&& other) : _ref(std::move(other._ref)) { other._ref = nullptr; }
    ~Window() { djinni_ref_release(_ref); }
    Window& operator=(const Window& other) {
      if (&other != this) {
          auto old = _ref;
          _ref = other._ref;
          djinni_ref_retain(_ref);
          djinni_ref_release(old);
       }
       return *this;
    }
    Window& operator=(Window&& other) noexcept {
      if (&other != this) {
        auto old = _ref;
        _ref = other._ref;
        other._ref = nullptr;
    
        djinni_ref_release(old);
      }
      return *this;
    }
    Window& operator=(test_test_Window_ref&& ref) {
      auto old = _ref;
      _ref = ref;
      djinni_ref_release(old);
      return *this;
    }
    Window& operator=(const test_test_Window_ref& ref) {
      auto old = _ref;
      _ref = ref;
      djinni_ref_retain(ref);
      djinni_ref_release(old);
      return *this;
    }

    operator const test_test_Window_ref&() const { return _ref; }
    const test_test_Window_ref& _djinni_ref() const { return _ref; }

    static test_test_Window_ref create()  {
        return test_test_Window_create();
    }

    void show()  {
        test_test_Window_show(_ref);
    }

    void hide()  {
        test_test_Window_hide(_ref);
    }

    void setSize(test_test_Size_ref size)  {
        test_test_Window_setSize(_ref, size);
    }

    void setTitle(djinni_string_ref title)  {
        test_test_Window_setTitle(_ref, title);
    }

private:
    test_test_Window_ref _ref;
};

} // namespace snap::test::c_wrappers
#endif // __cplusplus

The wrappers can then be used to simplify C API usage:

#include "djinni/cpp/djinni_c_ref.hpp"

#include "my_module/Size.h"
#include "my_module/Window.h"

#include <stdio.h>

void testFunc() {
  using namespace snap::test::c_wrappers;

  Window win = Window::create();
  Size size = win.getSize();
  printf("size is %d x %d\n", size.width(), size.height());

  size.width(size.width() + 10);
  win.setSize(size);
  win.setTitle(djinni_string_new("foo", 3));
  win.show();
}

writeEnumOptionAll(w, e, idCpp.enum, "=", enumCasePrefix)
}

wrapExternC(w, (w: IndentWriter) => {
Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This file is better to review with whitespace ignored as there are some block indentation changes

}

T get() const { return _ref; }
const T& get() const { return _ref; }
Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Made this const ref to ensure correct constructor overload is called when doing things like

Ref<djinni_ref> myRef = ....
WrapperClass myWrapper(myRef.get());

The lvalue constructor should be used to ensure the ref count is appropriately incremented

@li-feng-sc li-feng-sc merged commit 644b1ff into Snapchat:experimental-record Dec 9, 2025
1 check passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants