From b903ef46492b393cee6f4e9001be285f314b7c10 Mon Sep 17 00:00:00 2001 From: Syl Morrison Date: Sat, 6 Sep 2025 16:22:31 +0100 Subject: [PATCH 1/2] Cmake flatbuffer compilation --- CMakeLists.txt | 7 +++++++ cmake/mostly_harmless.cmake | 39 +++++++++++++++++++++++++++++++++++-- 2 files changed, 44 insertions(+), 2 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 5ee5097..6d15a1d 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -66,6 +66,13 @@ FetchContent_Declare( ) FetchContent_MakeAvailable(sqlite3) +FetchContent_Declare(FlatBuffers + GIT_REPOSITORY https://github.com/google/flatbuffers.git + GIT_TAG v25.2.10 + GIT_SHALLOW ON +) +FetchContent_MakeAvailable(FlatBuffers) + FetchContent_Declare( marvin GIT_REPOSITORY https://github.com/MeijisIrlnd/marvin.git diff --git a/cmake/mostly_harmless.cmake b/cmake/mostly_harmless.cmake index b2e5b85..e5ef31c 100644 --- a/cmake/mostly_harmless.cmake +++ b/cmake/mostly_harmless.cmake @@ -108,9 +108,9 @@ function(mostly_harmless_add_plugin targetName) set(PLUGIN_NOTE_BUS_CONFIG "BusConfig::None") endif () endif () - if(NOT ${PLUGIN_NEEDS_AUDIO_OUT_BUS}) + if (NOT ${PLUGIN_NEEDS_AUDIO_OUT_BUS}) message(FATAL_ERROR "Plugins that don't support audio output are not supported yet!") - endif() + endif () # AUDIO BUSSES if (${PLUGIN_NEEDS_AUDIO_IN_BUS}) if (${PLUGIN_NEEDS_AUDIO_OUT_BUS}) @@ -258,3 +258,38 @@ function(mostly_harmless_add_plugin targetName) ) endif () endfunction() + +function(mostly_harmless_compile_flatbuffer_schemas) + set(SINGLE_VALUE_ARGS + "TARGET" + "INCLUDE_PREFIX" + ) + set(MULTI_VALUE_ARGS + "SCHEMAS" + "LANGUAGES" + ) + cmake_parse_arguments(ARG "" "${SINGLE_VALUE_ARGS}" "${MULTI_VALUE_ARGS}" ${ARGN}) + if (NOT DEFINED ARG_TARGET) + message(FATAL_ERROR "A target is required!") + endif () + if (NOT DEFINED ARG_INCLUDE_PREFIX) + message(FATAL_ERROR "An include prefix is required!") + endif () + if (NOT DEFINED ARG_SCHEMAS) + message(FATAL_ERROR "At least one schema must be provided") + endif () + if (NOT DEFINED ARG_LANGUAGES) + message(FATAL_ERROR "At least one language must be provided") + endif () + foreach (language_string ${ARG_LANGUAGES}) + set(LANGUAGE_FLAGS "${LANGUAGE_FLAGS}" "--${language_string}") + endforeach () + set(FLAGS "${LANGUAGE_FLAGS}" "--gen-object-api") + flatbuffers_generate_headers( + TARGET ${ARG_TARGET} + INCLUDE_PREFIX ${ARG_INCLUDE_PREFIX} + SCHEMAS ${ARG_SCHEMAS} + BINARY_SCHEMAS_DIR "" + FLAGS ${FLAGS} + ) +endfunction() From ac3981cdfaad9036223871d4db8a587e9b2717a1 Mon Sep 17 00:00:00 2001 From: Syl Morrison Date: Sat, 6 Sep 2025 16:48:14 +0100 Subject: [PATCH 2/2] C++ side flat buffer api, document --- CMakeLists.txt | 1 + docs/mostlyharmless_NamespaceDocs.h | 6 ++ include/mostly_harmless/CMakeLists.txt | 1 + .../utils/mostlyharmless_FlatBuffers.h | 69 +++++++++++++++++++ source/CMakeLists.txt | 1 + source/utils/mostlyharmless_FlatBuffers.cpp | 13 ++++ 6 files changed, 91 insertions(+) create mode 100644 include/mostly_harmless/utils/mostlyharmless_FlatBuffers.h create mode 100644 source/utils/mostlyharmless_FlatBuffers.cpp diff --git a/CMakeLists.txt b/CMakeLists.txt index 6d15a1d..a71ebf8 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -122,6 +122,7 @@ target_link_libraries(MostlyHarmless fmt::fmt-header-only nlohmann_json sqlite3::sqlite3 + flatbuffers ${MOSTLY_HARMLESS_EXTRA_LIBS} ) diff --git a/docs/mostlyharmless_NamespaceDocs.h b/docs/mostlyharmless_NamespaceDocs.h index d4733e9..988bcf1 100644 --- a/docs/mostlyharmless_NamespaceDocs.h +++ b/docs/mostlyharmless_NamespaceDocs.h @@ -46,6 +46,12 @@ namespace mostly_harmless { namespace directories { } + /** + \brief Serialisation and Deserialisation utils for FlatBuffers + */ + namespace flat_buffers { + + } } /** diff --git a/include/mostly_harmless/CMakeLists.txt b/include/mostly_harmless/CMakeLists.txt index fb16367..2ff4307 100644 --- a/include/mostly_harmless/CMakeLists.txt +++ b/include/mostly_harmless/CMakeLists.txt @@ -26,6 +26,7 @@ set(MOSTLYHARMLESS_HEADERS ${CMAKE_CURRENT_SOURCE_DIR}/utils/mostlyharmless_OnScopeExit.h ${CMAKE_CURRENT_SOURCE_DIR}/utils/mostlyharmless_Proxy.h ${CMAKE_CURRENT_SOURCE_DIR}/utils/mostlyharmless_NoDenormals.h + ${CMAKE_CURRENT_SOURCE_DIR}/utils/mostlyharmless_FlatBuffers.h ${CMAKE_CURRENT_SOURCE_DIR}/data/mostlyharmless_DatabaseState.h ${CMAKE_CURRENT_SOURCE_DIR}/data/mostlyharmless_DatabasePropertyWatcher.h ${PLATFORM_HEADERS} diff --git a/include/mostly_harmless/utils/mostlyharmless_FlatBuffers.h b/include/mostly_harmless/utils/mostlyharmless_FlatBuffers.h new file mode 100644 index 0000000..41860d0 --- /dev/null +++ b/include/mostly_harmless/utils/mostlyharmless_FlatBuffers.h @@ -0,0 +1,69 @@ +// +// Created by Syl Morrison on 06/09/2025. +// + +#ifndef MOSTLYHARMLESS_FLATBUFFERS_H +#define MOSTLYHARMLESS_FLATBUFFERS_H +#include + +namespace mostly_harmless::utils::flat_buffers { + /** + * \brief Represents a flatbuffer autogenerated SomethingT type + **/ + template + concept FlatBufferType = std::is_base_of_v; + + + /** + * \brief Handles serialising or deserialising flat buffer data. + * Serialised data is tied to the lifecycle of a flatbuffer::FlatBufferBuilder - rather than requiring the user learn the ins and outs of the flatbuffer api, + * this class owns a FlatBufferBuilder. + * Subsequent calls to `serialise` will clear the builder's buffer, so if serialising multiple flat buffers in a loop, be sure to copy the serialised data into a + * concrete vector etc! + */ + class Serialiser final { + public: + /** + * Constructor + * @param initialAllocSize The initial alloc size, to pass to the flatbuffer builder + */ + explicit Serialiser(size_t initialAllocSize); + + /** + * Given an autogenerated flatbuffer SomethingT type, serialises the data. + * As mentioned above, calling serialise will clear the internal builder's buffer, and the return value from this function's lifecycle is tied to that builder, + * so ensure you've done what you need to with the data before calling this function again! + * @tparam T An autogenerated flatbuffer YourTypeT type + * @param toSerialise The data you want to serialise + * @return A view into the resulting serialised buffer. + */ + template + [[nodiscard]] std::span serialise(T &toSerialise) { + using Underlying = typename T::TableType; + m_builder.Clear(); + auto packed = Underlying::Pack(m_builder, &toSerialise); + m_builder.Finish(packed); + return {m_builder.GetBufferPointer(), m_builder.GetSize()}; + } + + + /** + * Deserialises the given buffer into a flatbuffer autogenerated YourTypeT. + * In this case, the internal builder isn't used at all (hence being static), and the return value's lifecycle is tied to that of your input data. + * @tparam T A flatbuffer autogenerated YourTypeT + * @param toDeserialise A view into the data to deserialise + * @return A pointer to the resulting YourTypeT + */ + template + [[nodiscard]] static T *deserialise(std::span toDeserialise) { + using Underlying = typename T::TableType; + auto *root = flatbuffers::GetRoot(toDeserialise.data()); + auto *res = root->UnPack(); + return res; + } + + private: + flatbuffers::FlatBufferBuilder m_builder; + }; +} +#endif //MOSTLYHARMLESS_FLATBUFFERS_H diff --git a/source/CMakeLists.txt b/source/CMakeLists.txt index 3f4e840..b01b98f 100644 --- a/source/CMakeLists.txt +++ b/source/CMakeLists.txt @@ -32,6 +32,7 @@ set(MOSTLYHARMLESS_SOURCES ${CMAKE_CURRENT_SOURCE_DIR}/utils/mostlyharmless_NoDenormals.cpp ${CMAKE_CURRENT_SOURCE_DIR}/utils/mostlyharmless_Hash.cpp ${CMAKE_CURRENT_SOURCE_DIR}/utils/mostlyharmless_Macros.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/utils/mostlyharmless_FlatBuffers.cpp ${CMAKE_CURRENT_SOURCE_DIR}/data/mostlyharmless_DatabaseState.cpp ${CMAKE_CURRENT_SOURCE_DIR}/data/mostlyharmless_DatabasePropertyWatcher.cpp ${PLATFORM_SOURCES} diff --git a/source/utils/mostlyharmless_FlatBuffers.cpp b/source/utils/mostlyharmless_FlatBuffers.cpp new file mode 100644 index 0000000..d22df21 --- /dev/null +++ b/source/utils/mostlyharmless_FlatBuffers.cpp @@ -0,0 +1,13 @@ +// +// Created by Syl Morrison on 06/09/2025. +// +#include + +namespace mostly_harmless::utils::flat_buffers { + Serialiser::Serialiser(size_t initialAllocSize) : m_builder(initialAllocSize) { + + } + + + +} \ No newline at end of file