Reputation: 423
I'm quite new to C++, but I've been trying to diversify my skillset a bit during the lockdown. I'm attempting to write a node-based sound processing tool using SFML. I want to have a struct to contain all of my nodes' inputs and outputs. However, I obviously can't connect any type together. I'm looking to do something similar to what Blender does with its node types. The following screen recording shows what I mean: the green output type is incompatible with other input types.
I thought that a struct NodeInOut
could be a useful solution to this problem: I'd have a type that I assign whenever I create a new Node, and in the logic for connecting Nodes together I make sure that incompatible types aren't connectable. However, I'll need to pass in a type whenever I create a new Node definition.
My idea for the Node class is that it would be structured a bit like this hastily made diagram.
Does anyone have an idea on how to do this, or how to structure it differently such that this isn't an issue?
I haven't written any code yet besides the SFML boilerplate stuff.
Upvotes: 4
Views: 122
Reputation: 84
As discussed in the comments below HolyBlackCat's answer, here is my alternative:
Each nodes are a member of the (final, not virtual) Node
class
This Node
class has an std::vector<Input>
and an std::vector<Output>
, which represents each input and output of the node (they could probably be made const if you try hard enough)
You add a private variable called update
which has type std::function<void(Node &)>
or equivalent (so you could have lambdas, for example), which is a user-defined update method to set the outputs depending on the inputs, and a public function called Update
, which is a function that calls the update
of this node and the Update
of the following nodes (since they need to update too)
Then for the Output
s you need to have a function to set the value (maybe a templated function? Then you check if the type is correct and get a compatible type, maybe use std::variant
s to store the value), and the list of all Input
s connected to it (so an std::vector<Node*, unsigned>
maybe? Or you can also replace the Node*
by a std::weak_ptr<Node>
)
As for the inputs you need to be able to connect them to one Output
so either an Output*
or an std::weak_ptr<Output>
should work, but since you need to be able to have constants you can either:
std::variant
to store the default value, orNode
s with no input and one output for each dangling inputs, orNode
for each visible ones, which has no input and as many outputs as there are inputs for the target Node (and connect them one-to-one).This is maybe (probably) not the best solution, but at least it should work.
Upvotes: 1
Reputation: 96166
If I understand the question correctly, something like this should work:
Specific nodes inherit from an abstract class Node
.
class Node
contains pure virtual functions to list the input/output points:
virtual std::size_t InputCount() const = 0;
virtual const Input &GetInput(std::size_t i) const = 0;
Input &GetInput(std::size_t i) {return const_cast<Input &>(std::as_const(*this).GetInput(i));}
virtual std::size_t OutputCount() const = 0;
virtual const Output &GetOutput(std::size_t i) const = 0;
Output &GetOutput(std::size_t i) {return const_cast<Output &>(std::as_const(*this).GetOutput(i));}
And probably something like virtual void Update() = 0;
, DrawGui
, etc.
Classes derived from Node
have Input
and Output
objects as members, and override GetInput
and GetOutput
to return those members.
(Bonus: you might be able to do generate the functions automatically using a reflection library such as magic_get
.)
class Input
will have to somehow point to the Output
it's connected to, probably by storing a pointer (std::weak_ptr
?) to Node
owning the connected Output
, along with the index of that Output
in the node.
class Output
should at least contain float value;
(or something else, depending on what values you want to support). Maybe you will want to store a list of connected Input
s in there (where each element would be a pointer to Node
plus the index of the Input
in it).
Upvotes: 1
Reputation: 26066
Yes, you can have a "type of types".
However, notice the user connects nodes at runtime. Thus this is a runtime problem, not something you can solve at compile time.
In other words, simply use runtime values that contain whatever types your nodes accept/send and compare them as needed.
Upvotes: 2