# Two ways to get C++ interface class from C API

**URL:** <https://discourse.julialang.org/t/two-ways-to-get-c-interface-class-from-c-api/112522>\
**Category:** General Usage\
**Tags:** cxx, c, cxxwrap\
**Created:** [April 4, 2024, 1:41pm UTC](https://discourse.julialang.org/t/two-ways-to-get-c-interface-class-from-c-api/112522 "2024-04-04T13:41:09Z")\
**Posts on this page:** 1\
**Page:** 1

<div class="post-metadata">

**Author:** ![sairus7](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/sairus7/32/10816_2.png) [@sairus7](https://discourse.julialang.org/u/sairus7)\
**Post date:** [April 4, 2024, 1:41pm UTC](https://discourse.julialang.org/t/two-ways-to-get-c-interface-class-from-c-api/112522/1 "2024-04-04T13:41:09Z")

</div>

Consider simple C++ interface class:

> **File \`shape.h\`**
>
> ```cpp
> // C++ Interface Class
> class Shape {
> public:
> virtual double area() const = 0;
> virtual double perimeter() const = 0;
> virtual ~Shape() {}
> };
> 
> // Concrete implementations of the interface
> class Circle : public Shape {
> private:
> double radius;
> public:
> Circle(double r) : radius(r) {}
> double area() const override { return 3.14 * radius * radius; }
> double perimeter() const override { return 2 * 3.14 * radius; }
> };
> 
> class Square : public Shape {
> private:
> double side;
> public:
> Square(double s) : side(s) {}
> double area() const override { return side * side; }
> double perimeter() const override { return 4 * side; }
> };
> 
> ```

Since `Cxx.jl` in obsolete, so you cannot generate julia types from C++, and `CxxWrap.jl` means you have to make changes on C++ side, so I’ve made two C library wrappers for this C++ interface:

### 1) Opaque pointer with every method exported

> **File \`shapes\_lib1.cpp\`:**
>
> ```cpp
> #include "shapes.h"
> 
> // C API for Shape interface
> extern "C" {
> typedef struct Shape* ShapeHandle;
> 
> ShapeHandle Shape_create_circle(double radius) {
> return reinterpret_cast<ShapeHandle>(new Circle(radius));
> }
> ShapeHandle Shape_create_square(double side) {
> return reinterpret_cast<ShapeHandle>(new Square(side));
> }
> 
> double Shape_area(ShapeHandle handle) {
> return reinterpret_cast<Shape*>(handle)->area();
> }
> 
> double Shape_perimeter(ShapeHandle handle) {
> return reinterpret_cast<Shape*>(handle)->perimeter();
> }
> 
> void Shape_destroy(ShapeHandle handle) {
> delete reinterpret_cast<Shape*>(handle);
> }
> }
> 
> ```

Julia usage example

> **File \`shapes\_lib1.jl\`**
>
> ```julia
> using Libdl
> 
> lib1_path = abspath("build/libshapes_lib1.dll")
> lib1 = Libdl.dlopen(lib1_path, Libdl.RTLD_GLOBAL)
> 
> # cr = ccall((:Shape_create_circle, lib1_path), Ptr{Cvoid}, (Cdouble,), 10) - the same
> cr = @ccall lib1_path.Shape_create_circle(5::Cdouble)::Ptr{Cvoid}
> sq = @ccall lib1_path.Shape_create_square(5::Cdouble)::Ptr{Cvoid}
> 
> @ccall lib1_path.Shape_area(cr::Ptr{Cvoid})::Cdouble
> @ccall lib1_path.Shape_area(sq::Ptr{Cvoid})::Cdouble
> 
> @ccall lib1_path.Shape_perimeter(cr::Ptr{Cvoid})::Cdouble
> @ccall lib1_path.Shape_perimeter(sq::Ptr{Cvoid})::Cdouble
> 
> @ccall lib1_path.Shape_destroy(cr::Ptr{Cvoid})::Cvoid
> @ccall lib1_path.Shape_destroy(sq::Ptr{Cvoid})::Cvoid
> 
> Libdl.dlclose(lib1)
> 
> ```

### 2) Convert C++ interface into a C struct with instance and methods pointers and export its constructors

> **File \`shapes\_lib2.cpp\`**
>
> ```cpp
> #include "shapes.h"
> 
> // C API for Shape interface
> extern "C" {
> typedef struct {
> const void* instance;
> double (*area)(const void*);
> double (*perimeter)(const void*);
> void (*destroy)(const void*);
> } ShapeHandle;
> 
> ShapeHandle Shape_create_circle(double radius) {
> Circle* circle = new Circle(radius);
> ShapeHandle handle;
> handle.instance = circle;
> handle.area = [](const void* inst) -> double {
> return static_cast<const Circle*>(inst)->area();
> };
> handle.perimeter = [](const void* inst) -> double {
> return static_cast<const Circle*>(inst)->perimeter();
> };
> handle.destroy = [](const void* inst) {
> delete static_cast<const Circle*>(inst);
> };
> return handle;
> }
> 
> ShapeHandle Shape_create_square(double side) {
> Square* square = new Square(side);
> ShapeHandle handle;
> handle.instance = square;
> handle.area = [](const void* inst) -> double {
> return static_cast<const Square*>(inst)->area();
> };
> handle.perimeter = [](const void* inst) -> double {
> return static_cast<const Square*>(inst)->perimeter();
> };
> handle.destroy = [](const void* inst) {
> delete static_cast<const Square*>(inst);
> };
> return handle;
> }
> 
> }
> 
> ```

Julia usage example:

> **File \`shapes\_lib2.jl\`**
>
> ```julia
> using Libdl
> 
> lib2_path = abspath("build/libshapes_lib2.dll")
> 
> lib2 = Libdl.dlopen(lib2_path, Libdl.RTLD_GLOBAL) # <4>
> 
> mutable struct ShapeHandle
> instance::Ptr{Cvoid}
> area::Ptr{Cvoid}
> perimeter::Ptr{Cvoid}
> destroy::Ptr{Cvoid}
> end
> 
> cr = @ccall lib2_path.Shape_create_circle(5::Cdouble)::ShapeHandle
> sq = @ccall lib2_path.Shape_create_square(5::Cdouble)::ShapeHandle
> 
> @ccall $(cr.area)(cr.instance::Ptr{Cvoid})::Cdouble
> @ccall $(sq.area)(sq.instance::Ptr{Cvoid})::Cdouble
> 
> @ccall $(cr.perimeter)(cr.instance::Ptr{Cvoid})::Cdouble
> @ccall $(sq.perimeter)(sq.instance::Ptr{Cvoid})::Cdouble
> 
> @ccall $(cr.destroy)(cr.instance::Ptr{Cvoid})::Cdouble
> @ccall $(sq.destroy)(sq.instance::Ptr{Cvoid})::Cdouble
> 
> # @ccall $(unsafe_load(c_ptr).perimeter)(unsafe_load(c_ptr).instance::Ptr{Cvoid})::Cdouble
> 
> Libdl.dlclose(lib2)
> 
> ```

First option looks more compact, but I need to call every method from a shared library.  
Second option looks more verbose - need to copy interface into a handle structure, but then I just get the interface structure and then work directly with function pointers (possibly to a concrete class).

Are there any other considerations to choose one or another?
