From e1c607e28a3294de914c593e3de81eeef12c6bff Mon Sep 17 00:00:00 2001 From: Jean Pierre Cimalando Date: Mon, 9 Nov 2020 14:04:45 +0100 Subject: [PATCH 1/3] Type-safe OSC message sending --- src/CMakeLists.txt | 1 + src/sfizz/Messaging.h | 20 +++++++---- src/sfizz/Messaging.hpp | 76 +++++++++++++++++++++++++++++++++++++++++ 3 files changed, 91 insertions(+), 6 deletions(-) create mode 100644 src/sfizz/Messaging.hpp diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index f8171541..6e68d803 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -219,6 +219,7 @@ target_link_libraries (sfizz_parser PUBLIC absl::strings PRIVATE absl::flat_hash # OSC messaging library set (SFIZZ_MESSAGING_HEADERS sfizz/Messaging.h + sfizz/Messaging.hpp sfizz_message.h) set (SFIZZ_MESSAGING_SOURCES diff --git a/src/sfizz/Messaging.h b/src/sfizz/Messaging.h index 737fa1d4..ab76237f 100644 --- a/src/sfizz/Messaging.h +++ b/src/sfizz/Messaging.h @@ -6,9 +6,14 @@ #pragma once #include "sfizz_message.h" +#include namespace sfz { +template struct OscDataTraits; +template using OscType = typename OscDataTraits::type; +template using OscDecayedType = typename std::decay>::type; + class Client { public: explicit Client(void* data) : data_(data) {} @@ -17,15 +22,18 @@ public: bool canReceive() const { return receive_ != nullptr; } void receive(int delay, const char* path, const char* sig, const sfizz_arg_t* args); + template + void receive(int delay, const char* path, OscDecayedType... values); + +private: + template + sfizz_arg_t make_arg(OscDecayedType value); + private: void* data_ = nullptr; sfizz_receive_t* receive_ = nullptr; }; -inline void Client::receive(int delay, const char* path, const char* sig, const sfizz_arg_t* args) -{ - if (receive_) - receive_(data_, delay, path, sig, args); -} - } // namespace sfz + +#include "Messaging.hpp" diff --git a/src/sfizz/Messaging.hpp b/src/sfizz/Messaging.hpp new file mode 100644 index 00000000..cabd30b6 --- /dev/null +++ b/src/sfizz/Messaging.hpp @@ -0,0 +1,76 @@ +// SPDX-License-Identifier: BSD-2-Clause + +// This code is part of the sfizz library and is licensed under a BSD 2-clause +// license. You should have receive a LICENSE.md file along with the code. +// If not, contact the sfizz maintainers at https://github.com/sfztools/sfizz + +#pragma once +#include "Messaging.h" +#include +#include + +namespace sfz { + +/// +inline void Client::receive(int delay, const char* path, const char* sig, const sfizz_arg_t* args) +{ + if (receive_) + receive_(data_, delay, path, sig, args); +} + +template +inline void Client::receive(int delay, const char* path, OscDecayedType... values) +{ + constexpr size_t size = sizeof...(Sig); + char sig[size + 1] { Sig..., '\0' }; + sfizz_arg_t args[size] { OscDataTraits::make_arg(values)... }; + receive(delay, path, sig, args); +} + +/// +#define OSC_SCALAR_TRAITS(tag, member) \ + template <> struct OscDataTraits { \ + typedef decltype(sfizz_arg_t::member) type; \ + static_assert(std::is_scalar::value, ""); \ + static inline sfizz_arg_t make_arg(type v) { \ + sfizz_arg_t a; a.member = v; return a; \ + } \ + } +#define OSC_BYTEARRAY_TRAITS(tag, member) \ + template <> struct OscDataTraits { \ + typedef decltype(sfizz_arg_t::member) type; \ + static_assert(std::is_array::value, ""); \ + static inline sfizz_arg_t make_arg(type v) { \ + sfizz_arg_t a; \ + std::memcpy(a.member, v, sizeof(a.member)); \ + return a; \ + } \ + } +#define OSC_VOID_TRAITS(tag) \ + template <> struct OscDataTraits { \ + typedef struct Nothing {} type; \ + static inline sfizz_arg_t make_arg(type v) { \ + sfizz_arg_t a; (void)v; return a; \ + } \ + } + +OSC_SCALAR_TRAITS('i', i); +OSC_SCALAR_TRAITS('c', i); +OSC_SCALAR_TRAITS('r', i); +OSC_BYTEARRAY_TRAITS('m', m); +OSC_SCALAR_TRAITS('h', h); +OSC_SCALAR_TRAITS('f', f); +OSC_SCALAR_TRAITS('d', d); +OSC_SCALAR_TRAITS('s', s); +OSC_SCALAR_TRAITS('S', s); +OSC_SCALAR_TRAITS('b', b); +OSC_VOID_TRAITS('T'); +OSC_VOID_TRAITS('F'); +OSC_VOID_TRAITS('N'); +OSC_VOID_TRAITS('I'); + +#undef OSC_SCALAR_TRAITS +#undef OSC_BYTEARRAY_TRAITS +#undef OSC_VOID_TRAITS + +} // namespace sfz From 9b044a4eb93eeca064fffcd3f202f36b7e2b1f3f Mon Sep 17 00:00:00 2001 From: Jean Pierre Cimalando Date: Mon, 9 Nov 2020 14:13:54 +0100 Subject: [PATCH 2/3] Skip message processing if no callback --- src/sfizz/Messaging.hpp | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/src/sfizz/Messaging.hpp b/src/sfizz/Messaging.hpp index cabd30b6..e5df7537 100644 --- a/src/sfizz/Messaging.hpp +++ b/src/sfizz/Messaging.hpp @@ -21,10 +21,12 @@ inline void Client::receive(int delay, const char* path, const char* sig, const template inline void Client::receive(int delay, const char* path, OscDecayedType... values) { - constexpr size_t size = sizeof...(Sig); - char sig[size + 1] { Sig..., '\0' }; - sfizz_arg_t args[size] { OscDataTraits::make_arg(values)... }; - receive(delay, path, sig, args); + if (receive_) { + constexpr size_t size = sizeof...(Sig); + char sig[size + 1] { Sig..., '\0' }; + sfizz_arg_t args[size] { OscDataTraits::make_arg(values)... }; + receive_(data_, delay, path, sig, args); + } } /// From 425889c94957a19eff4eddd1b84f246a871d8acf Mon Sep 17 00:00:00 2001 From: Jean Pierre Cimalando Date: Mon, 9 Nov 2020 14:43:25 +0100 Subject: [PATCH 3/3] Add test --- src/sfizz/Messaging.h | 3 +-- src/sfizz/Messaging.hpp | 48 ++++++++++++++++++++++------------------- tests/MessagingT.cpp | 30 ++++++++++++++++++++++++++ 3 files changed, 57 insertions(+), 24 deletions(-) diff --git a/src/sfizz/Messaging.h b/src/sfizz/Messaging.h index ab76237f..0f48587b 100644 --- a/src/sfizz/Messaging.h +++ b/src/sfizz/Messaging.h @@ -6,13 +6,12 @@ #pragma once #include "sfizz_message.h" -#include namespace sfz { template struct OscDataTraits; template using OscType = typename OscDataTraits::type; -template using OscDecayedType = typename std::decay>::type; +template using OscDecayedType = typename OscDataTraits::decayed_type; class Client { public: diff --git a/src/sfizz/Messaging.hpp b/src/sfizz/Messaging.hpp index e5df7537..587e671d 100644 --- a/src/sfizz/Messaging.hpp +++ b/src/sfizz/Messaging.hpp @@ -6,6 +6,7 @@ #pragma once #include "Messaging.h" +#include #include #include @@ -30,30 +31,33 @@ inline void Client::receive(int delay, const char* path, OscDecayedType... } /// -#define OSC_SCALAR_TRAITS(tag, member) \ - template <> struct OscDataTraits { \ - typedef decltype(sfizz_arg_t::member) type; \ - static_assert(std::is_scalar::value, ""); \ - static inline sfizz_arg_t make_arg(type v) { \ - sfizz_arg_t a; a.member = v; return a; \ - } \ +#define OSC_SCALAR_TRAITS(tag, member) \ + template <> struct OscDataTraits { \ + typedef decltype(sfizz_arg_t::member) type; \ + typedef type decayed_type; \ + static_assert(std::is_scalar::value, ""); \ + static inline sfizz_arg_t make_arg(decayed_type v) { \ + sfizz_arg_t a; a.member = v; return a; \ + } \ } -#define OSC_BYTEARRAY_TRAITS(tag, member) \ - template <> struct OscDataTraits { \ - typedef decltype(sfizz_arg_t::member) type; \ - static_assert(std::is_array::value, ""); \ - static inline sfizz_arg_t make_arg(type v) { \ - sfizz_arg_t a; \ - std::memcpy(a.member, v, sizeof(a.member)); \ - return a; \ - } \ +#define OSC_BYTEARRAY_TRAITS(tag, member) \ + template <> struct OscDataTraits { \ + typedef decltype(sfizz_arg_t::member) type; \ + static_assert(std::is_array::value, ""); \ + typedef const typename std::remove_all_extents::type* decayed_type; \ + static inline sfizz_arg_t make_arg(decayed_type v) { \ + sfizz_arg_t a; \ + std::memcpy(a.member, v, sizeof(a.member)); \ + return a; \ + } \ } -#define OSC_VOID_TRAITS(tag) \ - template <> struct OscDataTraits { \ - typedef struct Nothing {} type; \ - static inline sfizz_arg_t make_arg(type v) { \ - sfizz_arg_t a; (void)v; return a; \ - } \ +#define OSC_VOID_TRAITS(tag) \ + template <> struct OscDataTraits { \ + typedef struct Nothing {} type; \ + typedef type decayed_type; \ + static inline sfizz_arg_t make_arg(decayed_type v) { \ + sfizz_arg_t a; (void)v; return a; \ + } \ } OSC_SCALAR_TRAITS('i', i); diff --git a/tests/MessagingT.cpp b/tests/MessagingT.cpp index 2b52df56..5496ca82 100644 --- a/tests/MessagingT.cpp +++ b/tests/MessagingT.cpp @@ -93,3 +93,33 @@ TEST_CASE("[Messaging] OSC message creation") REQUIRE(args2[4].f == 5.678f); } } + +TEST_CASE("[Messaging] Type-safe client API") +{ + sfz::Client client(nullptr); + + static const int32_t i = 777; + static const int64_t h = 0x100000000LL; + static const float f = 3.14f; + static const double d = 6.28; + static const uint8_t m[4] = {0x90, 0x40, 0xFF}; + static const sfizz_blob_t b { reinterpret_cast("MyBinaryString"), 14 }; + static const char s[] = "Hello, World!"; + + client.setReceiveCallback(+[](void*, int, const char* path, const char* sig, const sfizz_arg_t* args) { + REQUIRE(!strcmp(path, "/test")); + REQUIRE(!strcmp(sig, "imhfdsbTFNI")); + unsigned index = 0; + REQUIRE(args[index++].i == i); + REQUIRE(!memcmp(args[index++].m, m, 4)); + REQUIRE(args[index++].h == h); + REQUIRE(args[index++].f == f); + REQUIRE(args[index++].d == d); + REQUIRE(!strcmp(args[index++].s, s)); + REQUIRE(args[index ].b->data == b.data); + REQUIRE(args[index++].b->size == b.size); + }); + + client.receive<'i', 'm', 'h', 'f', 'd', 's', 'b', 'T', 'F', 'N', 'I'>( + 0, "/test", i, m, h, f, d, s, &b, {}, {}, {}, {}); +}