Opaque handles...
Adam D. Ruppe
destructionator at gmail.com
Wed Sep 18 09:57:24 PDT 2013
On Wednesday, 18 September 2013 at 16:05:00 UTC, Manu wrote:
> This seems okay at first, but then you realise there are
> serious problems with new (wants to return new memory, I can't
> hook the new operator?),
There is an override for new, but it is deprecated in d2.
http://dlang.org/memory.html#newdelete
> I kinda hate this. the struct keyword totally gives the user
> the wrong impression, and people have a natural instinct to
> avoid passing structs around by value
...just don't do that? You don't write "ref Class obj" in D, so
just pretend the struct is a class and do it the same way.
The only time you'd even need to know it is a struct is if you
look at the source, and there you can write
struct /* but pretend it is a class! */ whatever {
}
Here's how I might do it. Given this simple C test header:
struct Test;
struct Test* makeTest(int num);
void addTestRef(struct Test* t);
void killTest(struct Test* t); // i should have called that
releaseref lol
int getNumber(struct Test* t);
void setNumber(struct Test* t, int n);
We might use it in D like this:
// this represents the struct Test in C, our opaque pointer
// (the disabled stuff is to workaround a minor dmd bug)
struct c_Test {
@disable this();
@disable this(this);
}
// and this is our D wrapper
// always pass this by value
struct Test {
// the internal reference
private c_Test* c_ptr;
// construction can only be done publicly through the static
make method
@disable this();
private this(c_Test* ptr) {
c_ptr = ptr;
}
public static Test make(int num) {
return Test(makeTest(num));
}
// the alias this lets us call the rest of the binded C
functions with UFCS without manually writing wrappers
c_Test* getCPointer() { return c_ptr; } // it does a property so
you can't assign to the internal pointer
// you CAN break the refcounting with this, but since you have
to specifically ask for the uglier c_Test* to trigger
// this, it is unlikely to happen by accident.
alias getCPointer this;
// refcount stuff
~this() {
c_ptr.killTest();
}
this(this) {
c_ptr.addTestRef();
}
}
// you might notice the above is pretty generic, perhaps it could
all be a single template so you don't rewrite it for too many
types.
// and the C function prototypes
extern(C) {
c_Test* makeTest(int num);
void addTestRef(c_Test* t);
void killTest(c_Test* t);
int getNumber(c_Test* t);
void setNumber(c_Test* t, int n);
}
// test program
import core.stdc.stdio;
void foo(Test t) {
printf("foo on %d\n", t.getNumber());
t.setNumber(20);
}
void main() {
auto t = Test.make(12);
auto t2 = Test.make(24);
printf("about to call foo\n");
foo(t);
printf("done calling foo\n");
printf("main with t == %d\n", t.getNumber());
printf("main with t2 == %d\n", t2.getNumber());
}
You don't have my c implementation but it isn't special and
running the program produced the following output:
Test made with number 12
Test made with number 24
about to call foo
aref refcount now 2 on Test 12 # refcount increased properly for
the function call
foo on 12
dtor refcount now 1 on Test 20 # and properly decreased when
ending
done calling foo
main with t == 20 # setting the number in foo() correctly did it
by reference
main with t2 == 24
dtor refcount now 0 on Test 24 # and main is dead, so we release
the ref automatically
Test killed with number 24 # refcount == 0, so C free()'d it
dtor refcount now 0 on Test 20
Test killed with number 20
Using UFCS for the C functions might be a little bit weird, but
saves tediously writing them all out again to forward inside the
struct.
More information about the Digitalmars-d
mailing list