initial commit
This commit is contained in:
commit
4c7a851d22
1
.gitignore
vendored
Normal file
1
.gitignore
vendored
Normal file
|
@ -0,0 +1 @@
|
||||||
|
pipeline
|
9
Tupfile
Normal file
9
Tupfile
Normal file
|
@ -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
|
25
flake.lock
Normal file
25
flake.lock
Normal file
|
@ -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
|
||||||
|
}
|
17
flake.nix
Normal file
17
flake.nix
Normal file
|
@ -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
|
||||||
|
];
|
||||||
|
};
|
||||||
|
};
|
||||||
|
}
|
10
justfile
Normal file
10
justfile
Normal file
|
@ -0,0 +1,10 @@
|
||||||
|
exe := "./pipeline"
|
||||||
|
|
||||||
|
build:
|
||||||
|
tup
|
||||||
|
|
||||||
|
run: build
|
||||||
|
{{exe}}
|
||||||
|
|
||||||
|
debug: build
|
||||||
|
lldb --one-line run {{exe}}
|
152
main.cpp
Normal file
152
main.cpp
Normal file
|
@ -0,0 +1,152 @@
|
||||||
|
#include <iostream>
|
||||||
|
#include <list>
|
||||||
|
#include <optional>
|
||||||
|
#include <string>
|
||||||
|
#include <typeindex>
|
||||||
|
#include <typeinfo>
|
||||||
|
#include <unordered_map>
|
||||||
|
|
||||||
|
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<Connection>;
|
||||||
|
using ConnectionMap = std::unordered_map<std::string, std::list<Connection>>;
|
||||||
|
|
||||||
|
using SlotMap = std::unordered_map<std::string, std::type_index>;
|
||||||
|
|
||||||
|
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<std::type_index> out_type =
|
||||||
|
type_index_from_slot(name(), "output", outputs(), key_out);
|
||||||
|
if (!out_type) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::optional<std::type_index> in_type =
|
||||||
|
type_index_from_slot(in.name(), "input", in.inputs(), key_in);
|
||||||
|
if (!in_type) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::unordered_map<std::type_index, std::string> 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<std::type_index>
|
||||||
|
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<int *>(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<std::string *>(payload));
|
||||||
|
} else if (key == "in_int") {
|
||||||
|
this->in_int = *(reinterpret_cast<int *>(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;
|
||||||
|
}
|
Loading…
Reference in a new issue