1 module python.boilerplate;
2 
3 import python.raw: isPython2, isPython3;
4 
5 /// For a nicer API
6 struct Module {
7     string name;
8 }
9 
10 
11 /// For a nicer API
12 struct CFunctions(functions...) {
13     alias symbols = functions;
14     enum length = functions.length;
15 
16     static string stringifySymbols() {
17         import std.array: join;
18 
19         string[] ret;
20 
21         static foreach(cfunction; symbols)
22             ret ~= __traits(identifier, cfunction);
23 
24         return ret.join(", ");
25     }
26 }
27 
28 /// A list of aggregates to wrap
29 struct Aggregates(T...) {
30     alias Types = T;
31 
32     static string stringifyTypes() {
33         import std.array: join;
34         string[] ret;
35 
36         static foreach(T; Types)
37             ret ~= T.stringof;
38 
39         return ret.join(", ");
40     }
41 }
42 
43 
44 /**
45    A string mixin to reduce boilerplate when creating a Python module.
46    Takes a module name and a variadic list of C functions to make
47    available.
48  */
49 string createModuleMixin(Module module_, alias cfunctions, alias aggregates)()
50     if(isPython3)
51 {
52     import std.format: format;
53 
54     enum ret = q{
55         import python.raw: PyDateTime_CAPI;
56         // This is declared as an extern C variable in python.bindings.
57         // We declare it here to avoid linker errors.
58         export __gshared extern(C) PyDateTime_CAPI* PyDateTimeAPI;
59 
60         import python: ModuleInitRet;
61 
62         extern(C) export ModuleInitRet PyInit_%s() {
63             import python.raw: pyDateTimeImport;
64             import python.cooked: createModule;
65             import python.boilerplate: Module, CFunctions, Aggregates;
66 
67             pyDateTimeImport;
68 
69             return createModule!(
70                 Module("%s"),
71                 CFunctions!(
72                     %s
73                 ),
74                 Aggregates!(
75                     %s
76                 )
77             );
78         }
79     }.format(module_.name, module_.name, cfunctions.stringifySymbols, aggregates.stringifyTypes);
80 
81     return ret;
82 }
83 
84 string createModuleMixin(Module module_, alias cfunctions, alias aggregates)()
85     if(isPython2)
86 {
87     import std.format: format;
88 
89     enum ret = q{
90         import python.raw: PyDateTime_CAPI;
91 
92         // This is declared as an extern C variable in python.bindings.
93         // We declare it here to avoid linker errors.
94         export __gshared extern(C) PyDateTime_CAPI* PyDateTimeAPI;
95 
96         extern(C) export void init%s() {
97             import python.raw: pyDateTimeImport;
98             import python.cooked: initModule;
99             import python.boilerplate: Module, CFunctions, Aggregates;
100 
101             pyDateTimeImport;
102             initModule!(
103                 Module("%s"),
104                 CFunctions!(
105                     %s
106                 ),
107                 Aggregates!(
108                     %s
109                 ),
110             );
111         }
112     }.format(module_.name, module_.name, cfunctions.stringifySymbols, aggregates.stringifyTypes);
113 
114     return ret;
115 }