38#if !defined(_WIN32_WINNT) || (_WIN32_WINNT < 0x0600)
40#define _WIN32_WINNT 0x0600
42#if !defined(WINVER) || (WINVER < 0x0600)
46#elif !defined(_GNU_SOURCE)
81#ifdef GECODE_HAS_THREADS
93utf8_to_wide(
const std::string &s) {
95 return std::wstring();
97 int n = MultiByteToWideChar(CP_UTF8, MB_ERR_INVALID_CHARS, s.c_str(),
98 static_cast<int>(s.size()), NULL, 0);
100 throw Error(
"Blackbox",
"Invalid UTF-8 string in blackbox path or argument");
102 std::wstring w(
static_cast<size_t>(n), L
'\0');
103 if (MultiByteToWideChar(CP_UTF8, MB_ERR_INVALID_CHARS, s.c_str(),
104 static_cast<int>(s.size()), &w[0], n) == 0) {
105 throw Error(
"Blackbox",
"Invalid UTF-8 string in blackbox path or argument");
111windows_error(
const std::string &prefix, DWORD err) {
112 return prefix +
" (Windows error " + std::to_string(err) +
")";
117has_dll_suffix(
const std::string &name) {
118 if (name.size() < 4) {
121 const char *suffix =
".dll";
122 for (
size_t i = 0; i < 4; i++) {
123 if (std::tolower(
static_cast<unsigned char>(name[name.size() - 4 + i])) !=
131std::vector<std::string>
132dll_candidates(
const std::string &name) {
133 std::vector<std::string> candidates;
134 candidates.push_back(name);
135 if (!has_dll_suffix(name)) {
136 candidates.push_back(name +
".dll");
138 const size_t separator = name.find_last_of(
"\\/");
139 const std::string directory =
140 (separator == std::string::npos) ? std::string() :
141 name.substr(0, separator + 1);
142 const std::string basename =
143 (separator == std::string::npos) ? name : name.substr(separator + 1);
144 if (basename.compare(0, 3,
"lib") != 0) {
145 std::string prefixed = directory +
"lib" + basename;
146 if (!has_dll_suffix(prefixed)) {
149 candidates.push_back(prefixed);
156close_library(
void *library) {
157 if (library !=
nullptr) {
158 FreeLibrary(
static_cast<HMODULE
>(library));
163close_library(
void *library) {
164 if (library !=
nullptr) {
174library_symbol(
void *library,
const char *name,
unsigned int stdcall_bytes) {
176 FARPROC symbol = GetProcAddress(
static_cast<HMODULE
>(library), name);
177#if defined(_M_IX86) || defined(__i386__)
178 if (symbol ==
nullptr) {
179 const std::string decorated =
180 std::string(
"_") + name +
"@" + std::to_string(stdcall_bytes);
181 symbol = GetProcAddress(
static_cast<HMODULE
>(library), decorated.c_str());
183 if (symbol ==
nullptr) {
184 const std::string decorated =
185 std::string(name) +
"@" + std::to_string(stdcall_bytes);
186 symbol = GetProcAddress(
static_cast<HMODULE
>(library), decorated.c_str());
191 return reinterpret_cast<T
>(symbol);
195 *(
void **)(&symbol) = dlsym(library, name);
201check_int(int64_t v,
const char *source,
size_t i) {
203 throw Error(
"Blackbox", std::string(source) +
" integer " +
205 " is outside Gecode's integer range");
209#ifdef GECODE_HAS_FLOAT_VARS
211check_float(
double v,
const char *source,
size_t i) {
212 static_assert(
sizeof(double) ==
sizeof(std::uint64_t) &&
213 std::numeric_limits<double>::is_iec559,
214 "blackbox floats must use IEEE-754 binary64");
215 std::uint64_t bits = 0;
216 const volatile unsigned char *raw =
217 reinterpret_cast<const volatile unsigned char *
>(&v);
218 unsigned char *target =
reinterpret_cast<unsigned char *
>(&bits);
219 for (
size_t j = 0; j <
sizeof(bits); j++) {
222 if (((bits & UINT64_C(0x7ff0000000000000)) ==
223 UINT64_C(0x7ff0000000000000)) ||
225 throw Error(
"Blackbox", std::string(source) +
" float " +
227 " is not a finite value in Gecode's floating "
233check_floats(
const std::vector<double> &v,
const char *source) {
234 for (
size_t i = 0; i < v.size(); i++) {
235 check_float(v[i], source, i);
242#ifdef GECODE_HAS_THREADS
248 Instance(
const std::thread::id &owner0,
void *value0)
254 const std::vector<std::string> &args)
256 library_fzn_blackbox(nullptr), library_fzn_free(nullptr),
259 std::string loadError;
260 void *loaded =
nullptr;
262 DWORD err = ERROR_FILE_NOT_FOUND;
263 std::string failed_candidate = name;
264 for (
const std::string &candidate : dll_candidates(name)) {
265 loaded = LoadLibraryW(utf8_to_wide(candidate).c_str());
266 if (loaded !=
nullptr) {
269 failed_candidate = candidate;
270 err = GetLastError();
272 if (loaded ==
nullptr) {
273 loadError = std::string(
"unable to locate library `") + name +
"' (" +
274 windows_error(
"LoadLibraryW failed for `" + failed_candidate +
278 loaded = dlopen(name.c_str(), RTLD_LAZY);
280 loadError = std::string(dlerror());
281 loaded = dlopen((name +
".so").c_str(), RTLD_NOW);
284 loaded = dlopen((std::string(
"lib") + name +
".so").c_str(), RTLD_NOW);
288 loaded = dlopen((name +
".dylib").c_str(), RTLD_NOW);
291 loaded = dlopen((std::string(
"lib") + name +
".dylib").c_str(), RTLD_NOW);
296 throw Error(
"Blackbox",
"Unable to open dynamic library: " + loadError);
299 bool root_initialized =
false;
305 library_fzn_blackbox =
306 library_symbol<decltype(library_fzn_blackbox)>(loaded,
"fzn_blackbox",
308 std::string symError(
".");
309 if (library_fzn_blackbox ==
nullptr) {
312 windows_error(
"GetProcAddress failed", GetLastError()) +
")";
314 const char *error = dlerror();
315 if (error !=
nullptr) {
316 symError += std::string(
": ") + error;
319 throw Error(
"Blackbox",
320 "Unable to find symbol `fzn_blackbox` in dynamic library" +
325 library_symbol<decltype(library_fzn_init)>(loaded,
"fzn_init", 8);
327 library_symbol<decltype(library_fzn_clone)>(loaded,
"fzn_clone", 4);
329 library_symbol<decltype(library_fzn_free)>(loaded,
"fzn_free", 4);
331 throw Error(
"Blackbox",
332 "Dynamic library exports `fzn_init` but not `fzn_clone`");
335 std::vector<const char *> argv;
336 argv.reserve(args.size());
337 for (
const std::string &arg : args) {
338 argv.push_back(arg.c_str());
341 root_initialized =
true;
345 if (root_initialized && (library_fzn_free !=
nullptr)) {
350 close_library(loaded);
357#ifdef GECODE_HAS_THREADS
368#ifdef GECODE_HAS_THREADS
376#ifdef GECODE_HAS_THREADS
379 const std::thread::id owner = std::this_thread::get_id();
395 if (library_fzn_free !=
nullptr) {
397 library_fzn_free(value);
407#ifdef GECODE_HAS_THREADS
410 library_fzn_blackbox(selected->
value,
416 library_fzn_blackbox(
nullptr,
429 for (
size_t i = 0; i < call.
int_output.size(); ++i) {
430 check_int(call.
int_output[i],
"library output", i);
432#ifdef GECODE_HAS_FLOAT_VARS
439 const std::vector<std::string> &args0)
454 if (s->owned_by_current_thread()) {
458 std::unique_ptr<BlackBoxProcessSession> s(
470 std::ostringstream out;
471 out.imbue(std::locale::classic());
472 out.precision(std::numeric_limits<double>::max_digits10);
473 for (
size_t i = 0; i < call.
int_input.size(); ++i) {
480 for (
size_t i = 0; i < call.
float_input.size(); ++i) {
492 if (response.find(
'\0') != std::string::npos) {
493 throw Error(
"BlackBoxExec",
494 "Blackbox process response contains NUL data.");
498 const char *p = response.c_str();
499 auto skip_ws = [](
const char *&q) {
500 while (*q ==
' ' || *q ==
'\t' || *q ==
'\r') {
504 auto skip_final_ws = [](
const char *&q) {
505 while (*q ==
' ' || *q ==
'\t' || *q ==
'\r' || *q ==
'\n') {
509 auto value_end = [](
const char *q) {
510 while (*q !=
',' && *q !=
';' && *q !=
'\n' && *q !=
'\0') {
515 auto check_integer_tail = [](
const char *q,
const char *end) {
517 if (*q !=
' ' && *q !=
'\t' && *q !=
'\r') {
524 auto check_number_tail = [](std::istringstream &in) {
527 if (c !=
' ' && c !=
'\t' && c !=
'\r') {
533 for (
size_t i = 0; i < call.
int_output.size(); ++i) {
535 const char *end = value_end(p);
536 const char *integer = p;
537 if (*integer ==
'+') {
541 const std::from_chars_result parsed =
542 std::from_chars(integer, end, value);
543 if ((parsed.ptr == integer) || (parsed.ec != std::errc()) ||
544 !check_integer_tail(parsed.ptr, end)) {
545 throw Error(
"BlackBoxExec",
"Failed to read output integer " +
547 " from blackbox process output, " +
549 " integer values were expected.");
551 check_int(value,
"blackbox process output", i);
557 throw Error(
"BlackBoxExec",
558 "Blackbox process response is missing an integer output "
566 throw Error(
"BlackBoxExec",
567 "Blackbox process response is missing the `;' separator between "
568 "the integer and floating point outputs.");
573 const char *end = value_end(p);
574 std::istringstream in(std::string(p, end));
575 in.imbue(std::locale::classic());
577 if (!(in >> v) || !check_number_tail(in)) {
578 throw Error(
"BlackBoxExec",
"Failed to read output float " +
580 " from blackbox process output, " +
582 " floating point values were expected.");
584#ifdef GECODE_HAS_FLOAT_VARS
585 check_float(v,
"blackbox process output", i);
592 throw Error(
"BlackBoxExec",
593 "Blackbox process response is missing a floating point "
594 "output separator.");
601 throw Error(
"BlackBoxExec",
602 "Blackbox process response contains trailing data.");
608 const std::string response =
BlackBoxProcessSession & session(void)
std::vector< std::string > args
BlackBoxExec(const std::string &program, const std::vector< std::string > &args)
std::vector< BlackBoxProcessSession * > sessions
void run(BlackBoxCall &call) override
Instance(const std::thread::id &owner0, void *value0)
void *GECODE_BLACKBOX_CALL * library_fzn_clone(void *)
void *GECODE_BLACKBOX_CALL * library_fzn_init(const char **, size_t)
BlackBoxLibrary(const std::string &name, const std::vector< std::string > &args)
Instance * instance(void)
void run(BlackBoxCall &call) override
std::vector< Instance * > instances
Platform process session used by the executable blackbox backend.
virtual std::string exchange(const std::string &request)=0
Exception class for FlatZinc errors
A lock as a scoped frontend for a mutex.
std::string encode_blackbox_request(const BlackBoxCall &call)
Encode one request for the executable backend's line protocol.
BlackBoxProcessSession * create_blackbox_process(const std::string &, const std::vector< std::string > &)
Create the process implementation selected for the target platform.
void decode_blackbox_response(const std::string &response, BlackBoxCall &call)
Decode and validate one response from the executable backend.
const FloatNum max
Largest allowed float value.
const FloatNum min
Smallest allowed float value.
bool valid(int n)
Return whether n is in range.
Gecode toplevel namespace
Inputs and pre-sized output buffers for one backend call.
const std::vector< int64_t > & int_input
std::vector< int64_t > & int_output
const std::vector< double > & float_input
std::vector< double > & float_output