1 module pybuffer;
2 
3 import std.stdio;
4 import mir.ndslice;
5 
6 
7 struct pybuffer {}
8 
9 mixin template MixinPyBufferWrappers(string _Impl = __MODULE__) {
10     private enum string _generated = {
11         mixin("import Impl = " ~ _Impl ~ ";");
12         import std.conv : to;
13         import std.traits : Parameters, ReturnType;
14         string ret;
15         foreach (mem; __traits(allMembers, Impl)) {
16             foreach (attr; __traits(getAttributes, __traits(getMember, Impl, mem))) {
17                 if (is(attr == pybuffer)) {
18                     pragma(msg, mem);
19                     mixin("alias R = ReturnType!(" ~ _Impl ~ "." ~ mem ~ ");");
20                     static assert(is(R == void), "@pybuffer function should return void");
21                     string args;
22                     string rargs;
23                     string converts = "  import mir.ndslice.connect.cpython;\n";
24                     converts ~= "  import std.stdio : writeln;\n";
25 
26                     mixin("alias Ps = Parameters!(" ~ _Impl ~ "." ~ mem ~ ");");
27                     foreach (i, P; Ps) {
28                         pragma(msg, P.stringof);
29                         enum a = "a" ~ i.to!string;
30                         if (isSlice!P) {
31                             args ~= " " ~ "ref Py_buffer " ~ a ~ " ,";
32                             enum _a = "_a" ~ i.to!string;
33                             converts ~= "  " ~ P.stringof ~ " " ~ _a ~ ";\n";
34                             converts ~= "  {\n    auto err = fromPythonBuffer( " ~ _a ~ " , " ~ a ~ " );\n";
35                             // TODO: enrich error messages
36                             converts ~= "    if (err != PythonBufferErrorCode.success) { writeln(err, \" at param " ~ i.to!string ~ " \", a" ~ i.to!string ~ "); return err; }\n  }\n";
37                             rargs ~= " " ~ _a ~ " ,";
38                         } else {
39                             args ~= " " ~ P.stringof ~ " " ~ a ~ " ,";
40                             rargs ~= " " ~ a ~ " ,";
41                         }
42                     }
43 
44                     // decleration
45                     enum newName = "pybuffer_" ~ mem;
46                     // workaround to https://issues.dlang.org/show_bug.cgi?id=12575
47                     ret ~= "pragma(mangle, __traits(identifier, " ~ newName ~ "))\n";
48                     ret ~= "extern(C) auto " ~ newName ~ "(" ~ args[0..$-1] ~ ") {\n";
49 
50                     // conversions: pybuf -> ndslice
51                     ret ~= converts;
52 
53                     // exec function
54                     ret ~= "  " ~ _Impl ~ "." ~ mem ~ "(" ~ rargs[0..$-1] ~ ");\n";
55 
56                     // return success
57                     ret ~= "  return PythonBufferErrorCode.success;\n}\n\n";
58                 }
59             }
60         }
61         return ret;
62     }();
63 
64     pragma(mangle, __traits(identifier, print_generated))
65     extern(C) void print_generated() {
66         write(_generated);
67     }
68 
69     mixin(_generated);
70 }