Hiding a Foo right after Impl can be a solution. However, you need to pass 't', not '&t' to the C function because

- Although it may be unexpected, cast(void*) is the specified way of getting the address of a class object

- Taking the address of a class reference (which 't' is one), is just the address of the reference itself

So, you either have to do something similar to the following or pass void* to the C function.

import std.stdio: writeln;
import core.stdc.stdlib: malloc;

struct Impl {
    ubyte[8] payload;
}

struct ImplWithOwner {
    Impl impl;
    Foo owner;
}

class Foo {
    ImplWithOwner *io;

    Impl *payload() {
    // Guaranteed by D for structs:
    assert(cast(void*)io == cast(void*)&io.impl);
        return &io.impl;
    }

    alias payload this;

    this() {
        io = cast(ImplWithOwner*) malloc(Impl.sizeof);
        io.owner = this;
    }
}

static Foo asFoo(Impl* p) {
    return (cast(ImplWithOwner*)p).owner;
}

extern(C)
void actual_C_function(Impl* data, void function(Impl*) callback) {
    data.payload[0] = 42;
    callback(data);
}

extern(C)
void myCallback(Impl* p) {
    auto foo = p.asFoo;
    assert(foo.io.impl.payload[0] == 42);
}

void main() {
    auto t = new Foo();
    actual_C_function(t, &myCallback);
}

Ali

Reply via email to