1 /** 2 Necessary boilerplate for pyd. 3 4 To wrap all functions/return/parameter types and struct/class definitions from 5 a list of modules, write this in a "main" module and generate mylib.{so,dll}: 6 7 ------ 8 mixin wrapAll(LibraryName("mylib"), Modules("module1", "module2", ...)); 9 ------ 10 */ 11 module autowrap.python.boilerplate; 12 13 /** 14 The name of the dynamic library, i.e. the file name with the .so/.dll extension 15 */ 16 struct LibraryName { 17 string value; 18 } 19 20 21 /** 22 The list of modules to automatically wrap for Python consumption 23 */ 24 struct Modules { 25 import autowrap.reflection: Module; 26 import std.traits: Unqual; 27 import std.meta: allSatisfy; 28 29 Module[] value; 30 31 this(A...)(auto ref A modules) { 32 33 foreach(module_; modules) { 34 static if(is(Unqual!(typeof(module_)) == Module)) 35 value ~= module_; 36 else static if(is(Unqual!(typeof(module_)) == string)) 37 value ~= Module(module_); 38 else 39 static assert(false, "Modules must either be `string` or `Module`"); 40 } 41 } 42 } 43 44 45 /** 46 Code to be inserted before the call to module_init 47 */ 48 struct PreModuleInitCode { 49 string value; 50 } 51 52 /** 53 Code to be inserted after the call to module_init 54 */ 55 struct PostModuleInitCode { 56 string value; 57 } 58 59 60 /** 61 A string to be mixed in that defines all the necessary runtime pyd boilerplate. 62 */ 63 string wrapAll(in LibraryName libraryName, 64 in Modules modules, 65 in PreModuleInitCode preModuleInitCode = PreModuleInitCode(), 66 in PostModuleInitCode postModuleInitCode = PostModuleInitCode()) 67 @safe pure 68 { 69 if(!__ctfe) return null; 70 71 string ret = 72 pydMainMixin(modules, preModuleInitCode, postModuleInitCode) ~ 73 pydInitMixin(libraryName.value); 74 75 version(Have_excel_d) { 76 ret ~= 77 // this is needed because of the excel-d dependency 78 q{ 79 import xlld.wrap.worksheet: WorksheetFunction; 80 extern(C) WorksheetFunction[] getWorksheetFunctions() @safe pure nothrow { return []; } 81 }; 82 } else version(Windows) { 83 ret ~= dllMainMixinStr; 84 } 85 86 return ret; 87 } 88 89 /** 90 A string to be mixed in that defines PydMain, automatically registering all 91 functions and structs in the passed in modules. 92 */ 93 string pydMainMixin(in Modules modules, 94 in PreModuleInitCode preModuleInitCode = PreModuleInitCode(), 95 in PostModuleInitCode postModuleInitCode = PostModuleInitCode()) 96 @safe pure 97 { 98 import std.format: format; 99 import std.algorithm: map; 100 import std.array: join; 101 102 if(!__ctfe) return null; 103 104 const modulesList = modules.value.map!(a => a.toString).join(", "); 105 106 return q{ 107 extern(C) void PydMain() { 108 import std.typecons: Yes, No; 109 import pyd.pyd: module_init; 110 import autowrap.python.wrap: wrapAllFunctions, wrapAllAggregates; 111 112 // this must go before module_init 113 wrapAllFunctions!(%s); 114 115 %s 116 117 module_init; 118 119 // this must go after module_init 120 wrapAllAggregates!(%s); 121 122 %s 123 } 124 }.format(modulesList, preModuleInitCode.value, modulesList, postModuleInitCode.value); 125 } 126 127 /** 128 A string to be mixed in that defines the PyInit function for a library. 129 */ 130 string pydInitMixin(in string libraryName) @safe pure { 131 import std.format: format; 132 133 if(!__ctfe) return null; 134 135 version(Python_3_0_Or_Later) { 136 return q{ 137 import deimos.python.object: PyObject; 138 extern(C) export PyObject* PyInit_%s() { 139 import pyd.def: pyd_module_name, pyd_modules; 140 import pyd.exception: exception_catcher; 141 import pyd.thread: ensureAttached; 142 143 return exception_catcher(delegate PyObject*() { 144 ensureAttached(); 145 pyd_module_name = "%s"; 146 PydMain(); 147 return pyd_modules[""]; 148 }); 149 } 150 }.format(libraryName, libraryName); 151 } else { 152 return q{ 153 import pyd.exception: exception_catcher; 154 import pyd.thread: ensureAttached; 155 import pyd.def: pyd_module_name; 156 extern(C) export void init%s() { 157 exception_catcher(delegate void() { 158 ensureAttached(); 159 pyd_module_name = "%s"; 160 PydMain(); 161 }); 162 163 } 164 }.format(libraryName, libraryName); 165 } 166 } 167 168 /** 169 Returns a string to be mixed in that defines DllMain, needed on Windows. 170 */ 171 private string dllMainMixinStr() @safe pure { 172 return q{ 173 174 import core.sys.windows.windows: HINSTANCE, BOOL, ULONG, LPVOID; 175 176 __gshared HINSTANCE g_hInst; 177 178 extern (Windows) BOOL DllMain(HINSTANCE hInstance, ULONG ulReason, LPVOID pvReserved) { 179 180 import core.sys.windows.windows; 181 import core.sys.windows.dll; 182 183 switch (ulReason) { 184 185 case DLL_PROCESS_ATTACH: 186 g_hInst = hInstance; 187 dll_process_attach(hInstance, true); 188 break; 189 190 case DLL_PROCESS_DETACH: 191 dll_process_detach(hInstance, true); 192 break; 193 194 case DLL_THREAD_ATTACH: 195 dll_thread_attach(true, true); 196 break; 197 198 case DLL_THREAD_DETACH: 199 dll_thread_detach(true, true); 200 break; 201 202 default: 203 } 204 205 return true; 206 } 207 }; 208 }