1 /* -*- mode: C++; c-basic-offset: 2; indent-tabs-mode: nil -*- */ 2 3 /* 4 * Main authors: 5 * Jason Nguyen <jason.nguyen@monash.edu> 6 */ 7 8 /* This Source Code Form is subject to the terms of the Mozilla Public 9 * License, v. 2.0. If a copy of the MPL was not distributed with this 10 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ 11 12 #pragma once 13 14 #ifdef _WIN32 15 #define NOMINMAX // Ensure the words min/max remain available 16 #include <Windows.h> 17 #undef ERROR 18 #else 19 #include <dlfcn.h> 20 #endif 21 22 #include <minizinc/exception.hh> 23 #include <minizinc/file_utils.hh> 24 25 #include <cstdlib> 26 #include <sstream> 27 #include <string> 28 #include <vector> 29 30 /// Convenience macro for loading symbols 31 #define load_symbol(name) *(void**)(&(name)) = symbol(#name) 32 33 namespace MiniZinc { 34 /// Base class for plugins loaded from DLLs 35 class Plugin { 36 public: 37 class PluginError : public Exception { 38 public: 39 /// Construct with message \a msg PluginError(const std::string & msg)40 PluginError(const std::string& msg) : Exception(msg) {} 41 /// Destructor ~PluginError()42 ~PluginError() throw() override {} 43 /// Return description what() const44 const char* what() const throw() override { return "MiniZinc: plugin loading error"; } 45 }; 46 47 /// Load a plugin with given DLL path Plugin(const std::string & file)48 Plugin(const std::string& file) { 49 if (!open(file)) { 50 throw PluginError("Failed to load plugin " + file); 51 } 52 } 53 /// Load a plugin by trying the given DLL file paths Plugin(const std::vector<std::string> & files)54 Plugin(const std::vector<std::string>& files) { 55 for (const auto& file : files) { 56 if (open(file)) { 57 return; 58 } 59 } 60 bool first = true; 61 std::stringstream ss; 62 ss << "Failed to load plugin. Tried "; 63 for (const auto& file : files) { 64 if (first) { 65 first = false; 66 } else { 67 ss << ", "; 68 } 69 ss << file; 70 } 71 throw PluginError(ss.str()); 72 } 73 ~Plugin()74 ~Plugin() { close(); } 75 76 /// Get the path to the loaded DLL path()77 const std::string& path() { return _loaded; } 78 79 protected: 80 /// Load a symbol from this DLL symbol(const char * name)81 void* symbol(const char* name) { 82 void* ret; 83 #ifdef _WIN32 84 ret = (void*)GetProcAddress((HMODULE)_dll, (LPCSTR)name); 85 #else 86 ret = dlsym(_dll, name); 87 #endif 88 if (ret == nullptr) { 89 throw PluginError(std::string("Failed to load symbol ") + name); 90 } 91 return ret; 92 } 93 94 private: 95 void* _dll; 96 std::string _loaded; open(const std::string & file)97 bool open(const std::string& file) { 98 #ifdef _WIN32 99 const std::string ext = ".dll"; 100 #elif __APPLE__ 101 const std::string ext = ".dylib"; 102 #else 103 const std::string ext = ".so"; 104 #endif 105 bool hasExt = 106 file.size() >= ext.size() && file.compare(file.size() - ext.size(), ext.size(), ext) == 0; 107 auto path = (hasExt || MiniZinc::FileUtils::is_absolute(file)) ? file : (file + ext); 108 #ifdef _WIN32 109 auto dir = MiniZinc::FileUtils::dir_name(path); 110 if (!dir.empty()) { 111 // Add the path with the DLL to the search path for dependency loading 112 SetDllDirectoryW(MiniZinc::FileUtils::utf8_to_wide(dir).c_str()); 113 } 114 _dll = (void*)LoadLibrary((LPCSTR)path.c_str()); 115 if (!dir.empty()) { 116 SetDllDirectoryW(nullptr); 117 } 118 #else 119 _dll = dlopen(path.c_str(), RTLD_NOW); 120 #endif 121 if (_dll != nullptr) { 122 _loaded = path; 123 return true; 124 } 125 126 return false; 127 } close()128 void close() { 129 #ifdef _WIN32 130 FreeLibrary((HMODULE)_dll); 131 #else 132 dlclose(_dll); 133 #endif 134 _dll = nullptr; 135 } 136 }; 137 } // namespace MiniZinc 138