In previous chapters, we bypassed C++ name mangling and wrapper shims by manually ripping the Virtual Method Table (vtable) out of a C++ object and calling the function pointers directly.

However, we had to perform a chore: because C++ methods always take a hidden this pointer as their first argument, we had to manually insert Pointer[Void] at the beginning of every single signature we wrapped. When dealing with complex signatures or callbacks, this boilerplate gets messy.

Affix provides a semantic shortcut for this exact scenario: the ThisCall modifier.

The Recipe

We will compile a C++ class, extract its vtable, and use ThisCall to dynamically generate the correct method wrapper.

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

my $c = Affix::Build->new();
$c->add( \<<~'CPP', lang => 'cpp' );
    #include <iostream>

    class Greeter {
    public:
        virtual ~Greeter() {}

        virtual void greet(const char* name) {
            std::cout << "C++ says: Hello, " << name << "!" << std::endl;
        }
    };

    // A standard C shim just to give us the object instance
    extern "C" Greeter* new_greeter() {
        return new Greeter();
    }
CPP

my $lib = $c->link;

# 1. Get the object instance
affix $lib, 'new_greeter', [] => Pointer[Void];
my $obj = new_greeter();

# 2. Extract the VTable (Assuming Itanium ABI: gcc/clang)
# The first Size_t sized slot in the object is the pointer to the vtable.
my $vptr        = cast( $obj, Pointer[Size_t] );
my $vtable_addr = $$vptr;

# Read three function-pointer slots out of the vtable.
# Slots 0 and 1 are usually the destructors; slot 2 is our virtual `greet`.
my $vtable     = cast( $vtable_addr, Array[ Size_t, 3 ] );
my $greet_addr = $vtable->[2];

# 3. Bind the method
# The C++ method still receives the hidden `this` pointer as its first argument,
# so the signature leads with `*void` — but we no longer build a `Pointer[Void]`
# type object by hand, the signature string handles it.
my $greet = wrap( undef, $greet_addr, '(*void,*char)->void' );

# 4. Execute
# We still pass the object instance ($obj) as the first argument.
$greet->( $obj, "Affix Developer" );

Which prints:

C++ says: Hello, Affix Developer!

How It Works

Kitchen Reminders