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         import std.traits: functionAttributes, variadicFunctionStyle, Variadic;
72 
73         enum shim = Replace!(q{
74             alias __pyd_p$i = Params[$i];
75             $override ReturnType!(__pyd_p$i.func_t) $realname(ParameterTypeTuple!(__pyd_p$i.func_t) t) $attrs {
76                 return __pyd_get_overload!("$realname", __pyd_p$i.func_t).func!(ParameterTypeTuple!(__pyd_p$i.func_t))("$name", t);
77             }
78             alias T.$realname $realname;
79         },
80             "$i", i, "$realname", realname, "$name", name,
81             "$attrs", attrs_to_string(functionAttributes!func_t) ~ " " ~ tattrs_to_string!func_t(),
82             "$override",
83             //TODO: figure out what's going on here
84             (variadicFunctionStyle!func == Variadic.no ? "override": ""));
85     }
86 }
87 
88 
89 private string attrs_to_string(uint attrs) {
90     import std.traits: FunctionAttribute;
91     import std.compiler: version_major, version_minor;
92 
93     string s = "";
94     with(FunctionAttribute) {
95         if(attrs & pure_) s ~= " pure";
96         if(attrs & nothrow_) s ~= " nothrow";
97         if(attrs & ref_) s ~= " ref";
98         if(attrs & property) s ~= " @property";
99         if(attrs & trusted) s ~= " @trusted";
100         if(attrs & safe) s ~= " @safe";
101         if(attrs & nogc) s ~= " @nogc";
102         static if(version_major == 2 && version_minor >= 67) {
103             if(attrs & return_) s ~= " return";
104         }
105     }
106     return s;
107 }
108 
109 
110 private string tattrs_to_string(fn_t)() {
111     string s;
112     if(isConstFunction!fn_t) {
113         s ~= " const";
114     }
115     if(isImmutableFunction!fn_t) {
116         s ~= " immutable";
117     }
118     if(isSharedFunction!fn_t) {
119         s ~= " shared";
120     }
121     if(isWildcardFunction!fn_t) {
122         s ~= " inout";
123     }
124     return s;
125 }
126 
127 private template isImmutableFunction(T...) if (T.length == 1) {
128     alias funcTarget!T func_t;
129     enum isImmutableFunction = is(func_t == immutable);
130 }
131 private template isConstFunction(T...) if (T.length == 1) {
132     alias funcTarget!T func_t;
133     enum isConstFunction = is(func_t == const);
134 }
135 private template isMutableFunction(T...) if (T.length == 1) {
136     alias funcTarget!T func_t;
137     enum isMutableFunction = !is(func_t == inout) && !is(func_t == const) && !is(func_t == immutable);
138 }
139 private template isWildcardFunction(T...) if (T.length == 1) {
140     alias funcTarget!T func_t;
141     enum isWildcardFunction = is(func_t == inout);
142 }
143 private template isSharedFunction(T...) if (T.length == 1) {
144     alias funcTarget!T func_t;
145     enum isSharedFunction = is(func_t == shared);
146 }
147 
148 private template funcTarget(T...) if(T.length == 1) {
149     import std.traits;
150     static if(isPointer!(T[0]) && is(PointerTarget!(T[0]) == function)) {
151         alias PointerTarget!(T[0]) funcTarget;
152     }else static if(is(T[0] == function)) {
153         alias T[0] funcTarget;
154     }else static if(is(T[0] == delegate)) {
155         alias PointerTarget!(typeof((T[0]).init.funcptr)) funcTarget;
156     }else static assert(false);
157 }