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