add Node/DLL

This commit is contained in:
falsycat 2022-12-16 23:00:22 +09:00
parent ee718b09f4
commit 66a2b4f552
4 changed files with 502 additions and 0 deletions

View File

@ -83,6 +83,7 @@ target_sources(nf7
common/context_owner.hh
common/dir.hh
common/dir_item.hh
common/dll.hh
common/factory.hh
common/file_base.hh
common/font_queue.hh
@ -124,6 +125,7 @@ target_sources(nf7
common/mutex.hh
common/nfile.hh
common/nfile_watcher.hh
common/node.h
common/node.hh
common/node_link_store.hh
common/node_root_lambda.hh
@ -160,6 +162,7 @@ target_sources(nf7
file/luajit_context.cc
file/luajit_node.cc
file/node_comment.cc
file/node_dll.cc
file/node_exprtk.cc
file/node_mutex.cc
file/node_network.cc

72
common/dll.hh Normal file
View File

@ -0,0 +1,72 @@
#pragma once
#include <memory>
#include <string_view>
#if defined(__unix__)
# include <dlfcn.h>
#endif
#include "nf7.hh"
#include "common/future.hh"
namespace nf7 {
struct DLL final {
public:
class Exception : public nf7::Exception {
public:
using nf7::Exception::Exception;
};
static nf7::Future<std::shared_ptr<DLL>> Create(
const std::shared_ptr<nf7::Context>& ctx, const std::string& p) noexcept {
nf7::Future<std::shared_ptr<DLL>>::Promise pro {ctx};
ctx->env().ExecAsync(ctx, [p, pro]() mutable {
pro.Wrap([&]() { return std::make_shared<nf7::DLL>(p.c_str()); });
});
return pro.future();
}
explicit DLL(const char* p) : ptr_(Open(p)) {
}
~DLL() noexcept {
Close(ptr_);
}
DLL(const DLL&) = delete;
DLL(DLL&&) = delete;
DLL& operator=(const DLL&) = delete;
DLL& operator=(DLL&&) = delete;
template <typename R, typename... Args>
std::function<R(Args...)> Resolve(const char* name) {
return reinterpret_cast<R (*)(Args...)>(Resolve(ptr_, name));
}
private:
void* ptr_;
# if defined(__unix__)
static void* Open(const char* p) {
if (auto ret = dlopen(p, RTLD_LAZY | RTLD_LOCAL)) {
return ret;
}
throw DLL::Exception {dlerror()};
}
static void Close(void* ptr) noexcept {
dlclose(ptr);
}
static void* Resolve(void* ptr, const char* name) {
if (auto ret = dlsym(ptr, name)) {
return ret;
}
throw DLL::Exception {dlerror()};
}
# else
# error "unknown OS"
# endif
};
} // namespace nf7

96
common/node.h Normal file
View File

@ -0,0 +1,96 @@
#pragma once
#include <stdbool.h>
#include <stddef.h>
#include <stdint.h>
// DLL must have the following definition:
// `void nf7_init(nf7_init_t*) { }`
#ifdef __cplusplus
extern "C" {
#endif
typedef struct nf7_vtable_t nf7_vtable_t;
typedef struct nf7_init_t nf7_init_t;
typedef struct nf7_ctx_t nf7_ctx_t;
typedef struct nf7_node_t nf7_node_t;
typedef struct nf7_node_msg_t nf7_node_msg_t;
typedef struct nf7_value_t nf7_value_t;
typedef struct nf7_vtable_t {
// ---- entrypoint methods ----
struct {
void (*register_node)(nf7_init_t*, const nf7_node_t*);
} init;
// ---- context methods ----
struct {
// thread-safe
void (*exec_async)(nf7_ctx_t*, void (*f)(nf7_ctx_t*));
} ctx;
// ---- value accessor/mutator ----
struct {
uint8_t (*get_type)(nf7_value_t*);
# define NF7_PULSE UINT8_C(0)
# define NF7_BOOLEAN UINT8_C(1)
# define NF7_INTEGER UINT8_C(2)
# define NF7_SCALAR UINT8_C(3)
# define NF7_STRING UINT8_C(4)
# define NF7_VECTOR UINT8_C(5)
# define NF7_TUPLE UINT8_C(6)
# define NF7_UNKNOWN UINT8_MAX
// A result of value_get_type should be checked before calling the followings.
bool* (*get_boolean)(nf7_value_t*);
int64_t* (*get_integer)(nf7_value_t*);
double* (*get_scalar) (nf7_value_t*);
char* (*get_string) (nf7_value_t*, size_t*);
const uint8_t* (*get_vector) (nf7_value_t*, size_t*);
uint8_t* (*get_mvector)(nf7_value_t*, size_t*);
nf7_value_t* (*get_tuple) (nf7_value_t*, const char*);
void (*set_pulse) (nf7_value_t*);
void (*set_boolean)(nf7_value_t*, bool);
void (*set_integer)(nf7_value_t*, int64_t);
void (*set_scalar) (nf7_value_t*, double);
char* (*set_string) (nf7_value_t*, size_t);
uint8_t* (*set_vector) (nf7_value_t*, size_t);
void (*set_tuple) (nf7_value_t*, const char**, size_t);
} value;
} nf7_vtable_t;
typedef struct nf7_init_t {
const nf7_vtable_t* vtable;
} nf7_init_t;
typedef struct nf7_ctx_t {
void* ptr;
} nf7_ctx_t;
typedef struct nf7_node_t {
const char* name;
const char* desc;
const char** inputs; // null terminated string array
const char** outputs; // null terminated string array
void* (*init)(); // returned pointer will be set to ctx.ptr
void (*deinit)(void*);
void (*handle)(const nf7_node_msg_t*);
} nf7_node_t;
typedef struct nf7_node_msg_t {
const char* name;
nf7_value_t* value;
nf7_ctx_t* ctx;
void* ptr;
} nf7_node_msg_t;
#ifdef __cplusplus
}
#endif

331
file/node_dll.cc Normal file
View File

@ -0,0 +1,331 @@
#include <filesystem>
#include <memory>
#include <optional>
#include <typeinfo>
#include <utility>
#include <yas/serialize.hpp>
#include <yas/types/utility/usertype.hpp>
#include "nf7.hh"
#include "common/dir.hh"
#include "common/dir_item.hh"
#include "common/dll.hh"
#include "common/file_base.hh"
#include "common/future.hh"
#include "common/generic_context.hh"
#include "common/generic_dir.hh"
#include "common/generic_memento.hh"
#include "common/generic_type_info.hh"
#include "common/gui.hh"
#include "common/gui_dnd.hh"
#include "common/life.hh"
#include "common/logger_ref.hh"
#include "common/node.hh"
#include "common/ptr_selector.hh"
#include "common/yas_std_filesystem.hh"
#include "common/node.h"
namespace nf7 {
namespace {
namespace adaptor {
struct InitParam {
nf7_init_t base;
std::shared_ptr<nf7::DLL> dll;
std::vector<const nf7_node_t*> nodes;
};
struct NodeMsg {
nf7_node_msg_t base;
};
} // namespace adaptor
static const nf7_vtable_t kVtable = {
.init = {
.register_node = [](nf7_init_t* ptr, const nf7_node_t* n) {
auto& p = *reinterpret_cast<adaptor::InitParam*>(ptr);
p.nodes.push_back(n);
},
},
.ctx = {
.exec_async = nullptr, // TODO
},
.value = {
.get_type = [](nf7_value_t* vptr) {
struct Visitor {
uint8_t operator()(nf7::Value::Pulse) { return NF7_PULSE; }
uint8_t operator()(nf7::Value::Boolean) { return NF7_BOOLEAN; }
uint8_t operator()(nf7::Value::Integer) { return NF7_INTEGER; }
uint8_t operator()(nf7::Value::Scalar) { return NF7_SCALAR; }
uint8_t operator()(const nf7::Value::String&) { return NF7_STRING; }
uint8_t operator()(const nf7::Value::ConstVector&) { return NF7_VECTOR; }
uint8_t operator()(const nf7::Value::ConstTuple&) { return NF7_TUPLE; }
uint8_t operator()(const nf7::Value::DataPtr&) { return NF7_UNKNOWN; }
};
const auto& v = *reinterpret_cast<nf7::Value*>(vptr);
return std::visit(Visitor {}, v.value());
},
.get_boolean = [](nf7_value_t* vptr) {
auto& v = *reinterpret_cast<nf7::Value*>(vptr);
return v.isBoolean()? &v.boolean(): nullptr;
},
.get_integer = [](nf7_value_t* vptr) {
auto& v = *reinterpret_cast<nf7::Value*>(vptr);
return v.isInteger()? &v.integer(): nullptr;
},
.get_scalar = [](nf7_value_t* vptr) {
auto& v = *reinterpret_cast<nf7::Value*>(vptr);
return v.isScalar()? &v.scalar(): nullptr;
},
.get_string = [](nf7_value_t* vptr, size_t* n) -> char* {
auto& v = *reinterpret_cast<nf7::Value*>(vptr);
if (v.isString()) {
auto& str = v.string();
*n = str.size();
return str.data();
} else {
return nullptr;
}
},
// TODO
.get_vector = nullptr,
.get_mvector = nullptr,
.get_tuple = nullptr,
.set_pulse = nullptr,
.set_boolean = nullptr,
.set_integer = nullptr,
.set_scalar = nullptr,
.set_string = nullptr,
.set_vector = nullptr,
.set_tuple = nullptr,
},
};
class Loader final : public nf7::FileBase, public nf7::DirItem {
public:
static inline const nf7::GenericTypeInfo<Loader> kType = {
"Node/DLL", {"nf7::DirItem",}, "loads a dynamic link library and defines new Node"};
class Node;
struct Data {
std::filesystem::path npath;
void serialize(auto& ar) {
ar(npath);
}
};
Loader(nf7::Env& env, Data&& d = {}) noexcept :
nf7::FileBase(kType, env),
nf7::DirItem(nf7::DirItem::kMenu |
nf7::DirItem::kTree),
life_(*this),
log_(std::make_shared<nf7::LoggerRef>(*this)),
mem_(*this, std::move(d)),
dir_(*this) {
mem_.onCommit = mem_.onRestore = [this]() { Open(); };
}
Loader(nf7::Deserializer& ar) noexcept : Loader(ar.env()) {
ar(mem_.data());
}
void Serialize(nf7::Serializer& ar) const noexcept override {
ar(mem_.data());
}
std::unique_ptr<nf7::File> Clone(nf7::Env& env) const noexcept override {
return std::make_unique<Loader>(env, Data {mem_.data()});
}
void UpdateMenu() noexcept override;
void UpdateTree() noexcept override;
nf7::File::Interface* interface(const std::type_info& t) noexcept override {
return nf7::InterfaceSelector<nf7::DirItem>(t).Select(this);
}
private:
nf7::Life<Loader> life_;
std::shared_ptr<nf7::LoggerRef> log_;
nf7::GenericMemento<Data> mem_;
nf7::GenericDir dir_;
std::optional<nf7::Future<adaptor::InitParam>> open_fu_;
nf7::Future<adaptor::InitParam> Open() noexcept;
};
class Loader::Node final : public nf7::File, public nf7::DirItem, public nf7::Node {
public:
static inline const nf7::GenericTypeInfo<Loader::Node> kType = {
"Node/DLL/Node", {"nf7::DirItem",}, "Node defined by a dynamic link library"};
class Lambda;
Node(nf7::Env& env,
const std::shared_ptr<nf7::DLL>& dll,
const nf7_node_t& meta) noexcept :
nf7::File(kType, env),
nf7::DirItem(nf7::DirItem::kTooltip),
nf7::Node(nf7::Node::kNone),
life_(*this),
dll_(dll), meta_(meta) {
}
void Serialize(nf7::Serializer&) const noexcept override {
}
std::unique_ptr<nf7::File> Clone(nf7::Env& env) const noexcept override {
return std::make_unique<Loader::Node>(env, dll_, meta_);
}
std::shared_ptr<nf7::Node::Lambda> CreateLambda(
const std::shared_ptr<nf7::Node::Lambda>& parent) noexcept override;
nf7::Node::Meta GetMeta() const noexcept override {
return {GetSockList(meta_.inputs), GetSockList(meta_.outputs)};
}
nf7::File::Interface* interface(const std::type_info& t) noexcept override {
return nf7::InterfaceSelector<nf7::DirItem, nf7::Node>(t).Select(this);
}
private:
nf7::Life<Loader::Node> life_;
std::shared_ptr<nf7::DLL> dll_;
const nf7_node_t& meta_;
static std::vector<std::string> GetSockList(const char** arr) noexcept {
std::vector<std::string> ret;
auto itr = arr;
while (*itr) ++itr;
ret.reserve(static_cast<size_t>(itr-arr));
itr = arr;
while (*itr) ret.push_back(*(itr++));
return ret;
}
};
class Loader::Node::Lambda final : public nf7::Node::Lambda {
public:
Lambda(Loader::Node& f, const std::shared_ptr<nf7::Node::Lambda>& parent) noexcept :
nf7::Node::Lambda(f, parent),
dll_(f.dll_), meta_(f.meta_), ptr_(meta_.init()) {
}
~Lambda() noexcept {
meta_.deinit(ptr_);
}
void Handle(const nf7::Node::Lambda::Msg& in) noexcept override {
nf7::Value v = in.value;
const adaptor::NodeMsg msg = {
.base = {
.name = in.name.c_str(),
.value = reinterpret_cast<nf7_value_t*>(&v),
.ctx = reinterpret_cast<nf7_ctx_t*>(this),
.ptr = ptr_,
},
};
meta_.handle(&msg.base);
}
private:
std::shared_ptr<nf7::DLL> dll_;
const nf7_node_t& meta_;
void* ptr_;
};
std::shared_ptr<nf7::Node::Lambda> Loader::Node::CreateLambda(
const std::shared_ptr<nf7::Node::Lambda>& parent) noexcept {
return std::make_shared<Loader::Node::Lambda>(*this, parent);
}
nf7::Future<adaptor::InitParam> Loader::Open() noexcept {
if (open_fu_ && open_fu_->yet()) return *open_fu_;
const auto npath = env().npath() / mem_->npath;
const auto ctx = std::make_shared<nf7::GenericContext>(*this, "loading DLL");
nf7::Future<adaptor::InitParam>::Promise pro {ctx};
nf7::DLL::Create(ctx, env().npath() / mem_->npath).
Chain(pro, [](auto& dll) {
auto f = dll->template Resolve<void, const nf7_init_t*>("nf7_init");
adaptor::InitParam p = {
.base = {
.vtable = &kVtable,
},
.dll = dll,
.nodes = {},
};
f(&p.base);
return p;
});
open_fu_ = pro.future();
open_fu_->ThenIf(ctx, [this, f = life_.ref()](auto& p) {
if (!f) return;
dir_.Clear();
for (auto meta : p.nodes) {
// TODO: validate meta
dir_.Add(meta->name, std::make_unique<Loader::Node>(env(), p.dll, *meta));
}
}).
Catch<nf7::Exception>(ctx, [log = log_](auto&) {
log->Warn("failed to load dynamic library");
});
return *open_fu_;
}
void Loader::UpdateMenu() noexcept {
if (ImGui::BeginMenu("config")) {
if (nf7::gui::NPathButton("npath", mem_->npath, env())) {
mem_.Commit();
}
ImGui::EndMenu();
}
}
void Loader::UpdateTree() noexcept {
for (const auto& item : dir_.items()) {
const auto& name = item.first;
auto& file = *item.second;
constexpr auto kFlags =
ImGuiTreeNodeFlags_SpanFullWidth |
ImGuiTreeNodeFlags_NoTreePushOnOpen |
ImGuiTreeNodeFlags_Leaf;
ImGui::TreeNodeEx(item.second.get(), kFlags, "%s", name.c_str());
// tooltip
if (ImGui::IsItemHovered()) {
ImGui::BeginTooltip();
nf7::gui::FileTooltip(file);
ImGui::EndTooltip();
}
// dnd source
if (ImGui::BeginDragDropSource()) {
gui::dnd::Send(gui::dnd::kFilePath, file.abspath());
ImGui::TextUnformatted(file.type().name().c_str());
ImGui::SameLine();
ImGui::TextDisabled(file.abspath().Stringify().c_str());
ImGui::EndDragDropSource();
}
// context menu
if (ImGui::BeginPopupContextItem()) {
nf7::gui::FileMenuItems(file);
ImGui::EndPopup();
}
}
}
}
} // namespace nf7