Merge pull request #545 from jpcima/type-safe-osc

Type-safe OSC message sending
This commit is contained in:
JP Cimalando 2020-11-17 19:14:59 +01:00 committed by GitHub
commit 28dc603f2b
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
4 changed files with 126 additions and 6 deletions

View file

@ -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

View file

@ -9,6 +9,10 @@
namespace sfz {
template <char Tag> struct OscDataTraits;
template <char Tag> using OscType = typename OscDataTraits<Tag>::type;
template <char Tag> using OscDecayedType = typename OscDataTraits<Tag>::decayed_type;
class Client {
public:
explicit Client(void* data) : data_(data) {}
@ -17,15 +21,18 @@ public:
bool canReceive() const { return receive_ != nullptr; }
void receive(int delay, const char* path, const char* sig, const sfizz_arg_t* args);
template <char... Sig>
void receive(int delay, const char* path, OscDecayedType<Sig>... values);
private:
template <char Tag>
sfizz_arg_t make_arg(OscDecayedType<Tag> 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"

82
src/sfizz/Messaging.hpp Normal file
View file

@ -0,0 +1,82 @@
// 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 <type_traits>
#include <cstddef>
#include <cstring>
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 <char... Sig>
inline void Client::receive(int delay, const char* path, OscDecayedType<Sig>... values)
{
if (receive_) {
constexpr size_t size = sizeof...(Sig);
char sig[size + 1] { Sig..., '\0' };
sfizz_arg_t args[size] { OscDataTraits<Sig>::make_arg(values)... };
receive_(data_, delay, path, sig, args);
}
}
///
#define OSC_SCALAR_TRAITS(tag, member) \
template <> struct OscDataTraits<tag> { \
typedef decltype(sfizz_arg_t::member) type; \
typedef type decayed_type; \
static_assert(std::is_scalar<type>::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<tag> { \
typedef decltype(sfizz_arg_t::member) type; \
static_assert(std::is_array<type>::value, ""); \
typedef const typename std::remove_all_extents<type>::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<tag> { \
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);
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

View file

@ -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<const uint8_t*>("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, {}, {}, {}, {});
}