#pragma once #include #include #include #include #include #include #include #include #include #include #include #include namespace nlohmann { template void to_json(json& j, const std::optional& opt) { if (opt) j = *opt; else j = nullptr; } template void from_json(const json& j, std::optional& opt) { if (j.is_null()) opt = std::nullopt; else opt = j.get(); } } // ============================================================ // Types // ============================================================ struct TimerConfig { std::string name; }; NLOHMANN_DEFINE_TYPE_NON_INTRUSIVE(TimerConfig, name) struct EchoRequest { std::string message; int64_t delayMs; }; NLOHMANN_DEFINE_TYPE_NON_INTRUSIVE(EchoRequest, message, delayMs) struct EchoResponse { std::string echoed; std::string timerName; }; NLOHMANN_DEFINE_TYPE_NON_INTRUSIVE(EchoResponse, echoed, timerName) struct ComplexRequest { std::vector messages; std::vector tags; std::optional note; std::optional retries; }; NLOHMANN_DEFINE_TYPE_NON_INTRUSIVE(ComplexRequest, messages, tags, note, retries) struct ComplexResponse { std::string summary; int64_t itemCount; bool hasNote; }; NLOHMANN_DEFINE_TYPE_NON_INTRUSIVE(ComplexResponse, summary, itemCount, hasNote) // ============================================================ // C FFI declarations // ============================================================ extern "C" { typedef void (*FfiCallback)(int ret, const char* msg, size_t len, void* user_data); int nimtimer_create(const char* config_json, FfiCallback callback, void* user_data); int nimtimer_echo(void* ctx, FfiCallback callback, void* user_data, const char* req_json); int nimtimer_version(void* ctx, FfiCallback callback, void* user_data); int nimtimer_complex(void* ctx, FfiCallback callback, void* user_data, const char* req_json); void nimtimer_destroy(void* ctx); } // extern "C" template inline std::string serializeFfiArg(const T& value) { return nlohmann::json(value).dump(); } inline std::string serializeFfiArg(void* value) { return std::to_string(reinterpret_cast(value)); } template inline T deserializeFfiResult(const std::string& raw) { try { return nlohmann::json::parse(raw).get(); } catch (const nlohmann::json::exception& e) { throw std::runtime_error(std::string("FFI response deserialization failed: ") + e.what()); } } template<> inline void* deserializeFfiResult(const std::string& raw) { try { return reinterpret_cast(static_cast(std::stoull(raw))); } catch (const std::exception& e) { throw std::runtime_error(std::string("FFI returned non-numeric address: ") + raw); } } // ============================================================ // Synchronous call helper (anonymous namespace, header-only) // ============================================================ namespace { struct FfiCallState_ { std::mutex mtx; std::condition_variable cv; bool done{false}; bool ok{false}; std::string msg; }; inline void ffi_cb_(int ret, const char* msg, size_t /*len*/, void* ud) { auto* sptr = static_cast*>(ud); { auto& s = **sptr; std::lock_guard lock(s.mtx); s.ok = (ret == 0); s.msg = msg ? std::string(msg) : std::string{}; s.done = true; s.cv.notify_one(); } delete sptr; } inline std::string ffi_call_(std::function f, std::chrono::milliseconds timeout) { auto state = std::make_shared(); auto* cb_ref = new std::shared_ptr(state); const int ret = f(ffi_cb_, cb_ref); if (ret == 2) { delete cb_ref; throw std::runtime_error("RET_MISSING_CALLBACK (internal error)"); } std::unique_lock lock(state->mtx); const bool fired = state->cv.wait_for(lock, timeout, [&]{ return state->done; }); if (!fired) throw std::runtime_error("FFI call timed out after " + std::to_string(timeout.count()) + "ms"); if (!state->ok) throw std::runtime_error(state->msg); return state->msg; } } // anonymous namespace // ============================================================ // High-level C++ context class // ============================================================ class NimTimerCtx { public: static NimTimerCtx create(const TimerConfig& config, std::chrono::milliseconds timeout = std::chrono::seconds{30}) { const auto config_json = serializeFfiArg(config); const auto raw = ffi_call_([&](FfiCallback cb, void* ud) { return nimtimer_create(config_json.c_str(), cb, ud); }, timeout); try { const auto addr = std::stoull(raw); return NimTimerCtx(reinterpret_cast(static_cast(addr)), timeout); } catch (const std::exception&) { throw std::runtime_error("FFI create returned non-numeric address: " + raw); } } static std::future createAsync(const TimerConfig& config, std::chrono::milliseconds timeout = std::chrono::seconds{30}) { return std::async(std::launch::async, [config, timeout]() { return create(config, timeout); }); } ~NimTimerCtx() { if (ptr_) { nimtimer_destroy(ptr_); ptr_ = nullptr; } } NimTimerCtx(const NimTimerCtx&) = delete; NimTimerCtx& operator=(const NimTimerCtx&) = delete; NimTimerCtx(NimTimerCtx&& other) noexcept : ptr_(other.ptr_), timeout_(other.timeout_) { other.ptr_ = nullptr; } NimTimerCtx& operator=(NimTimerCtx&& other) noexcept { if (this != &other) { if (ptr_) nimtimer_destroy(ptr_); ptr_ = other.ptr_; timeout_ = other.timeout_; other.ptr_ = nullptr; } return *this; } EchoResponse echo(const EchoRequest& req) const { const auto req_json = serializeFfiArg(req); const auto raw = ffi_call_([&](FfiCallback cb, void* ud) { return nimtimer_echo(ptr_, cb, ud, req_json.c_str()); }, timeout_); return deserializeFfiResult(raw); } std::future echoAsync(const EchoRequest& req) const { return std::async(std::launch::async, [this, req]() { return echo(req); }); } std::string version() const { const auto raw = ffi_call_([&](FfiCallback cb, void* ud) { return nimtimer_version(ptr_, cb, ud); }, timeout_); return deserializeFfiResult(raw); } std::future versionAsync() const { return std::async(std::launch::async, [this]() { return version(); }); } ComplexResponse complex(const ComplexRequest& req) const { const auto req_json = serializeFfiArg(req); const auto raw = ffi_call_([&](FfiCallback cb, void* ud) { return nimtimer_complex(ptr_, cb, ud, req_json.c_str()); }, timeout_); return deserializeFfiResult(raw); } std::future complexAsync(const ComplexRequest& req) const { return std::async(std::launch::async, [this, req]() { return complex(req); }); } private: void* ptr_; std::chrono::milliseconds timeout_; explicit NimTimerCtx(void* p, std::chrono::milliseconds t) : ptr_(p), timeout_(t) {} };