Created
July 4, 2009 09:03
-
-
Save DanielKeep/140507 to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/** | |
* Converts a function pointer to a delegate pointer. | |
* | |
* Copyright: © 2009, Daniel Keep. | |
* | |
* Permission is hereby granted, free of charge, to any person obtaining a copy | |
* of this software and associated documentation files (the "Software"), to deal | |
* in the Software without restriction, including without limitation the rights | |
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell | |
* copies of the Software, and to permit persons to whom the Software is | |
* furnished to do so, subject to the following conditions: | |
* | |
* The above copyright notice and this permission notice shall be included in | |
* all copies or substantial portions of the Software. | |
* | |
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR | |
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, | |
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE | |
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER | |
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, | |
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN | |
* THE SOFTWARE. | |
*/ | |
module util.meta.todg; | |
import tango.core.Traits; | |
private template ToDgType(Fn) | |
{ | |
mixin("alias ReturnTypeOf!(Fn) delegate"~ParameterTupleOf!(Fn).stringof | |
~" ToDgType;"); | |
} | |
static assert( is( | |
ToDgType!(long function(byte, ref short, out int)) | |
== long delegate(byte, ref short, out int) ) ); | |
private char[] toString_ct(int v) | |
{ | |
if( v == 0 ) | |
return "0"; | |
if( v < 0 ) | |
return "-" ~ toString_ct(-v); | |
char[] r; | |
while( v > 0 ) | |
{ | |
r = "0123456789"[v%10] ~ r; | |
v /= 10; | |
} | |
return r; | |
} | |
static assert( toString_ct(0) == "0" ); | |
static assert( toString_ct(1) == "1" ); | |
static assert( toString_ct(9) == "9" ); | |
static assert( toString_ct(10) == "10" ); | |
static assert( toString_ct(19) == "19" ); | |
static assert( toString_ct(-1) == "-1" ); | |
static assert( toString_ct(-9) == "-9" ); | |
static assert( toString_ct(-10) == "-10" ); | |
private char[] toArgList_ct(char[] args) | |
{ | |
// Strip off parens | |
if( args[0] == '(' && args[$-1] == ')' ) | |
return "("~toArgList_ct(args[1..$-1])~")"; | |
// The no-argument case is easy :D | |
if( args == "" ) | |
return ""; | |
// We need to translate the type list into an argument list. To do this, | |
// we will scan the string for commas. If we see an | |
// opening paren, we will ignore any commas until we find the matching | |
// closing paren. | |
// | |
// When we find a comma, we'll insert "an" before it, | |
// where n is the index of the argument. | |
// | |
// The reason we can't use tuples is that tuples can't have ref, out, | |
// scope, etc. in the type list. | |
// | |
// Lastly, when we run out of string to process, we append the last | |
// argument name. | |
int depth = 0; // how deep in parens we are | |
int argord = 0; // argument ordinal | |
char[] result; | |
foreach( c ; args ) | |
{ | |
if( depth == 0 ) | |
{ | |
if( c == '(' ) | |
++ depth; | |
else if( c == ',' ) | |
{ | |
result ~= " a"~toString_ct(argord); | |
++ argord; | |
} | |
} | |
else | |
{ | |
if( c == '(' ) | |
++ depth; | |
else if( c == ')' ) | |
-- depth; | |
} | |
result ~= c; | |
} | |
return result~" a"~toString_ct(argord); | |
} | |
static assert( toArgList_ct("(byte, ref short, out int, scope float)") | |
== "(byte a0, ref short a1, out int a2, scope float a3)" ); | |
private template ToArgList(Fn) | |
{ | |
const ToArgList = toArgList_ct(ParameterTupleOf!(Fn).stringof); | |
} | |
private char[] toArgNameList_ct(int args) | |
{ | |
char[] result; | |
for( int i=0; i<args; ++i ) | |
{ | |
if( i > 0 ) result ~= ", "; | |
result ~= "a" ~ toString_ct(i); | |
} | |
return "(" ~ result ~ ")"; | |
} | |
static assert( toArgNameList_ct(0) == "()" ); | |
static assert( toArgNameList_ct(1) == "(a0)" ); | |
static assert( toArgNameList_ct(2) == "(a0, a1)" ); | |
private template ToArgNameList(Fn) | |
{ | |
const ToArgNameList = toArgNameList_ct(ParameterTupleOf!(Fn).length); | |
} | |
// TODO: call shouldn't return void; it should return the same type as Fn. | |
private struct WrapFn(Fn) | |
{ | |
debug | |
{ | |
Fn ptr; | |
private const impl = ` | |
` ~ ReturnTypeOf!(Fn).stringof ~ ` call` ~ ToArgList!(Fn) ~ ` | |
{ | |
return ptr` ~ ToArgNameList!(Fn) ~ `; | |
} | |
`; | |
} | |
else | |
{ | |
private const impl = ` | |
` ~ ReturnTypeOf!(Fn).stringof ~ ` call` ~ ToArgList!(Fn) ~ ` | |
{ | |
return (cast(Fn)this)` ~ ToArgNameList!(Fn) ~ `; | |
} | |
`; | |
} | |
//debug(todg) pragma(msg, impl); | |
mixin(impl); | |
} | |
/** | |
* Converts a function pointer into a delegate of the corresponding type. | |
* This delegate merely forwards calls to the given function pointer. | |
* | |
* For release builds, this is done without requiring any heap allocations. | |
* Debug builds use a heap-based method to improve compatibility with | |
* debuggers. | |
*/ | |
ToDgType!(Fn) toDg(Fn)(Fn fn) | |
{ | |
debug | |
{ | |
auto wrap = new WrapFn!(Fn); | |
wrap.ptr = fn; | |
return &wrap.call; | |
} | |
else | |
{ | |
ToDgType!(Fn) dg; | |
WrapFn!(Fn) wrap; | |
dg.ptr = fn; | |
dg.funcptr = cast(Fn)(&wrap.call); | |
return dg; | |
} | |
} | |
/* | |
* This is just a simple little self-test used to ensure the conversion works. | |
* Compile this file by itself with -version=todg_selftest to run it. | |
*/ | |
version( todg_selftest ): | |
import tango.io.Stdout; | |
int foo(char[] a, out int b, ref float c) | |
{ | |
Stdout("foo got: ")(a)(", ")(b)(", ")(c).newline; | |
b = 1701; | |
c = 3.141; | |
return 42; | |
} | |
void bar(int delegate(char[], out int, ref float) dg) | |
{ | |
char[] a = "hello"; | |
int b = 7; | |
float c = 2.159; | |
int r = dg(a, b, c); | |
Stdout(r)(" = dg(")(a)(", ")(b)(", ")(c)(")").newline; | |
} | |
void main() | |
{ | |
bar(toDg(&foo)); | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment