diff --git a/cmake/avendish.max.cmake b/cmake/avendish.max.cmake index 6fd81ad..c4150d3 100644 --- a/cmake/avendish.max.cmake +++ b/cmake/avendish.max.cmake @@ -51,7 +51,7 @@ target_compile_definitions( C74_USE_STRICT_TYPES=1 ) -target_include_directories(maxmsp_commonsyms PRIVATE +target_include_directories(maxmsp_commonsyms SYSTEM PRIVATE "${MAXSDK_MAX_INCLUDE_DIR}" "${MAXSDK_MSP_INCLUDE_DIR}" "${MAXSDK_JIT_INCLUDE_DIR}" @@ -132,7 +132,7 @@ function(avnd_make_max) C74_USE_STRICT_TYPES=1 ) - target_include_directories(${AVND_FX_TARGET} PRIVATE + target_include_directories(${AVND_FX_TARGET} SYSTEM PRIVATE "${MAXSDK_MAX_INCLUDE_DIR}" "${MAXSDK_MSP_INCLUDE_DIR}" "${MAXSDK_JIT_INCLUDE_DIR}" diff --git a/include/avnd/binding/clap/helpers.hpp b/include/avnd/binding/clap/helpers.hpp index 56652c1..471eca5 100644 --- a/include/avnd/binding/clap/helpers.hpp +++ b/include/avnd/binding/clap/helpers.hpp @@ -1,4 +1,5 @@ #pragma once +#include #include namespace avnd_clap diff --git a/include/avnd/binding/clap/prototype.cpp.in b/include/avnd/binding/clap/prototype.cpp.in index 1e8476d..5698c61 100644 --- a/include/avnd/binding/clap/prototype.cpp.in +++ b/include/avnd/binding/clap/prototype.cpp.in @@ -8,7 +8,7 @@ using plug_type = decltype(avnd::configure())::type; using effect_type = avnd_clap::SimpleAudioEffect; -AVND_EXPORTED_SYMBOL const struct clap_plugin_entry clap_plugin_entry = { +AVND_EXPORTED_SYMBOL extern const struct clap_plugin_entry clap_plugin_entry = { CLAP_VERSION, +[] (const char* path) -> bool { /* init */ return true; }, +[] () { /* deinit */ }, diff --git a/include/avnd/binding/max/outputs.hpp b/include/avnd/binding/max/outputs.hpp index 53685ef..52ab14b 100644 --- a/include/avnd/binding/max/outputs.hpp +++ b/include/avnd/binding/max/outputs.hpp @@ -2,9 +2,10 @@ /* SPDX-License-Identifier: GPL-3.0-or-later */ -#include #include +#include #include +#include #include namespace max { @@ -35,6 +36,86 @@ inline void value_to_max(t_atom& atom, const std::string& v) noexcept { atom = {.a_type = A_SYM, .a_w = {.w_sym = gensym(v.c_str())}}; } + +struct do_value_to_max_rec +{ + // FIXME static thread_local errors with msvc... + boost::container::small_vector atoms; + + template + requires(std::is_aggregate_v && !avnd::span_ish && !avnd::tuple_ish) + void operator()(const T& v) + { + avnd::for_each_field_ref_n( + v, [this](auto& field, avnd::field_index) mutable { + (*this)(field); + }); + } + + void operator()() noexcept + { + atoms.push_back({.a_type = A_NOTHING, .a_w = {.w_long = 0}}); + } + void operator()(std::floating_point auto v) noexcept + { + atoms.push_back({.a_type = A_FLOAT, .a_w = {.w_float = v}}); + } + void operator()(std::integral auto v) noexcept + { + atoms.push_back({.a_type = A_LONG, .a_w = {.w_long = v}}); + } + void operator()(std::string_view v) noexcept + { + atoms.push_back({.a_type = A_SYM, .a_w = {.w_sym = gensym(v.data())}}); + } + void operator()(const std::string& v) noexcept + { + atoms.push_back({.a_type = A_SYM, .a_w = {.w_sym = gensym(v.data())}}); + } + + void operator()(const avnd::iterable_ish auto& v) noexcept + { + atoms.reserve(atoms.size() + v.size()); + for(auto& e : v) + { + (*this)(e); + } + } + + void operator()(const avnd::map_ish auto& v) noexcept + { + atoms.reserve(atoms.size() + v.size() * 2); + for(auto& [k, v] : v) + { + (*this)(k); + (*this)(v); + } + } + + void operator()(const avnd::variant_ish auto& v) noexcept + { + using namespace std; + visit([this](const auto& val) { (*this)(val); }, v); + } + + void operator()(const avnd::pair_ish auto& v) noexcept + { + (*this)(v.first); + (*this)(v.second); + } + + template + requires(!avnd::iterable_ish) + void operator()(const T& v) noexcept + { + static constexpr int N = std::tuple_size_v; + + [&](std::index_sequence) { + ((*this)(std::get(v)), ...); + }(std::make_index_sequence{}); + } +}; + struct do_value_to_max_typed { t_outlet* p; @@ -60,8 +141,8 @@ struct do_value_to_max_typed outlet_anything(p, gensym(v.data()), 0, nullptr); } - template - requires std::is_aggregate_v + template + requires(std::is_aggregate_v && !avnd::span_ish && !avnd::tuple_ish) void operator()(const T& v) const noexcept { to_list l; @@ -74,17 +155,24 @@ struct do_value_to_max_typed } template - void operator()(t_outlet* outlet, const avnd::array_ish auto& v) + void operator()(const avnd::array_ish auto& v) { std::array atoms; [&](std::index_sequence) { - (set_atom{}(&atoms[I], v), ...); + (set_atom{}(&atoms[I], v[I]), ...); }(std::make_index_sequence{}); outlet_list(p, nullptr, N, atoms.data()); } + void operator()(const avnd::span_ish auto& v) const noexcept + { + to_list l; + l(v); + outlet_list(p, nullptr, l.atoms.size(), l.atoms.data()); + } + void operator()(const avnd::vector_ish auto& v) const noexcept { to_list l; @@ -161,8 +249,8 @@ struct do_value_to_max_anything outlet_anything(p, s, 1, &atom); } - template - requires std::is_aggregate_v + template + requires(std::is_aggregate_v && !avnd::span_ish && !avnd::tuple_ish) void operator()(const T& v) const noexcept { to_list l; @@ -174,6 +262,38 @@ struct do_value_to_max_anything outlet_anything(p, s, (short)l.atoms.size(), l.atoms.data()); } + template + void operator()(const avnd::array_ish auto& v) + { + std::array atoms; + + [&](std::index_sequence) { + (set_atom{}(&atoms[I], v[I]), ...); + }(std::make_index_sequence{}); + + outlet_anything(p, s, N, atoms.data()); + } + + template + void operator()(const T& v) + { + static constexpr auto N = std::tuple_size_v; + std::array atoms; + + [&](std::index_sequence) { + (set_atom{}(&atoms[I], std::get(v)), ...); + }(std::make_index_sequence{}); + + outlet_anything(p, s, N, atoms.data()); + } + + void operator()(const avnd::span_ish auto& v) const noexcept + { + to_list l; + l(v); + outlet_anything(p, s, l.atoms.size(), l.atoms.data()); + } + void operator()(const avnd::vector_ish auto& v) const noexcept { to_list l; @@ -203,16 +323,19 @@ struct do_value_to_max_anything template requires avnd::pair_ish - void operator()(t_outlet* outlet, t_symbol* sym, const T& v) + void operator()(const T& v) { t_atom atoms[2]; value_to_max(atoms[0], v.first); value_to_max(atoms[1], v.second); - outlet_anything(outlet, sym, 2, atoms); + outlet_anything(p, s, 2, atoms); } + template + void operator()(t_outlet*, Args&&... v) noexcept = delete; + template requires(sizeof...(Args) > 1) void operator()(Args&&... v) noexcept @@ -221,7 +344,7 @@ struct do_value_to_max_anything static constexpr int N = sizeof...(Args); [&](std::index_sequence) { - (set_atom{}(&atoms[I], v), ...); + (set_atom{}(&atoms[I], v[I]), ...); }(std::make_index_sequence{}); outlet_anything(p, s, N, atoms.data()); @@ -255,11 +378,31 @@ inline void value_to_max_dispatch(T& self, avnd::field_index, t_outlet* out { // FIXME static const auto sym = get_message_out_symbol(); - // return do_value_to_max_anything{}(outlet, sym, std::forward(v)); + if constexpr(convertible_to_atom_list_statically) + { + return do_value_to_max_anything{outlet, sym}(std::forward(v)); + } + else + { + do_value_to_max_rec rec{}; + rec(v); + outlet_anything(outlet, sym, rec.atoms.size(), rec.atoms.data()); + return; + } } - //else + else { - return do_value_to_max_typed{outlet}(std::forward(v)); + if constexpr(convertible_to_atom_list_statically) + { + return do_value_to_max_typed{outlet}(std::forward(v)); + } + else + { + do_value_to_max_rec rec{}; + rec(v); + outlet_list(outlet, _sym_list, rec.atoms.size(), rec.atoms.data()); + return; + } } } @@ -277,11 +420,31 @@ inline void value_to_max_dispatch(t_outlet* outlet, Args&&... v) noexcept { // FIXME static const auto sym = get_message_out_symbol(); - // return do_value_to_max_anything{}(outlet, sym, std::forward(v)...); + if constexpr((convertible_to_atom_list_statically && ...)) + { + return do_value_to_max_anything{outlet, sym}(std::forward(v)...); + } + else + { + do_value_to_max_rec rec{}; + (rec(v), ...); + outlet_anything(outlet, sym, rec.atoms.size(), rec.atoms.data()); + return; + } } - //else + else { - return do_value_to_max_typed{outlet}(std::forward(v)...); + if constexpr((convertible_to_atom_list_statically && ...)) + { + return do_value_to_max_typed{outlet}(std::forward(v)...); + } + else + { + do_value_to_max_rec rec{}; + (rec(v), ...); + outlet_list(outlet, _sym_list, rec.atoms.size(), rec.atoms.data()); + return; + } } } diff --git a/include/avnd/binding/pd/outputs.hpp b/include/avnd/binding/pd/outputs.hpp index e5c66fc..c9a399c 100644 --- a/include/avnd/binding/pd/outputs.hpp +++ b/include/avnd/binding/pd/outputs.hpp @@ -6,6 +6,8 @@ #include #include #include +#include + #include #include namespace pd @@ -45,21 +47,88 @@ inline void value_to_pd(t_atom& atom, const std::optional& v) noexcept struct do_value_to_pd_rec { // FIXME static thread_local errors with msvc... - std::vector atoms; + boost::container::small_vector atoms; template - requires (std::is_aggregate_v && !avnd::span_ish && !avnd::tuple_ish) + requires(std::is_aggregate_v && !avnd::span_ish && !avnd::tuple_ish) void operator()(const T& v) { - avnd::for_each_field_ref_n( - v, [this](auto& field, avnd::field_index) mutable { - (*this)(field); - }); + static constexpr int sz = avnd::pfr::tuple_size_v; + + if constexpr(sz == 0) + { + atoms.push_back({.a_type = A_NULL, .a_w = {.w_float = 0}}); + } + else + { + avnd::for_each_field_ref_n( + v, [this](auto& field, avnd::field_index) mutable { + (*this)(field); + }); + } } - void operator()(const auto& v) + void operator()() noexcept + { + atoms.push_back({.a_type = A_NULL, .a_w = {.w_float = 0}}); + } + void operator()(std::floating_point auto v) noexcept { - //FIXME + atoms.push_back({.a_type = A_FLOAT, .a_w = {.w_float = (t_float)v}}); + } + void operator()(std::integral auto v) noexcept + { + atoms.push_back({.a_type = A_FLOAT, .a_w = {.w_float = (t_float)v}}); + } + void operator()(std::string_view v) noexcept + { + atoms.push_back({.a_type = A_SYMBOL, .a_w = {.w_symbol = gensym(v.data())}}); + } + void operator()(const std::string& v) noexcept + { + atoms.push_back({.a_type = A_SYMBOL, .a_w = {.w_symbol = gensym(v.data())}}); + } + + void operator()(const avnd::iterable_ish auto& v) noexcept + { + atoms.reserve(atoms.size() + v.size()); + for(auto& e : v) + { + (*this)(e); + } + } + + void operator()(const avnd::map_ish auto& v) noexcept + { + atoms.reserve(atoms.size() + v.size() * 2); + for(auto& [k, v] : v) + { + (*this)(k); + (*this)(v); + } + } + + void operator()(const avnd::variant_ish auto& v) noexcept + { + using namespace std; + visit([this](const auto& val) { (*this)(val); }, v); + } + + void operator()(const avnd::pair_ish auto& v) noexcept + { + (*this)(v.first); + (*this)(v.second); + } + + template + requires(!avnd::iterable_ish) + void operator()(const T& v) noexcept + { + static constexpr int N = std::tuple_size_v; + + [&](std::index_sequence) { + ((*this)(std::get(v)), ...); + }(std::make_index_sequence{}); } }; @@ -93,7 +162,7 @@ struct do_value_to_pd_typed } template - requires avnd::vector_ish + requires avnd::span_ish void operator()(t_outlet* outlet, const T& v) { static_assert(convertible_to_fundamental_value_type); @@ -249,7 +318,7 @@ struct do_value_to_pd_anything } template - requires (std::is_aggregate_v && !avnd::span_ish) + requires(std::is_aggregate_v && !avnd::span_ish && !avnd::tuple_ish) void operator()(t_outlet* outlet, t_symbol* sym, const T& v) { static constexpr int sz = avnd::pfr::tuple_size_v; @@ -273,7 +342,7 @@ struct do_value_to_pd_anything } template - requires avnd::vector_ish + requires avnd::span_ish void operator()(t_outlet* outlet, t_symbol* sym, const T& v) { static thread_local std::vector atoms; diff --git a/include/avnd/concepts/generic.hpp b/include/avnd/concepts/generic.hpp index 5ea68e2..ee35abd 100644 --- a/include/avnd/concepts/generic.hpp +++ b/include/avnd/concepts/generic.hpp @@ -43,51 +43,54 @@ namespace avnd // Very generic concepts template -concept vector_ish = requires(T t) { - t.push_back({}); - t.size(); - // t.reserve(1); // many containers don't have it - t.resize(1); - t.clear(); - // t.data(); // did you know? std::vector does not have it - t[1]; - }; +concept iterable_ish = requires(const T& t) { + std::size(t); + std::begin(t); + std::end(t); +}; +template +concept span_ish = iterable_ish && requires(const T& t) { t[0]; }; +template +concept vector_ish = span_ish && requires(T t) { + t.push_back({}); + // t.reserve(1); // many containers don't have it + t.resize(1); + t.clear(); + // t.data(); // did you know? std::vector does not have it +}; template -concept set_ish = requires(T t) { - sizeof(typename T::key_type); - sizeof(typename T::value_type); - t.insert(typename T::key_type{}); - t.find(typename T::key_type{}); - t.size(); - t.clear(); - } && !requires(T t) { sizeof(typename T::mapped_type); }; +concept set_ish = iterable_ish && requires(T t) { + sizeof(typename T::key_type); + sizeof(typename T::value_type); + t.insert(typename T::key_type{}); + t.find(typename T::key_type{}); + t.size(); + t.clear(); +} && !requires(T t) { sizeof(typename T::mapped_type); }; template -concept map_ish = requires(T t) { - sizeof(typename T::key_type); - sizeof(typename T::mapped_type); - sizeof(typename T::value_type); - t.insert(typename T::value_type{}); - t.find(typename T::key_type{}); - t[typename T::key_type{}]; - t.size(); - t.clear(); - }; +concept map_ish = iterable_ish && requires(T t) { + sizeof(typename T::key_type); + sizeof(typename T::mapped_type); + sizeof(typename T::value_type); + t.insert(typename T::value_type{}); + t.find(typename T::key_type{}); + t[typename T::key_type{}]; + t.size(); + t.clear(); +}; template -concept vector_v_ish = requires(T t) { - t.push_back(V{}); - t.size(); - t.resize(1); - t.reserve(1); - t.clear(); - t.data(); - { - t[1] - } -> std::convertible_to; - t[1] = std::declval(); - }; +concept vector_v_ish = span_ish && requires(T t) { + t.push_back(V{}); + t.resize(1); + t.reserve(1); + t.clear(); + t.data(); + { t[1] } -> std::convertible_to; + t[1] = std::declval(); +}; template concept vector_v_strict = vector_ish && std::is_same_v; @@ -105,11 +108,11 @@ concept pair_ish = requires(T t) { } && (std::tuple_size_v == 2); template -concept tuple_ish = requires(T t) { - std::tuple_size::value; - typename std::tuple_element_t<0, T>; - std::get<0>(t); - }; +concept tuple_ish = requires(const T& t) { + std::tuple_size::value; + typename std::tuple_element_t<0, T>; + std::get<0>(t); +}; template concept optional_ish = requires(T t) { @@ -120,19 +123,18 @@ concept optional_ish = requires(T t) { }; template -concept c_array_ish = std::extent_v >= -N; +concept c_array_ish = span_ish && (std::extent_v >= N); template -concept cpp_tuple_ish = requires { std::tuple_size_v> >= N; }; +concept cpp_tuple_ish + = tuple_ish && requires { std::tuple_size_v> == N; }; template -concept cpp_array_ish = ! -vector_ish&& cpp_tuple_ish&& requires(T t) { - std::size(t); - t[0]; - }; +concept cpp_array_ish = span_ish && !vector_ish && cpp_tuple_ish; template -concept array_ish = c_array_ish || cpp_array_ish; +concept array_ish = span_ish && (c_array_ish || cpp_array_ish); + +template +concept static_array_ish = span_ish && tuple_ish; template concept bitset_ish = requires(T t) { @@ -147,12 +149,6 @@ concept bitset_ish = requires(T t) { t.reset(123); }; -template -concept span_ish = requires(T t) { - t.size(); - t[0]; - }; - template concept pointer = std::is_pointer_v>; diff --git a/include/avnd/wrappers/generic.hpp b/include/avnd/wrappers/generic.hpp index 5ddee3f..b4f474e 100644 --- a/include/avnd/wrappers/generic.hpp +++ b/include/avnd/wrappers/generic.hpp @@ -87,4 +87,3 @@ \ [[no_unique_address]] hdl_tuple handles; \ } -\