Trick for passing void* arrays around with typing
Bart
Bart at gmail.com
Fri Jun 21 15:41:50 UTC 2019
foo(cast(void*)[Object1, Object2]);
foo(cast(bool delegate(void*))(Tuple!(X,Y) objects)) { });
One can pass arbitrary data as an array(the cast is ugly though)
Then the tuple is cast back to void* but one can access objects
correctly with typing.
This works well when interacting with C/C++ void* data passing
but can also work with passing heterogeneous objects in general.
import std.stdio, std.typecons;
class X { int x = 1; }
class Y { int y = 2; }
// a ** is required for the cast.
void foo(void** data)
{
auto dd = cast(Tuple!(X,Y)*)(data);
auto d = *dd;
writeln(d[0].x);
writeln(d[1].y);
}
// This would be a C callback that is hidden from us but the
callback isn't.
alias Q = void delegate(void*);
void bar(Q d, void* x)
{
d(x);
}
void main()
{
X x = new X;
Y y = new Y;
foo(cast(void**)[x, y]);
bar(cast(Q)(Tuple!(X,Y)* dd)
{
auto d = *dd;
writeln("> ", d[0].x);
writeln("> ", d[1].y);
}, cast(void*)[x, y]);
}
What would be nice is if one didn't actually have to jump through
all the hoops:
void foo(void* as Tuple!(X,Y) d) // Redundant here but only
because we have access to foo
{
writeln(d[0].x);
writeln(d[1].y);
}
// This would be a C callback that is hidden from us but the
callback isn't.
alias Q = void delegate(void*);
void bar(Q d, void* x)
{
d(x);
}
void main()
{
X x = new X;
Y y = new Y;
foo([x, y]);
bar((as Tuple!(X,Y) d)
{
writeln("> ", d[0].x);
writeln("> ", d[1].y);
}, as [x, y]);
}
here I'm using as to signify a sort of implicit cast.
The main thing to note is that for the callback, D gets the type
as it is defined even though the cast changes the overall
delegate to what it should be:
bar(cast(Q)(Tuple!(X,Y)* dd)
{
auto d = *dd;
writeln("> ", d[0].x);
writeln("> ", d[1].y);
}, cast(void*)[x, y]);
}
I don't actually use * in my code I think it is necessary to get
the right values due to how they are passed. I think it has to do
with register vs stack passing. So it might be fragile
What I have is
bar(cast(Q)(Tuple!(X,Y) d)
{
writeln("> ", d[0].x);
writeln("> ", d[1].y);
}, cast(void*)[x, y]);
}
which works, at least for x64.
The cool thing is the "casting" all happens internally and so
there is no direct need for casting. Obviously though there is no
real casting going on(one could swap x and y in the argument) and
so it is dangerous... but D could make it 100% type safe with the
appropriate syntax. (only for delegates and callbacks) although
maybe one could use it for normal functions too:
foo([x,y] as Tuple!(X,Y))
e.g., normally we would have to do
bar((dd)
{
auto d = cast(Tuple!(X,Y))dd; // Doesn't work though
because tuple is not a reference, so must use the double pointer
hacks.
writeln("> ", d[0].x);
writeln("> ", d[1].y);
}, cast(void*)[x, y]);
}
When working with quite a number of C callbacks and passing data,
this is a nice pattern to use to avoid the ugly temp variables
and casting.
More information about the Digitalmars-d-learn
mailing list