feat: type-checked merge inputs - #10
Merged
Merged
Conversation
psyinf
added this pull request to stack #12
September 17, 2026 17:44
psyinf
force-pushed
the
feature/typed-merge-inputs
branch
from
September 17, 2026 18:13
1aa2a3b to
0354a55
Compare
A merge stage may now declare the type of each of its slots, so the DSL checks the edges of its fan-in group when the graph is built instead of failing with a std::bad_any_cast on the first message. - MergeStage / MergeSlotTypes (new MergeStage.hpp): how a merge declares its slot types; MergeInputs moved here from MergeFilter.hpp - AnyMessageFilter::mergeInputTypes() exposes them to the DSL; the adapter picks them up from any stage implementing MergeStage - TypedMergeFilter<Out, Ins...>: fixed arity, one type per slot, passed to merge() as std::optionals (empty = hole), no any_cast in user code - UniformMergeFilter<In, Out>: any number of slots of one type - registerTypedMergeFilter<Out, Ins...>: the same from a lambda - GraphPlan reports "slot N of 'X' expects ... but edge 'e' carries ..." and "stage 'X' takes N inputs but the group has M" - MergeFilter / registerMergeFilter declare no slot types and keep their unchecked behaviour The sample app gains a typed merge (Report), and README, EXAMPLE.md and the changelog document it.
psyinf
force-pushed
the
feature/typed-merge-inputs
branch
from
September 17, 2026 18:22
0354a55 to
54d07ec
Compare
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Stack (bottom → top)
GraphContextfinish()lifecycle hookSummary
A merge stage may now declare the type of each of its slots, so the DSL checks the edges of its fan-in group when the graph is built instead of failing with a
std::bad_any_caston the first message. This closes the only edges the library did not check:GraphPlanchecks every single-input edge, but a merge only declaredMergeInputs = std::vector<std::any>, so(msg, valid)and(valid, msg)both validated and ran — silently producing a different result when the slots share a type.What's new
MergeStage/MergeSlotTypes(newMergeStage.hpp) — how a merge declares its slot types.MergeInputsmoved here fromMergeFilter.hpp, which still provides it.AnyMessageFilter::mergeInputTypes()— exposes them to the DSL. The adapter picks them up from any stage implementingMergeStage, soMessageFilteritself is unchanged.TypedMergeFilter<Out, Ins...>— fixed arity, one type per slot.merge()receives the slots asstd::optionals (std::nullopt= a hole left by a dropped path), so user code needs noany_cast.UniformMergeFilter<In, Out>— any number of slots, all of one type: N variants of the same computation.registerTypedMergeFilter<Out, Ins...>(name, fn)— the same from a lambda, the typed counterpart ofregisterMergeFilter.New diagnostics, in the usual
line:columnstyle:Compatibility
Additive.
MergeFilter/registerMergeFilterdeclare no slot types, so their edges stay unchecked and existing graphs behave exactly as before.Notes
MessageFilter<MergeInputs, Out>registered viaFilterRegistrar) can also implementMergeStageto be checked.registerMergeFilter's combiner is copied into every instance, so captured state is shared between instances. That is now documented at the registration function; per-instance state is what the class-based merges above are for.Testing
New
MergeTests(9 cases): typed slots and holes, the lambda registration, uniform merges of 2 and 3 slots, a mis-wired group and a wrong slot count reported at build time, an untyped merge still accepted unchecked, per-instance state, and direct use outside a graph. The sample app gains a typed merge (Report) whose real output and diagnostics are what EXAMPLE.md shows. Full suite: 77/77 locally (MSVC debug, ASan).