Skip to content

Instantly share code, notes, and snippets.

@DanielKeep
Created July 4, 2009 09:03
Show Gist options
  • Save DanielKeep/140507 to your computer and use it in GitHub Desktop.
Save DanielKeep/140507 to your computer and use it in GitHub Desktop.
/**
* 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