1 module janet.func;
2 
3 import janet.c;
4 
5 import janet.d;
6 
7 /**
8     A wrapper around call_janet using D-like varargs.
9 */
10 Janet callJanet(T...)(JanetFunction* fun,T args)
11 {
12     Janet[T.length] wrappedArgs;
13     static foreach(i,v;args)
14     {
15         wrappedArgs[i]=janetWrap(v);
16     }
17     return janet_call(fun,T.length,cast(const(Janet*))(wrappedArgs));
18 }
19 /**
20     Wraps around a function, allowing it to be called in Janet.
21 */
22 template makeJanetCFunc(alias func)
23 {
24     import std.traits : Parameters,ReturnType,isNestedFunction;
25     import std.typecons : Tuple;
26     extern(C) static Janet ourJanetFunc (int argc, Janet* argv)
27     {
28         Tuple!(Parameters!func) args;
29         alias funcParams = Parameters!func;
30         static foreach(i;0..args.length)
31         {
32             args[i] = getFromJanet!(funcParams[i])(argv[i]);
33         }
34         return janetWrap!(ReturnType!func)(func(args.expand));
35     }
36     JanetCFunction makeJanetCFunc()
37     {
38         return &ourJanetFunc;
39     }
40 }
41 /**
42     The same, but requires a delegate or function pointer be put in as an argument.
43     This is due to many class methods requiring context pointers.
44     This is mostly only useful for class registering.
45 */
46 template makeJanetCFunc(alias func,T,Args...)
47 {
48     import std.traits : Parameters,ReturnType,isNestedFunction;
49     import std.typecons : Tuple;
50     static T delegate(Args) dg;
51     static T function(Args) fp;
52     JanetCFunction makeJanetCFunc(T delegate(Args) argDg)
53     {
54         dg = argDg;
55         return &ourJanetFunc;
56     }
57     JanetCFunction makeJanetCFunc(T function(Args) argFp) // templates are so wonderful.
58     {
59         fp = argFp;
60         return &ourJanetFunc;
61     }
62     extern(C) static Janet ourJanetFunc (int argc, Janet* argv)
63     {
64         Tuple!(Parameters!func) args;
65         alias funcParams = Parameters!func;
66         static foreach(i;0..args.length)
67         {
68             args[i] = getFromJanet!(funcParams[i])(argv[i]);
69         }
70         if(dg)
71         {
72             return janetWrap!(ReturnType!func)(dg(args.expand));
73         }
74         else
75         {
76             return janetWrap!(ReturnType!func)(fp(args.expand));
77         }
78     }
79 }
80 
81 /**
82     Makes a function globally available with Janet.
83 */
84 void registerFunctionWithJanet(alias func,string documentation = "")()
85 {
86     import std.string : toStringz;
87     JanetReg[2] reg;
88     reg[0].name = cast(const(char)*)toStringz(__traits(identifier,func));
89     reg[0].cfun = makeJanetCFunc!func;
90     reg[0].documentation = cast(const(char)*)toStringz(documentation);
91     janet_cfuns(coreEnv,"",&reg[0]);
92 }
93 
94 version(unittest)
95 {
96     int foo(int x)
97     {
98         return x+1;
99     }
100     int bar(int y)
101     {
102         return y+2;
103     }
104 }
105 
106 unittest
107 {
108     import std.file;
109     import std.parallelism : task;
110     auto fileTask = task!read("./source/tests/dtests/function.janet");
111     fileTask.executeInNewThread();
112     initJanet();
113     scope(exit) janet_deinit();
114     import std.stdio : writeln;
115     writeln("Performing CFunction register test.");
116     registerFunctionWithJanet!foo();
117     registerFunctionWithJanet!bar();
118     Janet* j;
119     const ubyte[] file = cast(const(ubyte[]))(fileTask.spinForce);
120     const(ubyte)* realFile = cast(const(ubyte)*)file;
121     int realFileLength = cast(int)(file.length);
122     assert(janet_dobytes(coreEnv,realFile,realFileLength,
123         cast(const(char)*)("./source/tests/dtests/function.janet"),j)==0,"CFunction register test errored!");
124     writeln("Success.");
125 }