mirror of
https://github.com/logos-messaging/nim-ffi.git
synced 2026-06-22 09:20:13 +00:00
Aligns the C++ generators with the C generator and the symbol naming: the native (zero-serialization, same-process) wrapper is the bare `<lib>.hpp` and the CBOR (inter-process) wrapper carries the `_cbor` suffix — mirroring the C headers (`<lib>.h` / `<lib>_cbor.h`) and the `<name>` / `<name>_cbor` exports. Previously native was `<lib>_native.hpp` and CBOR was the bare `<lib>.hpp`, which is backwards from the symbol convention and would collide on the native `<lib>.h` when both ABIs emit into one dir (ffiMode=both). With the flip, a single `genbindings_cpp` run now drops `<lib>.hpp` + `<lib>_cbor.hpp` side by side, exactly like c_bindings holds both `.h` headers. Consumers updated to match: the CBOR cpp_bindings driver and the C++ e2e suite include `*_cbor.hpp`; the native example includes the bare `<lib>.hpp`. Validated: native example runs on `my_timer.hpp`; C++ e2e suite 19/19 on the `_cbor.hpp` headers; check_bindings_cpp regen is deterministic. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
432 lines
13 KiB
C++
432 lines
13 KiB
C++
// Generated by nim-ffi native C++ codegen. Do not edit by hand.
|
|
//
|
|
// Native (zero-serialization) wrapper over the C ABI in "my_timer.h". Struct params/returns cross as flat C-POD structs — no CBOR. For the
|
|
// inter-process path use the CBOR header (my_timer_cbor.hpp).
|
|
#ifndef NIM_FFI_GEN_MY_TIMER_NATIVE_HPP
|
|
#define NIM_FFI_GEN_MY_TIMER_NATIVE_HPP
|
|
|
|
#include "my_timer.h"
|
|
#include <cstdint>
|
|
#include <functional>
|
|
#include <future>
|
|
#include <map>
|
|
#include <memory>
|
|
#include <optional>
|
|
#include <stdexcept>
|
|
#include <string>
|
|
#include <vector>
|
|
|
|
namespace my_timer {
|
|
|
|
struct TimerConfig {
|
|
std::string name{};
|
|
};
|
|
struct TimerConfigC {
|
|
::TimerConfig c{};
|
|
};
|
|
inline TimerConfigC toC(const TimerConfig& v) {
|
|
TimerConfigC h;
|
|
h.c.name = v.name.c_str();
|
|
return h;
|
|
}
|
|
inline TimerConfig fromC(const ::TimerConfig& c) {
|
|
TimerConfig v{};
|
|
v.name = c.name ? std::string(c.name) : std::string();
|
|
return v;
|
|
}
|
|
|
|
struct EchoRequest {
|
|
std::string message{};
|
|
int64_t delayMs{};
|
|
};
|
|
struct EchoRequestC {
|
|
::EchoRequest c{};
|
|
};
|
|
inline EchoRequestC toC(const EchoRequest& v) {
|
|
EchoRequestC h;
|
|
h.c.message = v.message.c_str();
|
|
h.c.delayMs = v.delayMs;
|
|
return h;
|
|
}
|
|
inline EchoRequest fromC(const ::EchoRequest& c) {
|
|
EchoRequest v{};
|
|
v.message = c.message ? std::string(c.message) : std::string();
|
|
v.delayMs = c.delayMs;
|
|
return v;
|
|
}
|
|
|
|
struct EchoResponse {
|
|
std::string echoed{};
|
|
std::string timerName{};
|
|
};
|
|
struct EchoResponseC {
|
|
::EchoResponse c{};
|
|
};
|
|
inline EchoResponseC toC(const EchoResponse& v) {
|
|
EchoResponseC h;
|
|
h.c.echoed = v.echoed.c_str();
|
|
h.c.timerName = v.timerName.c_str();
|
|
return h;
|
|
}
|
|
inline EchoResponse fromC(const ::EchoResponse& c) {
|
|
EchoResponse v{};
|
|
v.echoed = c.echoed ? std::string(c.echoed) : std::string();
|
|
v.timerName = c.timerName ? std::string(c.timerName) : std::string();
|
|
return v;
|
|
}
|
|
|
|
struct ComplexRequest {
|
|
std::vector<EchoRequest> messages{};
|
|
std::vector<std::string> tags{};
|
|
std::optional<std::string> note{};
|
|
std::optional<int64_t> retries{};
|
|
};
|
|
struct ComplexRequestC {
|
|
::ComplexRequest c{};
|
|
std::vector<::EchoRequest> _messages;
|
|
std::vector<EchoRequestC> _messagesH;
|
|
std::vector<const char*> _tags;
|
|
};
|
|
inline ComplexRequestC toC(const ComplexRequest& v) {
|
|
ComplexRequestC h;
|
|
for (const auto& it : v.messages) {
|
|
h._messagesH.push_back(toC(it));
|
|
}
|
|
for (const auto& hh : h._messagesH) h._messages.push_back(hh.c);
|
|
h.c.messages = h._messages.empty() ? nullptr : h._messages.data();
|
|
h.c.messages_len = h._messages.size();
|
|
for (const auto& it : v.tags) {
|
|
h._tags.push_back(it.c_str());
|
|
}
|
|
h.c.tags = h._tags.empty() ? nullptr : h._tags.data();
|
|
h.c.tags_len = h._tags.size();
|
|
if (v.note.has_value()) {
|
|
h.c.note_present = 1;
|
|
h.c.note = (*v.note).c_str();
|
|
}
|
|
if (v.retries.has_value()) {
|
|
h.c.retries_present = 1;
|
|
h.c.retries = (*v.retries);
|
|
}
|
|
return h;
|
|
}
|
|
inline ComplexRequest fromC(const ::ComplexRequest& c) {
|
|
ComplexRequest v{};
|
|
for (std::size_t i = 0; i < c.messages_len; i++)
|
|
v.messages.push_back(fromC(c.messages[i]));
|
|
for (std::size_t i = 0; i < c.tags_len; i++)
|
|
v.tags.push_back((c.tags[i] ? std::string(c.tags[i]) : std::string()));
|
|
if (c.note_present)
|
|
v.note = (c.note ? std::string(c.note) : std::string());
|
|
if (c.retries_present)
|
|
v.retries = c.retries;
|
|
return v;
|
|
}
|
|
|
|
struct ComplexResponse {
|
|
std::string summary{};
|
|
int64_t itemCount{};
|
|
bool hasNote{};
|
|
};
|
|
struct ComplexResponseC {
|
|
::ComplexResponse c{};
|
|
};
|
|
inline ComplexResponseC toC(const ComplexResponse& v) {
|
|
ComplexResponseC h;
|
|
h.c.summary = v.summary.c_str();
|
|
h.c.itemCount = v.itemCount;
|
|
h.c.hasNote = v.hasNote ? 1 : 0;
|
|
return h;
|
|
}
|
|
inline ComplexResponse fromC(const ::ComplexResponse& c) {
|
|
ComplexResponse v{};
|
|
v.summary = c.summary ? std::string(c.summary) : std::string();
|
|
v.itemCount = c.itemCount;
|
|
v.hasNote = c.hasNote != 0;
|
|
return v;
|
|
}
|
|
|
|
struct EchoEvent {
|
|
std::string message{};
|
|
int64_t echoCount{};
|
|
};
|
|
struct EchoEventC {
|
|
::EchoEvent c{};
|
|
};
|
|
inline EchoEventC toC(const EchoEvent& v) {
|
|
EchoEventC h;
|
|
h.c.message = v.message.c_str();
|
|
h.c.echoCount = v.echoCount;
|
|
return h;
|
|
}
|
|
inline EchoEvent fromC(const ::EchoEvent& c) {
|
|
EchoEvent v{};
|
|
v.message = c.message ? std::string(c.message) : std::string();
|
|
v.echoCount = c.echoCount;
|
|
return v;
|
|
}
|
|
|
|
struct JobSpec {
|
|
std::string name{};
|
|
std::vector<std::string> payload{};
|
|
int64_t priority{};
|
|
};
|
|
struct JobSpecC {
|
|
::JobSpec c{};
|
|
std::vector<const char*> _payload;
|
|
};
|
|
inline JobSpecC toC(const JobSpec& v) {
|
|
JobSpecC h;
|
|
h.c.name = v.name.c_str();
|
|
for (const auto& it : v.payload) {
|
|
h._payload.push_back(it.c_str());
|
|
}
|
|
h.c.payload = h._payload.empty() ? nullptr : h._payload.data();
|
|
h.c.payload_len = h._payload.size();
|
|
h.c.priority = v.priority;
|
|
return h;
|
|
}
|
|
inline JobSpec fromC(const ::JobSpec& c) {
|
|
JobSpec v{};
|
|
v.name = c.name ? std::string(c.name) : std::string();
|
|
for (std::size_t i = 0; i < c.payload_len; i++)
|
|
v.payload.push_back((c.payload[i] ? std::string(c.payload[i]) : std::string()));
|
|
v.priority = c.priority;
|
|
return v;
|
|
}
|
|
|
|
struct RetryPolicy {
|
|
int64_t maxAttempts{};
|
|
int64_t backoffMs{};
|
|
std::vector<std::string> retryOn{};
|
|
};
|
|
struct RetryPolicyC {
|
|
::RetryPolicy c{};
|
|
std::vector<const char*> _retryOn;
|
|
};
|
|
inline RetryPolicyC toC(const RetryPolicy& v) {
|
|
RetryPolicyC h;
|
|
h.c.maxAttempts = v.maxAttempts;
|
|
h.c.backoffMs = v.backoffMs;
|
|
for (const auto& it : v.retryOn) {
|
|
h._retryOn.push_back(it.c_str());
|
|
}
|
|
h.c.retryOn = h._retryOn.empty() ? nullptr : h._retryOn.data();
|
|
h.c.retryOn_len = h._retryOn.size();
|
|
return h;
|
|
}
|
|
inline RetryPolicy fromC(const ::RetryPolicy& c) {
|
|
RetryPolicy v{};
|
|
v.maxAttempts = c.maxAttempts;
|
|
v.backoffMs = c.backoffMs;
|
|
for (std::size_t i = 0; i < c.retryOn_len; i++)
|
|
v.retryOn.push_back((c.retryOn[i] ? std::string(c.retryOn[i]) : std::string()));
|
|
return v;
|
|
}
|
|
|
|
struct ScheduleConfig {
|
|
int64_t startAtMs{};
|
|
int64_t intervalMs{};
|
|
std::optional<int64_t> jitter{};
|
|
};
|
|
struct ScheduleConfigC {
|
|
::ScheduleConfig c{};
|
|
};
|
|
inline ScheduleConfigC toC(const ScheduleConfig& v) {
|
|
ScheduleConfigC h;
|
|
h.c.startAtMs = v.startAtMs;
|
|
h.c.intervalMs = v.intervalMs;
|
|
if (v.jitter.has_value()) {
|
|
h.c.jitter_present = 1;
|
|
h.c.jitter = (*v.jitter);
|
|
}
|
|
return h;
|
|
}
|
|
inline ScheduleConfig fromC(const ::ScheduleConfig& c) {
|
|
ScheduleConfig v{};
|
|
v.startAtMs = c.startAtMs;
|
|
v.intervalMs = c.intervalMs;
|
|
if (c.jitter_present)
|
|
v.jitter = c.jitter;
|
|
return v;
|
|
}
|
|
|
|
struct ScheduleResult {
|
|
std::string jobId{};
|
|
int64_t willRunCount{};
|
|
int64_t firstRunAtMs{};
|
|
int64_t effectiveBackoffMs{};
|
|
};
|
|
struct ScheduleResultC {
|
|
::ScheduleResult c{};
|
|
};
|
|
inline ScheduleResultC toC(const ScheduleResult& v) {
|
|
ScheduleResultC h;
|
|
h.c.jobId = v.jobId.c_str();
|
|
h.c.willRunCount = v.willRunCount;
|
|
h.c.firstRunAtMs = v.firstRunAtMs;
|
|
h.c.effectiveBackoffMs = v.effectiveBackoffMs;
|
|
return h;
|
|
}
|
|
inline ScheduleResult fromC(const ::ScheduleResult& c) {
|
|
ScheduleResult v{};
|
|
v.jobId = c.jobId ? std::string(c.jobId) : std::string();
|
|
v.willRunCount = c.willRunCount;
|
|
v.firstRunAtMs = c.firstRunAtMs;
|
|
v.effectiveBackoffMs = c.effectiveBackoffMs;
|
|
return v;
|
|
}
|
|
|
|
namespace detail {
|
|
template <typename T> struct Capture {
|
|
int ret = RET_ERR;
|
|
T value{};
|
|
std::string err;
|
|
std::promise<void> done;
|
|
};
|
|
struct AckCapture {
|
|
int ret = RET_ERR;
|
|
std::string err;
|
|
std::promise<void> done;
|
|
};
|
|
inline std::string rawText(const char* msg, std::size_t len) {
|
|
return (msg && len) ? std::string(msg, len) : std::string();
|
|
}
|
|
// Event listener storage: a heap handler kept alive by the node so the
|
|
// native callback's userData stays valid until removed.
|
|
struct ListenerBase { virtual ~ListenerBase() = default; };
|
|
template <typename T> struct EventListener : ListenerBase {
|
|
std::function<void(const T&)> handler;
|
|
explicit EventListener(std::function<void(const T&)> h)
|
|
: handler(std::move(h)) {}
|
|
};
|
|
} // namespace detail
|
|
struct ListenerHandle { std::uint64_t id = 0; };
|
|
|
|
extern "C" {
|
|
inline void my_timer_native_ack(int ret, const char* msg, std::size_t len, void* ud) {
|
|
auto* c = static_cast<detail::AckCapture*>(ud);
|
|
c->ret = ret;
|
|
if (ret == RET_ERR) c->err = detail::rawText(msg, len);
|
|
c->done.set_value();
|
|
}
|
|
inline void my_timer_native_str(int ret, const char* msg, std::size_t len, void* ud) {
|
|
auto* c = static_cast<detail::Capture<std::string>*>(ud);
|
|
c->ret = ret;
|
|
if (ret == RET_OK) c->value = detail::rawText(msg, len);
|
|
else c->err = detail::rawText(msg, len);
|
|
c->done.set_value();
|
|
}
|
|
inline void my_timer_native_my_timer_echo(int ret, const char* msg, std::size_t len, void* ud) {
|
|
auto* c = static_cast<detail::Capture<EchoResponse>*>(ud);
|
|
c->ret = ret;
|
|
if (ret == RET_OK) c->value = fromC(*reinterpret_cast<const ::EchoResponse*>(msg));
|
|
else c->err = detail::rawText(msg, len);
|
|
c->done.set_value();
|
|
}
|
|
inline void my_timer_native_my_timer_complex(int ret, const char* msg, std::size_t len, void* ud) {
|
|
auto* c = static_cast<detail::Capture<ComplexResponse>*>(ud);
|
|
c->ret = ret;
|
|
if (ret == RET_OK) c->value = fromC(*reinterpret_cast<const ::ComplexResponse*>(msg));
|
|
else c->err = detail::rawText(msg, len);
|
|
c->done.set_value();
|
|
}
|
|
inline void my_timer_native_my_timer_schedule(int ret, const char* msg, std::size_t len, void* ud) {
|
|
auto* c = static_cast<detail::Capture<ScheduleResult>*>(ud);
|
|
c->ret = ret;
|
|
if (ret == RET_OK) c->value = fromC(*reinterpret_cast<const ::ScheduleResult*>(msg));
|
|
else c->err = detail::rawText(msg, len);
|
|
c->done.set_value();
|
|
}
|
|
inline void my_timer_evt_OnEchoFired(int ret, const char* msg, std::size_t, void* ud) {
|
|
auto* l = static_cast<detail::EventListener<EchoEvent>*>(ud);
|
|
if (ret == RET_OK && l->handler)
|
|
l->handler(fromC(*reinterpret_cast<const ::EchoEvent*>(msg)));
|
|
}
|
|
} // extern "C"
|
|
|
|
class My_timerNode {
|
|
public:
|
|
explicit My_timerNode(const TimerConfig& config) {
|
|
detail::AckCapture cap;
|
|
auto fut = cap.done.get_future();
|
|
auto c_config = toC(config);
|
|
ctx_ = my_timer_create(c_config.c, my_timer_native_ack, &cap);
|
|
if (!ctx_) throw std::runtime_error("my_timer_create returned null");
|
|
fut.wait();
|
|
if (cap.ret != RET_OK) throw std::runtime_error(cap.err);
|
|
}
|
|
|
|
EchoResponse Echo(const EchoRequest& req) {
|
|
detail::Capture<EchoResponse> cap;
|
|
auto fut = cap.done.get_future();
|
|
auto c_req = toC(req);
|
|
if (my_timer_echo(ctx_, my_timer_native_my_timer_echo, &cap, c_req.c) != RET_OK)
|
|
throw std::runtime_error("my_timer_echo dispatch failed");
|
|
fut.wait();
|
|
if (cap.ret != RET_OK) throw std::runtime_error(cap.err);
|
|
return cap.value;
|
|
}
|
|
|
|
std::string Version() {
|
|
detail::Capture<std::string> cap;
|
|
auto fut = cap.done.get_future();
|
|
if (my_timer_version(ctx_, my_timer_native_str, &cap) != RET_OK)
|
|
throw std::runtime_error("my_timer_version dispatch failed");
|
|
fut.wait();
|
|
if (cap.ret != RET_OK) throw std::runtime_error(cap.err);
|
|
return cap.value;
|
|
}
|
|
|
|
ComplexResponse Complex(const ComplexRequest& req) {
|
|
detail::Capture<ComplexResponse> cap;
|
|
auto fut = cap.done.get_future();
|
|
auto c_req = toC(req);
|
|
if (my_timer_complex(ctx_, my_timer_native_my_timer_complex, &cap, c_req.c) != RET_OK)
|
|
throw std::runtime_error("my_timer_complex dispatch failed");
|
|
fut.wait();
|
|
if (cap.ret != RET_OK) throw std::runtime_error(cap.err);
|
|
return cap.value;
|
|
}
|
|
|
|
ScheduleResult Schedule(const JobSpec& job, const RetryPolicy& retry, const ScheduleConfig& schedule) {
|
|
detail::Capture<ScheduleResult> cap;
|
|
auto fut = cap.done.get_future();
|
|
auto c_job = toC(job);
|
|
auto c_retry = toC(retry);
|
|
auto c_schedule = toC(schedule);
|
|
if (my_timer_schedule(ctx_, my_timer_native_my_timer_schedule, &cap, c_job.c, c_retry.c, c_schedule.c) != RET_OK)
|
|
throw std::runtime_error("my_timer_schedule dispatch failed");
|
|
fut.wait();
|
|
if (cap.ret != RET_OK) throw std::runtime_error(cap.err);
|
|
return cap.value;
|
|
}
|
|
|
|
ListenerHandle OnEchoFired(std::function<void(const EchoEvent&)> handler) {
|
|
auto l = std::make_unique<detail::EventListener<EchoEvent>>(std::move(handler));
|
|
auto* raw = l.get();
|
|
const auto id = my_timer_add_event_listener(ctx_, "on_echo_fired", &my_timer_evt_OnEchoFired, raw);
|
|
if (id == 0) return ListenerHandle{0};
|
|
listeners_.emplace(id, std::move(l));
|
|
return ListenerHandle{id};
|
|
}
|
|
|
|
bool removeEventListener(ListenerHandle handle) {
|
|
if (handle.id == 0) return false;
|
|
const auto rc = my_timer_remove_event_listener(ctx_, handle.id);
|
|
listeners_.erase(handle.id);
|
|
return rc == 0;
|
|
}
|
|
|
|
~My_timerNode() { if (ctx_) my_timer_destroy(ctx_); }
|
|
My_timerNode(const My_timerNode&) = delete;
|
|
My_timerNode& operator=(const My_timerNode&) = delete;
|
|
|
|
private:
|
|
void* ctx_ = nullptr;
|
|
std::map<std::uint64_t, std::unique_ptr<detail::ListenerBase>> listeners_;
|
|
};
|
|
|
|
} // namespace my_timer
|
|
|
|
#endif // NIM_FFI_GEN_MY_TIMER_NATIVE_HPP
|