Skip to content

Commit 64b62e2

Browse files
committed
Correct the CRTP visitor interface
1 parent a0e427e commit 64b62e2

File tree

10 files changed

+485
-16
lines changed

10 files changed

+485
-16
lines changed

cpp/include/hgraph/types/base_time_series.h

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -204,6 +204,39 @@ namespace hgraph {
204204
engine_time_t _notify_time{MIN_DT};
205205
};
206206

207+
// Implementation of CRTP accept forwarding for TimeSeriesInputVisitable
208+
// This is tricky: we need to call the concrete type's accept(), not BaseTimeSeriesInput's
209+
// The concrete types (like TimeSeriesValueReferenceInput) override this with their own accept()
210+
// So we just need a default implementation that's never actually called
211+
template<typename Visitor>
212+
requires (!std::is_base_of_v<TimeSeriesVisitor, Visitor>)
213+
decltype(auto) TimeSeriesInputVisitable::accept(Visitor& visitor) {
214+
// This should never be called - concrete types override with CRTP accept
215+
throw std::runtime_error("CRTP accept() called on TimeSeriesInputVisitable base - missing override in derived class");
216+
}
217+
218+
template<typename Visitor>
219+
requires (!std::is_base_of_v<TimeSeriesVisitor, Visitor>)
220+
decltype(auto) TimeSeriesInputVisitable::accept(Visitor& visitor) const {
221+
// This should never be called - concrete types override with CRTP accept
222+
throw std::runtime_error("CRTP accept() called on TimeSeriesInputVisitable base - missing override in derived class");
223+
}
224+
225+
// Implementation of CRTP accept forwarding for TimeSeriesOutputVisitable
226+
template<typename Visitor>
227+
requires (!std::is_base_of_v<TimeSeriesVisitor, Visitor>)
228+
decltype(auto) TimeSeriesOutputVisitable::accept(Visitor& visitor) {
229+
// This should never be called - concrete types override with CRTP accept
230+
throw std::runtime_error("CRTP accept() called on TimeSeriesOutputVisitable base - missing override in derived class");
231+
}
232+
233+
template<typename Visitor>
234+
requires (!std::is_base_of_v<TimeSeriesVisitor, Visitor>)
235+
decltype(auto) TimeSeriesOutputVisitable::accept(Visitor& visitor) const {
236+
// This should never be called - concrete types override with CRTP accept
237+
throw std::runtime_error("CRTP accept() called on TimeSeriesOutputVisitable base - missing override in derived class");
238+
}
239+
207240
} // namespace hgraph
208241

209242
#endif // BASE_TIME_SERIES_H

cpp/include/hgraph/types/ref.h

Lines changed: 250 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -243,52 +243,130 @@ namespace hgraph {
243243
struct TimeSeriesValueReferenceInput : TimeSeriesReferenceInput {
244244
using TimeSeriesReferenceInput::TimeSeriesReferenceInput;
245245
static void register_with_nanobind(nb::module_ &m);
246+
247+
// CRTP visitor support (compile-time dispatch)
248+
template<typename Visitor>
249+
requires (!std::is_base_of_v<TimeSeriesVisitor, Visitor>)
250+
decltype(auto) accept(Visitor& visitor) {
251+
return visitor(*this);
252+
}
253+
254+
template<typename Visitor>
255+
requires (!std::is_base_of_v<TimeSeriesVisitor, Visitor>)
256+
decltype(auto) accept(Visitor& visitor) const {
257+
return visitor(*this);
258+
}
246259
};
247260

248261
struct TimeSeriesListReferenceInput : TimeSeriesReferenceInput {
249262
using TimeSeriesReferenceInput::TimeSeriesReferenceInput;
250-
263+
251264
// Constructor that accepts size
252265
TimeSeriesListReferenceInput(Node *owning_node, size_t size);
253266
TimeSeriesListReferenceInput(TimeSeriesType *parent_input, size_t size);
254-
267+
255268
TimeSeriesInput *get_input(size_t index) override;
256269
size_t size() const { return _size; }
257-
270+
258271
static void register_with_nanobind(nb::module_ &m);
259-
272+
273+
// CRTP visitor support (compile-time dispatch)
274+
template<typename Visitor>
275+
requires (!std::is_base_of_v<TimeSeriesVisitor, Visitor>)
276+
decltype(auto) accept(Visitor& visitor) {
277+
return visitor(*this);
278+
}
279+
280+
template<typename Visitor>
281+
requires (!std::is_base_of_v<TimeSeriesVisitor, Visitor>)
282+
decltype(auto) accept(Visitor& visitor) const {
283+
return visitor(*this);
284+
}
285+
260286
private:
261287
size_t _size{0};
262288
};
263289

264290
struct TimeSeriesBundleReferenceInput : TimeSeriesReferenceInput {
265291
using TimeSeriesReferenceInput::TimeSeriesReferenceInput;
266-
292+
267293
// Constructor that accepts size
268294
TimeSeriesBundleReferenceInput(Node *owning_node, size_t size);
269295
TimeSeriesBundleReferenceInput(TimeSeriesType *parent_input, size_t size);
270-
296+
271297
size_t size() const { return _size; }
272-
298+
273299
static void register_with_nanobind(nb::module_ &m);
274-
300+
301+
// CRTP visitor support (compile-time dispatch)
302+
template<typename Visitor>
303+
requires (!std::is_base_of_v<TimeSeriesVisitor, Visitor>)
304+
decltype(auto) accept(Visitor& visitor) {
305+
return visitor(*this);
306+
}
307+
308+
template<typename Visitor>
309+
requires (!std::is_base_of_v<TimeSeriesVisitor, Visitor>)
310+
decltype(auto) accept(Visitor& visitor) const {
311+
return visitor(*this);
312+
}
313+
275314
private:
276315
size_t _size{0};
277316
};
278317

279318
struct TimeSeriesDictReferenceInput : TimeSeriesReferenceInput {
280319
using TimeSeriesReferenceInput::TimeSeriesReferenceInput;
281320
static void register_with_nanobind(nb::module_ &m);
321+
322+
// CRTP visitor support (compile-time dispatch)
323+
template<typename Visitor>
324+
requires (!std::is_base_of_v<TimeSeriesVisitor, Visitor>)
325+
decltype(auto) accept(Visitor& visitor) {
326+
return visitor(*this);
327+
}
328+
329+
template<typename Visitor>
330+
requires (!std::is_base_of_v<TimeSeriesVisitor, Visitor>)
331+
decltype(auto) accept(Visitor& visitor) const {
332+
return visitor(*this);
333+
}
282334
};
283335

284336
struct TimeSeriesSetReferenceInput : TimeSeriesReferenceInput {
285337
using TimeSeriesReferenceInput::TimeSeriesReferenceInput;
286338
static void register_with_nanobind(nb::module_ &m);
339+
340+
// CRTP visitor support (compile-time dispatch)
341+
template<typename Visitor>
342+
requires (!std::is_base_of_v<TimeSeriesVisitor, Visitor>)
343+
decltype(auto) accept(Visitor& visitor) {
344+
return visitor(*this);
345+
}
346+
347+
template<typename Visitor>
348+
requires (!std::is_base_of_v<TimeSeriesVisitor, Visitor>)
349+
decltype(auto) accept(Visitor& visitor) const {
350+
return visitor(*this);
351+
}
287352
};
288353

289354
struct TimeSeriesWindowReferenceInput : TimeSeriesReferenceInput {
290355
using TimeSeriesReferenceInput::TimeSeriesReferenceInput;
291356
static void register_with_nanobind(nb::module_ &m);
357+
358+
// CRTP visitor support (compile-time dispatch)
359+
template<typename Visitor>
360+
requires (!std::is_base_of_v<TimeSeriesVisitor, Visitor>)
361+
decltype(auto) accept(Visitor& visitor) {
362+
return visitor(*this);
363+
}
364+
365+
template<typename Visitor>
366+
requires (!std::is_base_of_v<TimeSeriesVisitor, Visitor>)
367+
decltype(auto) accept(Visitor& visitor) const {
368+
return visitor(*this);
369+
}
292370
};
293371

294372
// ============================================================
@@ -298,51 +376,207 @@ namespace hgraph {
298376
struct TimeSeriesValueReferenceOutput final : TimeSeriesReferenceOutput {
299377
using TimeSeriesReferenceOutput::TimeSeriesReferenceOutput;
300378
static void register_with_nanobind(nb::module_ &m);
379+
380+
// Visitor support - Acyclic pattern (runtime dispatch)
381+
void accept(TimeSeriesVisitor& visitor) override {
382+
if (auto* typed_visitor = dynamic_cast<TimeSeriesOutputVisitor<TimeSeriesValueReferenceOutput>*>(&visitor)) {
383+
typed_visitor->visit(*this);
384+
}
385+
}
386+
387+
void accept(TimeSeriesVisitor& visitor) const override {
388+
if (auto* typed_visitor = dynamic_cast<ConstTimeSeriesOutputVisitor<TimeSeriesValueReferenceOutput>*>(&visitor)) {
389+
typed_visitor->visit(*this);
390+
}
391+
}
392+
393+
// CRTP visitor support (compile-time dispatch)
394+
template<typename Visitor>
395+
requires (!std::is_base_of_v<TimeSeriesVisitor, Visitor>)
396+
decltype(auto) accept(Visitor& visitor) {
397+
return visitor(*this);
398+
}
399+
400+
template<typename Visitor>
401+
requires (!std::is_base_of_v<TimeSeriesVisitor, Visitor>)
402+
decltype(auto) accept(Visitor& visitor) const {
403+
return visitor(*this);
404+
}
301405
};
302406

303407
struct TimeSeriesListReferenceOutput final : TimeSeriesReferenceOutput {
304408
using TimeSeriesReferenceOutput::TimeSeriesReferenceOutput;
305-
409+
306410
// Constructor that accepts size
307411
TimeSeriesListReferenceOutput(Node *owning_node, size_t size);
308412
TimeSeriesListReferenceOutput(TimeSeriesType *parent_output, size_t size);
309-
413+
310414
size_t size() const { return _size; }
311-
415+
312416
static void register_with_nanobind(nb::module_ &m);
313-
417+
418+
// Visitor support - Acyclic pattern (runtime dispatch)
419+
void accept(TimeSeriesVisitor& visitor) override {
420+
if (auto* typed_visitor = dynamic_cast<TimeSeriesOutputVisitor<TimeSeriesListReferenceOutput>*>(&visitor)) {
421+
typed_visitor->visit(*this);
422+
}
423+
}
424+
425+
void accept(TimeSeriesVisitor& visitor) const override {
426+
if (auto* typed_visitor = dynamic_cast<ConstTimeSeriesOutputVisitor<TimeSeriesListReferenceOutput>*>(&visitor)) {
427+
typed_visitor->visit(*this);
428+
}
429+
}
430+
431+
// CRTP visitor support (compile-time dispatch)
432+
template<typename Visitor>
433+
requires (!std::is_base_of_v<TimeSeriesVisitor, Visitor>)
434+
decltype(auto) accept(Visitor& visitor) {
435+
return visitor(*this);
436+
}
437+
438+
template<typename Visitor>
439+
requires (!std::is_base_of_v<TimeSeriesVisitor, Visitor>)
440+
decltype(auto) accept(Visitor& visitor) const {
441+
return visitor(*this);
442+
}
443+
314444
private:
315445
size_t _size{0};
316446
};
317447

318448
struct TimeSeriesBundleReferenceOutput final : TimeSeriesReferenceOutput {
319449
using TimeSeriesReferenceOutput::TimeSeriesReferenceOutput;
320-
450+
321451
// Constructor that accepts size
322452
TimeSeriesBundleReferenceOutput(Node *owning_node, size_t size);
323453
TimeSeriesBundleReferenceOutput(TimeSeriesType *parent_output, size_t size);
324-
454+
325455
size_t size() const { return _size; }
326-
456+
327457
static void register_with_nanobind(nb::module_ &m);
328-
458+
459+
// Visitor support - Acyclic pattern (runtime dispatch)
460+
void accept(TimeSeriesVisitor& visitor) override {
461+
if (auto* typed_visitor = dynamic_cast<TimeSeriesOutputVisitor<TimeSeriesBundleReferenceOutput>*>(&visitor)) {
462+
typed_visitor->visit(*this);
463+
}
464+
}
465+
466+
void accept(TimeSeriesVisitor& visitor) const override {
467+
if (auto* typed_visitor = dynamic_cast<ConstTimeSeriesOutputVisitor<TimeSeriesBundleReferenceOutput>*>(&visitor)) {
468+
typed_visitor->visit(*this);
469+
}
470+
}
471+
472+
// CRTP visitor support (compile-time dispatch)
473+
template<typename Visitor>
474+
requires (!std::is_base_of_v<TimeSeriesVisitor, Visitor>)
475+
decltype(auto) accept(Visitor& visitor) {
476+
return visitor(*this);
477+
}
478+
479+
template<typename Visitor>
480+
requires (!std::is_base_of_v<TimeSeriesVisitor, Visitor>)
481+
decltype(auto) accept(Visitor& visitor) const {
482+
return visitor(*this);
483+
}
484+
329485
private:
330486
size_t _size{0};
331487
};
332488

333489
struct TimeSeriesDictReferenceOutput final : TimeSeriesReferenceOutput {
334490
using TimeSeriesReferenceOutput::TimeSeriesReferenceOutput;
335491
static void register_with_nanobind(nb::module_ &m);
492+
493+
// Visitor support - Acyclic pattern (runtime dispatch)
494+
void accept(TimeSeriesVisitor& visitor) override {
495+
if (auto* typed_visitor = dynamic_cast<TimeSeriesOutputVisitor<TimeSeriesDictReferenceOutput>*>(&visitor)) {
496+
typed_visitor->visit(*this);
497+
}
498+
}
499+
500+
void accept(TimeSeriesVisitor& visitor) const override {
501+
if (auto* typed_visitor = dynamic_cast<ConstTimeSeriesOutputVisitor<TimeSeriesDictReferenceOutput>*>(&visitor)) {
502+
typed_visitor->visit(*this);
503+
}
504+
}
505+
506+
// CRTP visitor support (compile-time dispatch)
507+
template<typename Visitor>
508+
requires (!std::is_base_of_v<TimeSeriesVisitor, Visitor>)
509+
decltype(auto) accept(Visitor& visitor) {
510+
return visitor(*this);
511+
}
512+
513+
template<typename Visitor>
514+
requires (!std::is_base_of_v<TimeSeriesVisitor, Visitor>)
515+
decltype(auto) accept(Visitor& visitor) const {
516+
return visitor(*this);
517+
}
336518
};
337519

338520
struct TimeSeriesSetReferenceOutput final : TimeSeriesReferenceOutput {
339521
using TimeSeriesReferenceOutput::TimeSeriesReferenceOutput;
340522
static void register_with_nanobind(nb::module_ &m);
523+
524+
// Visitor support - Acyclic pattern (runtime dispatch)
525+
void accept(TimeSeriesVisitor& visitor) override {
526+
if (auto* typed_visitor = dynamic_cast<TimeSeriesOutputVisitor<TimeSeriesSetReferenceOutput>*>(&visitor)) {
527+
typed_visitor->visit(*this);
528+
}
529+
}
530+
531+
void accept(TimeSeriesVisitor& visitor) const override {
532+
if (auto* typed_visitor = dynamic_cast<ConstTimeSeriesOutputVisitor<TimeSeriesSetReferenceOutput>*>(&visitor)) {
533+
typed_visitor->visit(*this);
534+
}
535+
}
536+
537+
// CRTP visitor support (compile-time dispatch)
538+
template<typename Visitor>
539+
requires (!std::is_base_of_v<TimeSeriesVisitor, Visitor>)
540+
decltype(auto) accept(Visitor& visitor) {
541+
return visitor(*this);
542+
}
543+
544+
template<typename Visitor>
545+
requires (!std::is_base_of_v<TimeSeriesVisitor, Visitor>)
546+
decltype(auto) accept(Visitor& visitor) const {
547+
return visitor(*this);
548+
}
341549
};
342550

343551
struct TimeSeriesWindowReferenceOutput final : TimeSeriesReferenceOutput {
344552
using TimeSeriesReferenceOutput::TimeSeriesReferenceOutput;
345553
static void register_with_nanobind(nb::module_ &m);
554+
555+
// Visitor support - Acyclic pattern (runtime dispatch)
556+
void accept(TimeSeriesVisitor& visitor) override {
557+
if (auto* typed_visitor = dynamic_cast<TimeSeriesOutputVisitor<TimeSeriesWindowReferenceOutput>*>(&visitor)) {
558+
typed_visitor->visit(*this);
559+
}
560+
}
561+
562+
void accept(TimeSeriesVisitor& visitor) const override {
563+
if (auto* typed_visitor = dynamic_cast<ConstTimeSeriesOutputVisitor<TimeSeriesWindowReferenceOutput>*>(&visitor)) {
564+
typed_visitor->visit(*this);
565+
}
566+
}
567+
568+
// CRTP visitor support (compile-time dispatch)
569+
template<typename Visitor>
570+
requires (!std::is_base_of_v<TimeSeriesVisitor, Visitor>)
571+
decltype(auto) accept(Visitor& visitor) {
572+
return visitor(*this);
573+
}
574+
575+
template<typename Visitor>
576+
requires (!std::is_base_of_v<TimeSeriesVisitor, Visitor>)
577+
decltype(auto) accept(Visitor& visitor) const {
578+
return visitor(*this);
579+
}
346580
};
347581

348582
} // namespace hgraph

0 commit comments

Comments
 (0)