On Wednesday, 18 September 2013 at 16:05:00 UTC, Manu wrote:There is an override for new, but it is deprecated in d2.
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?),
http://dlang.org/memory.html#newdelete...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.
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
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.