commit 4c7a851d22848ec47667aec9fc1f0ff0f42f9d73 Author: Fabien Freling Date: Fri Sep 2 18:23:57 2022 +0200 initial commit diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..975ba4d --- /dev/null +++ b/.gitignore @@ -0,0 +1 @@ +pipeline diff --git a/Tupfile b/Tupfile new file mode 100644 index 0000000..ea49434 --- /dev/null +++ b/Tupfile @@ -0,0 +1,9 @@ +CPPFLAGS_ = -Wall -std=c++17 -fcolor-diagnostics -g -O1 +CPPFLAGS_ASAN = $(CPPFLAGS_) -fsanitize=address -fno-omit-frame-pointer +CPPFLAGS_THREAD = $(CPPFLAGS_) -fsanitize=thread +CPPFLAGS_MEM = $(CPPFLAGS_) -fsanitize=memory -fno-omit-frame-pointer +CPPFLAGS_UB = $(CPPFLAGS_) -fsanitize=undefined +CPPFLAGS = $(CPPFLAGS_) + +: foreach *.cpp |> clang++ -c %f -o %o $(CPPFLAGS) |> %B.o {objs} +: {objs} |> clang++ %f -o %o $(CPPFLAGS) |> ./pipeline diff --git a/flake.lock b/flake.lock new file mode 100644 index 0000000..6d89d83 --- /dev/null +++ b/flake.lock @@ -0,0 +1,25 @@ +{ + "nodes": { + "nixpkgs": { + "locked": { + "lastModified": 1661353537, + "narHash": "sha256-1E2IGPajOsrkR49mM5h55OtYnU0dGyre6gl60NXKITE=", + "owner": "NixOS", + "repo": "nixpkgs", + "rev": "0e304ff0d9db453a4b230e9386418fd974d5804a", + "type": "github" + }, + "original": { + "id": "nixpkgs", + "type": "indirect" + } + }, + "root": { + "inputs": { + "nixpkgs": "nixpkgs" + } + } + }, + "root": "root", + "version": 7 +} diff --git a/flake.nix b/flake.nix new file mode 100644 index 0000000..6983f7e --- /dev/null +++ b/flake.nix @@ -0,0 +1,17 @@ +{ + description = "graph-nodes flake"; + outputs = { self, nixpkgs }: + let + pkgs = nixpkgs.legacyPackages.x86_64-linux; + in { + devShell.x86_64-linux = with pkgs; + mkShell { + buildInputs = [ + clang + lldb + just + tup + ]; + }; + }; +} diff --git a/justfile b/justfile new file mode 100644 index 0000000..8afc191 --- /dev/null +++ b/justfile @@ -0,0 +1,10 @@ +exe := "./pipeline" + +build: + tup + +run: build + {{exe}} + +debug: build + lldb --one-line run {{exe}} diff --git a/main.cpp b/main.cpp new file mode 100644 index 0000000..f206b30 --- /dev/null +++ b/main.cpp @@ -0,0 +1,152 @@ +#include +#include +#include +#include +#include +#include +#include + +struct Slot { + void *payload; + std::type_info type_info; +}; + +class Node; + +struct Connection { + Node *src = nullptr; + std::string src_key; + Node *dst = nullptr; + std::string dst_key; +}; + +using ConnectionList = std::list; +using ConnectionMap = std::unordered_map>; + +using SlotMap = std::unordered_map; + +class Node { +public: + virtual const char *name() const = 0; + virtual const SlotMap &inputs() const = 0; + virtual const SlotMap &outputs() const = 0; + + ConnectionMap in_connections_; + ConnectionMap out_connections_; + + void attach(const std::string &key_out, Node &in, const std::string &key_in) { + std::optional out_type = + type_index_from_slot(name(), "output", outputs(), key_out); + if (!out_type) { + return; + } + + std::optional in_type = + type_index_from_slot(in.name(), "input", in.inputs(), key_in); + if (!in_type) { + return; + } + + std::unordered_map type_names = { + {typeid(int), "int"}, + {typeid(double), "double"}, + {typeid(std::string), "std::string"}}; + + if (out_type->hash_code() != in_type->hash_code()) { + std::cerr << "Incompatible type: \"" << type_names[*out_type] + << "\" (out) != \"" << type_names[*in_type] << "\" (in)\n"; + return; + } + + Connection c{ + .src = this, .src_key = key_out, .dst = &in, .dst_key = key_in}; + + // TODO: check for duplicates + in.in_connections_[key_in].push_back(c); + out_connections_[key_out].push_back(c); + }; + + void fire(const std::string &key, void *payload) const { + const auto &connections = out_connections_.at(key); + for (auto &c : connections) { + c.dst->incoming_payload(c.dst_key, payload); + } + } + + virtual void run() = 0; + virtual void incoming_payload(const std::string &key, void *payload) = 0; + +private: + std::optional + type_index_from_slot(const char *node_name, const char *slot_name, + const SlotMap &slots, const std::string &key) const { + try { + return slots.at(key); + } catch (std::out_of_range) { + std::cerr << "Node \"" << node_name << "\" does not have an " << slot_name + << " input slot named \"" << key << "\"\n"; + return {}; + } + }; +}; + +class Foo : public Node { +public: + virtual const char *name() const override { return "Foo"; }; + virtual const SlotMap &inputs() const override { return inputs_; }; + virtual const SlotMap &outputs() const override { return outputs_; }; + virtual void run() override { fire("str", (void *)"kikoo les pipous"); }; + virtual void incoming_payload(const std::string &key, + void *payload) override { + if (key == "integer") { + this->integer = *(reinterpret_cast(payload)); + } else { + std::cerr << "unsupported incoming payload: " << key << "\n"; + } + }; + +private: + static inline const SlotMap inputs_ = {{"integer", typeid(int)}}; + static inline const SlotMap outputs_ = {{"str", typeid(std::string)}}; + + int integer; +}; + +class Bar : public Node { +public: + virtual const char *name() const override { return "Bar"; }; + virtual const SlotMap &inputs() const override { return inputs_; }; + virtual const SlotMap &outputs() const override { return outputs_; }; + virtual void run() override { std::cout << in_str << "\n"; }; + virtual void incoming_payload(const std::string &key, + void *payload) override { + if (key == "in_str") { + this->in_str = *(reinterpret_cast(payload)); + } else if (key == "in_int") { + this->in_int = *(reinterpret_cast(payload)); + } else { + std::cerr << "unsupported incoming payload: " << key << "\n"; + } + }; + +private: + static inline const SlotMap inputs_ = {{"in_str", typeid(std::string)}, + {"in_int", typeid(int)}}; + static inline const SlotMap outputs_ = {{"final", typeid(std::string)}}; + + std::string in_str; + int in_int; +}; + +int main(int argc, char *argv[]) { + auto root = Foo(); + auto end = Bar(); + + root.attach("invalid", end, "xxx"); + root.attach("str", end, "in_int"); + root.attach("str", end, "in_str"); + + root.run(); + + return 0; +}