1 /**
2    Reimplementations of pyd's class_wrap module
3  */
4 module autowrap.python.pyd.class_wrap;
5 
6 
7 /**
8 Wraps a member function of the class.
9 
10 Supports default arguments, typesafe variadic arguments, and python's
11 keyword arguments.
12 
13 Params:
14 fn = The member function to wrap.
15 Options = Optional parameters. Takes Docstring!(docstring), PyName!(pyname),
16 and fn_t.
17 fn_t = The type of the function. It is only useful to specify this
18        if more than one function has the same name as this one.
19 pyname = The name of the function as it will appear in Python. Defaults to
20 fn's name in D
21 docstring = The function's docstring. Defaults to "".
22 */
23 struct MemberFunction(alias fn, Options...) {
24     import pyd.def: Args;
25 
26     alias args = Args!("", "", __traits(identifier, fn), "", Options);
27 
28     static if(args.rem.length) {
29         alias fn_t = args.rem[0];
30     } else {
31         alias fn_t = typeof(&fn);
32     }
33 
34     mixin MemberFunctionImpl!(fn, args.pyname, fn_t, args.docstring);
35 }
36 
37 private template MemberFunctionImpl(alias _fn, string name, fn_t, string docstring) {
38     import pyd.class_wrap: wrapped_method_list;
39     import pyd.references: PydTypeObject;
40     import pyd.def: def_selector;
41     import pyd.func_wrap: minArgs, method_wrap;
42     import util.typeinfo: ApplyConstness, constness;
43     import deimos.python.methodobject: PyMethodDef, PyCFunction, METH_VARARGS, METH_KEYWORDS;
44 
45     alias func = def_selector!(_fn, fn_t).FN;
46     static assert(!__traits(isStaticFunction, func),
47                   "Cannot register " ~ name ~ " because static member functions are not yet supported");
48     alias /*StripSafeTrusted!*/fn_t func_t;
49     enum realname = __traits(identifier,func);
50     enum funcname = name;
51     enum min_args = minArgs!func;
52     enum bool needs_shim = false; // needed for the compile-time interface
53 
54     static void call(string classname, T) () { // needed for the compile-time interface
55         alias cT = ApplyConstness!(T, constness!(typeof(func)));
56         static PyMethodDef empty = { null, null, 0, null };
57         alias list = wrapped_method_list!(T);
58 
59         list[$ - 1].ml_name = (name ~ "\0").ptr;
60         list[$ - 1].ml_meth = cast(PyCFunction) &method_wrap!(cT, func, classname ~ "." ~ name).func;
61         list[$ - 1].ml_flags = METH_VARARGS | METH_KEYWORDS;
62         list[$ - 1].ml_doc = (docstring~"\0").ptr;
63         list ~= empty;
64         // It's possible that appending the empty item invalidated the
65         // pointer in the type struct, so we renew it here.
66         PydTypeObject!T.tp_methods = list.ptr;
67     }
68 
69     template shim(size_t i, T) {
70         import util.replace: Replace;
71         enum shim = Replace!(q{
72             alias __pyd_p$i = Params[$i];
73             $override ReturnType!(__pyd_p$i.func_t) $realname(ParameterTypeTuple!(__pyd_p$i.func_t) t) $attrs {
74                 return __pyd_get_overload!("$realname", __pyd_p$i.func_t).func!(ParameterTypeTuple!(__pyd_p$i.func_t))("$name", t);
75             }
76             alias T.$realname $realname;
77         },
78             "$i", i, "$realname", realname, "$name", name,
79             "$attrs", attrs_to_string(functionAttributes!func_t) ~ " " ~ tattrs_to_string!func_t(),
80             "$override",
81             //TODO: figure out what's going on here
82             (variadicFunctionStyle!func == Variadic.no ? "override": ""));
83     }
84 }