Affix cannot directly instantiate a C++ class or read a C struct unless we define every single field with the correct type and in the proper order. Often, we don't care about the fields, we just want to hold onto the object and pass it back to the library later. In this case, we can treat the object as a black box, an opaque pointer (Pointer[Void]) and use helper functions to interact with it.

The Recipe

We will create a simple C++ Person struct, wrap it with C-compatible helpers, and bind it to Perl.

use v5.40;
use Affix qw[:all];
use Affix::Build;

# 1. Compile the C++ Library
my $c = Affix::Build->new();
$c->add( \<<~'END', lang => 'cpp' );
    #include <stdlib.h>
    #include <string.h>

    typedef struct {
        char * name;
        unsigned int age;
    } Person;

    // extern "C" prevents C++ name mangling. This ensures the symbols in the
    // DLL are just "person_new", not "_Z10person_newPKcj".
    extern "C" {
        Person * person_new(const char * name, unsigned int age) {
            Person * self = (Person *) malloc(sizeof(Person));
            self->name = strdup(name);
            self->age = age;
            return self;
        }

        const char * person_name(Person * self) {
            return self->name;
        }

        unsigned int person_age(Person * self) {
            return self->age;
        }

        void person_free(Person * self) {
            if (self) {
                free(self->name);
                free(self);
            }
        }
    }
END
my $lib = $c->link;

# 2. Define the Opaque Type
# We tell Affix: "Whenever you see 'Person', treat it as a void pointer."
typedef Person => Pointer [Void];

# 3. Bind
# Notice we use Person() as the type.
affix $lib, 'person_new',  [ String, UInt ] => Person();
affix $lib, 'person_name', [ Person() ]     => String;
affix $lib, 'person_age',  [ Person() ]     => UInt;
affix $lib, 'person_free', [ Person() ]     => Void;

# 4. Use
my $roger = person_new( 'Roger Frooble Bits', 35 );

# $roger is just a blessed scalar holding a memory address.
# We pass it back to C to get data.
say 'Name: ' . person_name($roger);
say 'Age:  ' . person_age($roger);

# Clean up
person_free($roger);

How It Works

Kitchen Reminders