From ebc28744e2cf40af9de7437003e00ade34d01ba7 Mon Sep 17 00:00:00 2001 From: leonarski_f Date: Fri, 19 Jun 2026 23:15:27 +0200 Subject: [PATCH] viewer: fix MSVC compatibility in viewer-reachable code Address portability issues found building jfjoch_viewer with MSVC: - common/{ADUHistogram,AzimuthalIntegrationProfile}.h and image_analysis/spot_finding/StrongPixelSet.h only need DeviceOutput, so include fpga/pcie_driver/jfjoch_fpga.h (plain-C, self-contained) directly instead of acquisition_device/AcquisitionDevice.h, which dragged into the viewer tree. - common/time_utc.h: guard gmtime_r/timegm/localtime_r with the MSVC equivalents (gmtime_s/_mkgmtime/localtime_s) under _WIN32; drop the duplicated includes and add the headers used directly. - gemmi_gph/gemmi/utf.hpp: vendor the upstream gemmi header; fileutil.hpp includes it on Windows for UTF8_to_wchar but it was never vendored. - writer/HDF5Objects.cpp: ExtractFilename returns path.filename().string() (std::filesystem::path has no implicit conversion to std::string on Windows, where it is wchar_t-based). Co-Authored-By: Claude Opus 4.8 --- common/ADUHistogram.h | 2 +- common/AzimuthalIntegrationProfile.h | 2 +- common/time_utc.h | 22 ++++-- gemmi_gph/gemmi/utf.hpp | 78 ++++++++++++++++++++ image_analysis/spot_finding/StrongPixelSet.h | 2 +- writer/HDF5Objects.cpp | 2 +- 6 files changed, 99 insertions(+), 9 deletions(-) create mode 100644 gemmi_gph/gemmi/utf.hpp diff --git a/common/ADUHistogram.h b/common/ADUHistogram.h index 2e30d90b..6a42913d 100644 --- a/common/ADUHistogram.h +++ b/common/ADUHistogram.h @@ -5,7 +5,7 @@ #include #include "MultiLinePlot.h" -#include "../acquisition_device/AcquisitionDevice.h" +#include "../fpga/pcie_driver/jfjoch_fpga.h" // DeviceOutput class ADUHistogram { mutable std::mutex m; diff --git a/common/AzimuthalIntegrationProfile.h b/common/AzimuthalIntegrationProfile.h index 90000efb..0fd41524 100644 --- a/common/AzimuthalIntegrationProfile.h +++ b/common/AzimuthalIntegrationProfile.h @@ -8,7 +8,7 @@ #include "MultiLinePlot.h" #include "AzimuthalIntegrationMapping.h" -#include "../acquisition_device/AcquisitionDevice.h" +#include "../fpga/pcie_driver/jfjoch_fpga.h" // DeviceOutput class AzimuthalIntegrationProfile { mutable std::mutex m; diff --git a/common/time_utc.h b/common/time_utc.h index 4d925929..4a1067ab 100644 --- a/common/time_utc.h +++ b/common/time_utc.h @@ -7,10 +7,10 @@ #include #include #include -#include -#include -#include -#include +#include +#include +#include +#include inline std::string time_UTC(const std::chrono::time_point &input) { auto time_ms = std::chrono::duration_cast(input.time_since_epoch()).count(); @@ -19,8 +19,12 @@ inline std::string time_UTC(const std::chrono::time_point(t) * 1000ULL + milliseconds; } @@ -71,7 +79,11 @@ inline std::string utc_to_local_human_readable(const std::string& utc_string) { time_t t = system_clock::to_time_t(tp); std::tm tm_local{}; +#ifdef _WIN32 + if (localtime_s(&tm_local, &t) != 0) +#else if (localtime_r(&t, &tm_local) == nullptr) +#endif return ""; static const char* months[] = { diff --git a/gemmi_gph/gemmi/utf.hpp b/gemmi_gph/gemmi/utf.hpp new file mode 100644 index 00000000..2b407419 --- /dev/null +++ b/gemmi_gph/gemmi/utf.hpp @@ -0,0 +1,78 @@ +// Copyright 2017 Global Phasing Ltd. +// +// Conversion between UTF-8 and wchar. Used only for file names on Windows. + +#ifndef GEMMI_UTF_HPP_ +#define GEMMI_UTF_HPP_ + +#include + +namespace gemmi { + +// from Mark Ransom's answer +// https://stackoverflow.com/questions/148403/utf8-to-from-wide-char-conversion-in-stl/148766#148766 +inline std::wstring UTF8_to_wchar(const char* in) { + std::wstring out; + unsigned int codepoint = 0; + while (*in != 0) { + unsigned char ch = static_cast(*in); + if (ch <= 0x7f) + codepoint = ch; + else if (ch <= 0xbf) + codepoint = (codepoint << 6) | (ch & 0x3f); + else if (ch <= 0xdf) + codepoint = ch & 0x1f; + else if (ch <= 0xef) + codepoint = ch & 0x0f; + else + codepoint = ch & 0x07; + ++in; + if ((*in & 0xc0) != 0x80 && codepoint <= 0x10ffff) { + if (sizeof(wchar_t) > 2) { + out.append(1, static_cast(codepoint)); + } else if (codepoint > 0xffff) { + out.append(1, static_cast(0xd800 + (codepoint >> 10))); + out.append(1, static_cast(0xdc00 + (codepoint & 0x03ff))); + } else if (codepoint < 0xd800 || codepoint >= 0xe000) { + out.append(1, static_cast(codepoint)); + } + } + } + return out; +} + +inline std::string wchar_to_UTF8(const wchar_t* in) { + std::string out; + unsigned int codepoint = 0; + while (*in != 0) { + if (*in >= 0xd800 && *in <= 0xdbff) { + codepoint = ((*in - 0xd800) << 10) + 0x10000; + } else { + if (*in >= 0xdc00 && *in <= 0xdfff) + codepoint |= *in - 0xdc00; + else + codepoint = *in; + if (codepoint <= 0x7f) { + out += static_cast(codepoint); + } else if (codepoint <= 0x7ff) { + out += static_cast(0xc0 | ((codepoint >> 6) & 0x1f)); + out += static_cast(0x80 | (codepoint & 0x3f)); + } else if (codepoint <= 0xffff) { + out += static_cast(0xe0 | ((codepoint >> 12) & 0x0f)); + out += static_cast(0x80 | ((codepoint >> 6) & 0x3f)); + out += static_cast(0x80 | (codepoint & 0x3f)); + } else { + out += static_cast(0xf0 | ((codepoint >> 18) & 0x07)); + out += static_cast(0x80 | ((codepoint >> 12) & 0x3f)); + out += static_cast(0x80 | ((codepoint >> 6) & 0x3f)); + out += static_cast(0x80 | (codepoint & 0x3f)); + } + codepoint = 0; + } + ++in; + } + return out; +} + +} // namespace gemmi +#endif diff --git a/image_analysis/spot_finding/StrongPixelSet.h b/image_analysis/spot_finding/StrongPixelSet.h index cbc322da..be242cec 100644 --- a/image_analysis/spot_finding/StrongPixelSet.h +++ b/image_analysis/spot_finding/StrongPixelSet.h @@ -5,7 +5,7 @@ #include "../../common/DiffractionExperiment.h" #include "../../common/DiffractionSpot.h" -#include "../../acquisition_device/AcquisitionDevice.h" +#include "../../fpga/pcie_driver/jfjoch_fpga.h" // DeviceOutput #include "SpotFindingSettings.h" struct strong_pixel { diff --git a/writer/HDF5Objects.cpp b/writer/HDF5Objects.cpp index e596398e..dc8329ec 100644 --- a/writer/HDF5Objects.cpp +++ b/writer/HDF5Objects.cpp @@ -1014,7 +1014,7 @@ CompressionAlgorithm HDF5Dcpl::GetCompression() { std::string ExtractFilename(const std::string& str) { std::filesystem::path path(str); // if (std::filesystem::is_regular_file(path)) - return path.filename(); + return path.filename().string(); } bool HDF5Object::GetBool(const std::string &name) const {