diff --git a/include/cubescript/cubescript.hh b/include/cubescript/cubescript.hh index 2c11a35..c525240 100644 --- a/include/cubescript/cubescript.hh +++ b/include/cubescript/cubescript.hh @@ -2,12 +2,15 @@ #define LIBCUBESCRIPT_CUBESCRIPT_HH #include +#include #include #include #include #include #include +#include #include +#include #include "cubescript_conf.hh" @@ -42,6 +45,216 @@ struct cs_internal_error: std::runtime_error { using std::runtime_error::runtime_error; }; +template +struct cs_callable { +private: + struct base { + base(base const &); + base &operator=(base const &); + + public: + base() {} + virtual ~base() {} + virtual void move_to(base *) = 0; + virtual R operator()(A &&...args) = 0; + }; + + template + struct store: base { + explicit store(F &&f): p_stor{std::move(f)} {} + + virtual void move_to(base *p) { + ::new (p) store{std::move(p_stor)}; + } + + virtual R operator()(A &&...args) { + return std::invoke( + *reinterpret_cast(&p_stor), std::forward(args)... + ); + } + + private: + F p_stor; + }; + + using alloc_f = void *(*)(void *, void *, std::size_t, std::size_t); + + struct f_alloc { + alloc_f af; + void *ud; + size_t asize; + }; + + std::aligned_storage_t p_stor; + base *p_func; + + static inline base *as_base(void *p) { + return static_cast(p); + } + + template + static inline bool f_not_null(T const &) { return true; } + + template + static inline bool f_not_null(T *p) { return !!p; } + + template + static inline bool f_not_null(CR C::*p) { return !!p; } + + template + static inline bool f_not_null(cs_callable const &f) { return !!f; } + + bool small_storage() { + return (static_cast(p_func) == &p_stor); + } + + void cleanup() { + if (!p_func) { + return; + } + p_func->~base(); + if (!small_storage()) { + auto &ad = *reinterpret_cast(&p_stor); + ad.af(ad.ud, p_func, ad.asize, 0); + } + } + +public: + cs_callable() noexcept: p_func{nullptr} {} + cs_callable(std::nullptr_t) noexcept: p_func{nullptr} {} + cs_callable(std::nullptr_t, alloc_f, void *) noexcept: p_func{nullptr} {} + + cs_callable(cs_callable &&f) noexcept { + if (!f.p_func) { + p_func = nullptr; + } else if (f.small_storage()) { + p_func = as_base(&p_stor); + f.p_func->move_to(p_func); + } else { + p_func = f.p_func; + f.p_func = nullptr; + } + } + + template + cs_callable(F func, alloc_f af, void *ud) { + if (!f_not_null(func)) { + return; + } + if constexpr (sizeof(store) <= sizeof(p_stor)) { + auto *p = static_cast(&p_stor); + p_func = ::new (p) store{std::move(func)}; + } else { + auto &ad = *reinterpret_cast(&p_stor); + ad.af = af; + ad.ud = ud; + ad.asize = sizeof(store); + p_func = static_cast *>( + af(ud, nullptr, 0, sizeof(store)) + ); + try { + new (p_func) store{std::move(func)}; + } catch (...) { + af(ud, p_func, sizeof(store), 0); + throw; + } + } + } + + cs_callable &operator=(cs_callable const &) = delete; + + cs_callable &operator=(cs_callable &&f) noexcept { + cleanup(); + if (f.p_func == nullptr) { + p_func = nullptr; + } else if (f.small_storage()) { + p_func = as_base(&p_stor); + f.p_func->move_to(p_func); + } else { + p_func = f.p_func; + f.p_func = nullptr; + } + return *this; + } + + cs_callable &operator=(std::nullptr_t) noexcept { + cleanup(); + p_func = nullptr; + return *this; + } + + template + cs_callable &operator=(F &&func) { + cs_callable{std::forward(func)}.swap(*this); + return *this; + } + + ~cs_callable() { + cleanup(); + } + + void swap(cs_callable &f) noexcept { + std::aligned_storage_t tmp_stor; + if (small_storage() && f.small_storage()) { + auto *t = as_base(&tmp_stor); + p_func->move_to(t); + p_func->~base(); + p_func = nullptr; + f.p_func->move_to(as_base(&p_stor)); + f.p_func->~base(); + f.p_func = nullptr; + p_func = as_base(&p_stor); + t->move_to(as_base(&f.p_stor)); + t->~base(); + f.p_func = as_base(&f.p_stor); + } else if (small_storage()) { + /* copy allocator address/size */ + memcpy(&tmp_stor, &f.p_stor, sizeof(tmp_stor)); + p_func->move_to(as_base(&f.p_stor)); + p_func->~base(); + p_func = f.p_func; + f.p_func = as_base(&f.p_stor); + memcpy(&p_stor, &tmp_stor, sizeof(tmp_stor)); + } else if (f.small_storage()) { + /* copy allocator address/size */ + memcpy(&tmp_stor, &p_stor, sizeof(tmp_stor)); + f.p_func->move_to(as_base(&p_stor)); + f.p_func->~base(); + f.p_func = p_func; + p_func = as_base(&p_stor); + memcpy(&f.p_stor, &tmp_stor, sizeof(tmp_stor)); + } else { + /* copy allocator address/size */ + memcpy(&tmp_stor, &p_stor, sizeof(tmp_stor)); + memcpy(&p_stor, &f.p_stor, sizeof(tmp_stor)); + memcpy(&f.p_stor, &tmp_stor, sizeof(tmp_stor)); + std::swap(p_func, f.p_func); + } + } + + explicit operator bool() const noexcept { + return !!p_func; + } + + R operator()(A ...args) { + return (*p_func)(std::forward(args)...); + } +}; + +using cs_alloc_cb = void *(*)(void *, void *, size_t, size_t); + +struct cs_state; +struct cs_ident; +struct cs_value; +struct cs_var; + +using cs_hook_cb = cs_callable; +using cs_var_cb = cs_callable; +using cs_vprint_cb = cs_callable; +using cs_command_cb = cs_callable< + void, cs_state &, std::span, cs_value & +>; + enum { CS_IDF_PERSIST = 1 << 0, CS_IDF_OVERRIDE = 1 << 1, @@ -53,8 +266,6 @@ enum { }; struct cs_bcode; -struct cs_value; -struct cs_state; struct cs_shared_state; struct cs_ident_impl; @@ -355,11 +566,21 @@ struct LIBCUBESCRIPT_EXPORT cs_state { cs_state new_thread(); - cs_hook_cb set_call_hook(cs_hook_cb func); + template + cs_hook_cb set_call_hook(F &&f) { + return std::move(set_call_hook( + cs_hook_cb{std::forward(f), callable_alloc, this} + )); + } cs_hook_cb const &get_call_hook() const; cs_hook_cb &get_call_hook(); - cs_vprint_cb set_var_printer(cs_vprint_cb func); + template + cs_vprint_cb set_var_printer(F &&f) { + return std::move(set_var_printer( + cs_vprint_cb{std::forward(f), callable_alloc, this} + )); + } cs_vprint_cb const &get_var_printer() const; void init_libs(int libs = CS_LIB_ALL); @@ -370,22 +591,58 @@ struct LIBCUBESCRIPT_EXPORT cs_state { cs_ident *new_ident(std::string_view name, int flags = CS_IDF_UNKNOWN); cs_ident *force_ident(cs_value &v); + template cs_ivar *new_ivar( - std::string_view n, cs_int m, cs_int x, cs_int v, - cs_var_cb f = cs_var_cb(), int flags = 0 - ); - cs_fvar *new_fvar( - std::string_view n, cs_float m, cs_float x, cs_float v, - cs_var_cb f = cs_var_cb(), int flags = 0 - ); - cs_svar *new_svar( - std::string_view n, std::string_view v, - cs_var_cb f = cs_var_cb(), int flags = 0 - ); + std::string_view name, cs_int m, cs_int x, cs_int v, + F &&f, int flags = 0 + ) { + return new_ivar( + name, m, x, v, + cs_var_cb{std::forward(f), callable_alloc, this}, flags + ); + } + cs_ivar *new_ivar(std::string_view name, cs_int m, cs_int x, cs_int v) { + return new_ivar(name, m, x, v, cs_var_cb{}, 0); + } + template + cs_fvar *new_fvar( + std::string_view name, cs_float m, cs_float x, cs_float v, + F &&f, int flags = 0 + ) { + return new_fvar( + name, m, x, v, + cs_var_cb{std::forward(f), callable_alloc, this}, flags + ); + } + cs_fvar *new_fvar( + std::string_view name, cs_float m, cs_float x, cs_float v + ) { + return new_fvar(name, m, x, v, cs_var_cb{}, 0); + } + + template + cs_svar *new_svar( + std::string_view name, std::string_view v, F &&f, int flags = 0 + ) { + return new_svar( + name, v, + cs_var_cb{std::forward(f), callable_alloc, this}, flags + ); + } + cs_svar *new_svar(std::string_view name, std::string_view v) { + return new_svar(name, v, cs_var_cb{}, 0); + } + + template cs_command *new_command( - std::string_view name, std::string_view args, cs_command_cb func - ); + std::string_view name, std::string_view args, F &&f + ) { + return new_command( + name, args, + cs_command_cb{std::forward(f), callable_alloc, this} + ); + } cs_ident *get_ident(std::string_view name); cs_alias *get_alias(std::string_view name); @@ -448,11 +705,36 @@ struct LIBCUBESCRIPT_EXPORT cs_state { void print_var(cs_var const &v) const; private: + cs_hook_cb set_call_hook(cs_hook_cb func); + cs_vprint_cb set_var_printer(cs_vprint_cb func); + + cs_ivar *new_ivar( + std::string_view n, cs_int m, cs_int x, cs_int v, + cs_var_cb f, int flags + ); + cs_fvar *new_fvar( + std::string_view n, cs_float m, cs_float x, cs_float v, + cs_var_cb f, int flags + ); + cs_svar *new_svar( + std::string_view n, std::string_view v, cs_var_cb f, int flags + ); + + cs_command *new_command( + std::string_view name, std::string_view args, cs_command_cb func + ); + + static void *callable_alloc( + void *data, void *p, std::size_t os, std::size_t ns + ) { + return static_cast(data)->alloc(p, os, ns); + } + LIBCUBESCRIPT_LOCAL cs_state(cs_shared_state *s); cs_ident *add_ident(cs_ident *id, cs_ident_impl *impl); - LIBCUBESCRIPT_LOCAL void *alloc(void *ptr, size_t olds, size_t news); + void *alloc(void *ptr, size_t olds, size_t news); cs_gen_state *p_pstate = nullptr; void *p_errbuf = nullptr; diff --git a/include/cubescript/cubescript_conf.hh b/include/cubescript/cubescript_conf.hh index 67cc7f7..1e1f970 100644 --- a/include/cubescript/cubescript_conf.hh +++ b/include/cubescript/cubescript_conf.hh @@ -1,40 +1,13 @@ #ifndef LIBCUBESCRIPT_CUBESCRIPT_CONF_HH #define LIBCUBESCRIPT_CUBESCRIPT_CONF_HH -#include -#include - -/* do not modify */ -namespace cscript { - struct cs_state; - struct cs_ident; - struct cs_value; - struct cs_var; -} - -/* configurable section */ namespace cscript { using cs_int = int; using cs_float = float; - /* probably don't want to change these, but if you use a custom allocation - * function for your state, keep in mind potential heap allocations in - * these are not handled by it (as std::function has no allocator support) - * - * normally std::function is optimized not to do allocations for small - * objects, so as long as you don't pass a lambda that captures by copy - * or move or something similar, you should be fine - but if you really - * need to make sure, override this with your own type - */ - using cs_var_cb = std::function; - using cs_vprint_cb = std::function; - using cs_command_cb = std::function, cs_value &)>; - using cs_hook_cb = std::function; - using cs_alloc_cb = void *(*)(void *, void *, size_t, size_t); - constexpr auto const CS_INT_FORMAT = "%d"; constexpr auto const CS_FLOAT_FORMAT = "%.7g"; constexpr auto const CS_ROUND_FLOAT_FORMAT = "%.1f"; } /* namespace cscript */ -#endif /* LIBCUBESCRIPT_CUBESCRIPT_CONF_HH */ \ No newline at end of file +#endif /* LIBCUBESCRIPT_CUBESCRIPT_CONF_HH */ diff --git a/src/cs_util.hh b/src/cs_util.hh index d23b0dc..bc7ac83 100644 --- a/src/cs_util.hh +++ b/src/cs_util.hh @@ -147,7 +147,7 @@ struct cs_shared_state { allocf{af}, aptr{data}, idents{allocator_type{this}}, identmap{allocator_type{this}}, - varprintf{[](auto &, auto &) {}}, + varprintf{}, strman{create(this)} {} diff --git a/src/cubescript.cc b/src/cubescript.cc index 597b5a4..8d959ef 100644 --- a/src/cubescript.cc +++ b/src/cubescript.cc @@ -458,7 +458,7 @@ LIBCUBESCRIPT_EXPORT cs_vprint_cb const &cs_state::get_var_printer() const { return p_state->varprintf; } -void *cs_state::alloc(void *ptr, size_t os, size_t ns) { +LIBCUBESCRIPT_EXPORT void *cs_state::alloc(void *ptr, size_t os, size_t ns) { return p_state->alloc(ptr, os, ns); } @@ -668,7 +668,9 @@ LIBCUBESCRIPT_EXPORT void cs_state::set_alias(std::string_view name, cs_value v) } LIBCUBESCRIPT_EXPORT void cs_state::print_var(cs_var const &v) const { - p_state->varprintf(*this, v); + if (p_state->varprintf) { + p_state->varprintf(*this, v); + } } LIBCUBESCRIPT_EXPORT cs_value cs_alias::get_value() const {