Initial UI setup
This commit is contained in:
@@ -0,0 +1,289 @@
|
||||
/*
|
||||
Copyright (c) 2026 - SD-Innovation S.A.S. - FRANCE
|
||||
*/
|
||||
|
||||
/*
|
||||
ver: 2.x.x - build: 2026-04-28
|
||||
*/
|
||||
|
||||
/*
|
||||
The zlib License
|
||||
|
||||
Copyright (c) 2026 SD-Innovation S.A.S.
|
||||
|
||||
This software is provided ‘as-is’, without any express or implied
|
||||
warranty. In no event will the authors be held liable for any damages
|
||||
arising from the use of this software.
|
||||
|
||||
Permission is granted to anyone to use this software for any purpose,
|
||||
including commercial applications, and to alter it and redistribute it
|
||||
freely, subject to the following restrictions:
|
||||
|
||||
1. The origin of this software must not be misrepresented; you must not
|
||||
claim that you wrote the original software. If you use this software
|
||||
in a product, an acknowledgment in the product documentation would be
|
||||
appreciated but is not required.
|
||||
|
||||
2. Altered source versions must be plainly marked as such, and must not be
|
||||
misrepresented as being the original software.
|
||||
|
||||
3. This notice may not be removed or altered from any source
|
||||
distribution.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "ringBuffer.h"
|
||||
|
||||
namespace sdi_toolBox::common::utils
|
||||
{
|
||||
//--------------------------------------------------------------
|
||||
/**
|
||||
* @brief Fixed-size circular buffer with compile-time capacity
|
||||
* and no-overwrite behavior.
|
||||
*
|
||||
* This template wraps a RingBuffer and prevents new elements from being
|
||||
* written when the buffer is full. Existing data is never overwritten;
|
||||
* push() simply fails and returns false when capacity is reached.
|
||||
*
|
||||
* @tparam T Element type stored in the buffer.
|
||||
* @tparam CAPACITY Compile-time buffer capacity. Must be > 0.
|
||||
*
|
||||
* @note The class provides both optional-returning and out-parameter
|
||||
* overloads for pop/front/back to suit different runtime constraints.
|
||||
* @warning Instantiating with CAPACITY == 0 is forbidden (static_assert).
|
||||
*
|
||||
* @code{.cpp}
|
||||
* CircularBuffer<int, 8> cb;
|
||||
* if (cb.push(42))
|
||||
* {
|
||||
* // element was inserted
|
||||
* }
|
||||
* else
|
||||
* {
|
||||
* // buffer was full, element was discarded
|
||||
* }
|
||||
* @endcode
|
||||
*/
|
||||
template<class T, std::size_t CAPACITY>
|
||||
class CircularBuffer
|
||||
{
|
||||
static_assert(CAPACITY > 0, "CAPACITY must be > 0");
|
||||
|
||||
public:
|
||||
///@name Write operations
|
||||
///@{
|
||||
|
||||
/**
|
||||
* @brief Try to append a value to the buffer.
|
||||
*
|
||||
* If the buffer is full, the value is discarded and no overwrite occurs.
|
||||
* @param value Value to append (copied).
|
||||
*
|
||||
* @return true if the value was inserted, false if the buffer was full.
|
||||
*/
|
||||
bool push(const T &value);
|
||||
|
||||
///@}
|
||||
///@name Read operations
|
||||
///@{
|
||||
|
||||
/**
|
||||
* @brief Remove and return the oldest element from the buffer.
|
||||
*
|
||||
* @return std::optional<T> The oldest element if present, std::nullopt if empty.
|
||||
*/
|
||||
|
||||
std::optional<T> pop();
|
||||
/**
|
||||
* @brief Remove the oldest element and store it in the provided reference.
|
||||
*
|
||||
* @param value Output reference that receives the removed element.
|
||||
*
|
||||
* @return true if an element was removed, false if the buffer was empty.
|
||||
*/
|
||||
bool pop(T &value);
|
||||
|
||||
/**
|
||||
* @brief Return (without removing) the oldest element.
|
||||
*
|
||||
* @return std::optional<T> The oldest element if present, std::nullopt if empty.
|
||||
*/
|
||||
[[nodiscard]] std::optional<T> front() const;
|
||||
|
||||
/**
|
||||
* @brief Copy the oldest element into the provided reference without removing it.
|
||||
*
|
||||
* @param value Output reference that receives the element.
|
||||
*
|
||||
* @return true if the element was copied, false if the buffer is empty.
|
||||
*/
|
||||
[[nodiscard]] bool front(T &value) const;
|
||||
|
||||
/**
|
||||
* @brief Return (without removing) the newest element.
|
||||
*
|
||||
* @return std::optional<T> The newest element if present, std::nullopt if empty.
|
||||
*/
|
||||
[[nodiscard]] std::optional<T> back() const;
|
||||
|
||||
/**
|
||||
* @brief Copy the newest element into the provided reference without removing it.
|
||||
*
|
||||
* @param value Output reference that receives the element.
|
||||
*
|
||||
* @return true if the element was copied, false if the buffer is empty.
|
||||
*/
|
||||
[[nodiscard]] bool back(T &value) const;
|
||||
|
||||
///@}
|
||||
///@name State queries
|
||||
///@{
|
||||
|
||||
/**
|
||||
* @brief Check whether the buffer is empty.
|
||||
*
|
||||
* @return true if empty, false otherwise.
|
||||
*/
|
||||
[[nodiscard]] bool empty() const;
|
||||
|
||||
/**
|
||||
* @brief Check whether the buffer is full.
|
||||
*
|
||||
* @return true if full, false otherwise.
|
||||
*/
|
||||
[[nodiscard]] bool full() const;
|
||||
|
||||
/**
|
||||
* @brief Number of elements currently stored in the buffer.
|
||||
*
|
||||
* @return Current size (0 .. CAPACITY).
|
||||
*/
|
||||
[[nodiscard]] std::size_t size() const;
|
||||
|
||||
/**
|
||||
* @brief Compile-time capacity of the buffer.
|
||||
*
|
||||
* @return The maximum number of elements the buffer can hold.
|
||||
*/
|
||||
[[nodiscard]] constexpr std::size_t capacity() const;
|
||||
|
||||
///@}
|
||||
///@name Modifiers
|
||||
///@{
|
||||
|
||||
/**
|
||||
* @brief Clear the buffer and reset internal indices.
|
||||
*
|
||||
* After calling clear(), empty() returns true and size() returns 0.
|
||||
*/
|
||||
void clear();
|
||||
|
||||
///@}
|
||||
|
||||
private:
|
||||
///@name Data members
|
||||
///@{
|
||||
|
||||
RingBuffer<T, CAPACITY> m_buffer{}; ///< Underlying ring buffer
|
||||
|
||||
///@}
|
||||
};
|
||||
//--------------------------------------------------------------
|
||||
|
||||
//--------------------------------------------------------------
|
||||
/* Try to append a value to the buffer. If the buffer is full,
|
||||
* the value is discarded. Return true if the value was inserted,
|
||||
* false if the buffer was full. */
|
||||
template<class T, std::size_t CAPACITY>
|
||||
bool CircularBuffer<T, CAPACITY>::push(const T &value)
|
||||
{
|
||||
if (m_buffer.full())
|
||||
return false;
|
||||
return m_buffer.push(value);
|
||||
}
|
||||
//--------------------------------------------------------------
|
||||
/* Remove and return the oldest value from the buffer. If the
|
||||
* buffer is empty, return std::nullopt */
|
||||
template<class T, std::size_t CAPACITY>
|
||||
std::optional<T> CircularBuffer<T, CAPACITY>::pop()
|
||||
{
|
||||
return m_buffer.pop();
|
||||
}
|
||||
//--------------------------------------------------------------
|
||||
/* Remove the oldest value from the buffer and store it in
|
||||
* 'value'. Return true if successful, false if the buffer is empty */
|
||||
template<class T, std::size_t CAPACITY>
|
||||
bool CircularBuffer<T, CAPACITY>::pop(T &value)
|
||||
{
|
||||
return m_buffer.pop(value);
|
||||
}
|
||||
//--------------------------------------------------------------
|
||||
/* Return the oldest value without removing it. If the buffer is
|
||||
* empty, return std::nullopt */
|
||||
template<class T, std::size_t CAPACITY>
|
||||
std::optional<T> CircularBuffer<T, CAPACITY>::front() const
|
||||
{
|
||||
return m_buffer.front();
|
||||
}
|
||||
//--------------------------------------------------------------
|
||||
/* Return the oldest value without removing it and store it in
|
||||
* 'value'. Return true if successful, false if the buffer is empty */
|
||||
template<class T, std::size_t CAPACITY>
|
||||
bool CircularBuffer<T, CAPACITY>::front(T &value) const
|
||||
{
|
||||
return m_buffer.front(value);
|
||||
}
|
||||
//--------------------------------------------------------------
|
||||
/* Return the newest value without removing it. If the buffer is
|
||||
* empty, return std::nullopt */
|
||||
template<class T, std::size_t CAPACITY>
|
||||
std::optional<T> CircularBuffer<T, CAPACITY>::back() const
|
||||
{
|
||||
return m_buffer.back();
|
||||
}
|
||||
//--------------------------------------------------------------
|
||||
/* Return the newest value without removing it and store it in
|
||||
* 'value'. Return true if successful, false if the buffer is empty */
|
||||
template<class T, std::size_t CAPACITY>
|
||||
bool CircularBuffer<T, CAPACITY>::back(T &value) const
|
||||
{
|
||||
return m_buffer.back(value);
|
||||
}
|
||||
//--------------------------------------------------------------
|
||||
/* Check if the buffer is empty */
|
||||
template<class T, std::size_t CAPACITY>
|
||||
bool CircularBuffer<T, CAPACITY>::empty() const
|
||||
{
|
||||
return m_buffer.empty();
|
||||
}
|
||||
//--------------------------------------------------------------
|
||||
/* Check if the buffer is full */
|
||||
template<class T, std::size_t CAPACITY>
|
||||
bool CircularBuffer<T, CAPACITY>::full() const
|
||||
{
|
||||
return m_buffer.full();
|
||||
}
|
||||
//--------------------------------------------------------------
|
||||
/* Get the number of elements currently in the buffer */
|
||||
template<class T, std::size_t CAPACITY>
|
||||
std::size_t CircularBuffer<T, CAPACITY>::size() const
|
||||
{
|
||||
return m_buffer.size();
|
||||
}
|
||||
//--------------------------------------------------------------
|
||||
/* Get the maximum capacity of the buffer */
|
||||
template<class T, std::size_t CAPACITY>
|
||||
[[nodiscard]] constexpr std::size_t CircularBuffer<T, CAPACITY>::capacity() const
|
||||
{
|
||||
return CAPACITY;
|
||||
}
|
||||
//--------------------------------------------------------------
|
||||
/* Clear the buffer, resetting it to an empty state */
|
||||
template<class T, std::size_t CAPACITY>
|
||||
void CircularBuffer<T, CAPACITY>::clear()
|
||||
{
|
||||
m_buffer.clear();
|
||||
}
|
||||
//--------------------------------------------------------------
|
||||
} // namespace sdi_toolBox::common::utils
|
||||
198
sdi_toolBox_2.x.x/toolBox/sdi_toolBox/common/utils/hash.h
Normal file
198
sdi_toolBox_2.x.x/toolBox/sdi_toolBox/common/utils/hash.h
Normal file
@@ -0,0 +1,198 @@
|
||||
/*
|
||||
Copyright (c) 2026 - SD-Innovation S.A.S. - FRANCE
|
||||
*/
|
||||
|
||||
/*
|
||||
ver: 2.x.x - build: 2026-04-28
|
||||
*/
|
||||
|
||||
/*
|
||||
The zlib License
|
||||
|
||||
Copyright (c) 2026 SD-Innovation S.A.S.
|
||||
|
||||
This software is provided ‘as-is’, without any express or implied
|
||||
warranty. In no event will the authors be held liable for any damages
|
||||
arising from the use of this software.
|
||||
|
||||
Permission is granted to anyone to use this software for any purpose,
|
||||
including commercial applications, and to alter it and redistribute it
|
||||
freely, subject to the following restrictions:
|
||||
|
||||
1. The origin of this software must not be misrepresented; you must not
|
||||
claim that you wrote the original software. If you use this software
|
||||
in a product, an acknowledgment in the product documentation would be
|
||||
appreciated but is not required.
|
||||
|
||||
2. Altered source versions must be plainly marked as such, and must not be
|
||||
misrepresented as being the original software.
|
||||
|
||||
3. This notice may not be removed or altered from any source
|
||||
distribution.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <bit>
|
||||
#include <cstdint>
|
||||
#include <span>
|
||||
#include <string_view>
|
||||
|
||||
namespace sdi_toolBox::common::utils::hash
|
||||
{
|
||||
//--------------------------------------------------------------
|
||||
/**
|
||||
* @brief Compute the FNV-1a 32-bit hash for a span of bytes.
|
||||
*
|
||||
* Primary API: accepts a `std::span<const std::byte>` so callers can pass any
|
||||
* contiguous buffer without copies.
|
||||
*
|
||||
* @param data Span of bytes to hash.
|
||||
* @return std::uint32_t 32-bit FNV-1a hash.
|
||||
*/
|
||||
constexpr std::uint32_t fnv1a(std::span<const std::byte> data) noexcept;
|
||||
|
||||
/**
|
||||
* @brief Compute the FNV-1a 32-bit hash for a std::string_view.
|
||||
*
|
||||
* Convenience overload forwarding to the byte-based implementation.
|
||||
*
|
||||
* @param sv Input string view.
|
||||
* @return std::uint32_t The 32-bit FNV-1a hash.
|
||||
*/
|
||||
constexpr std::uint32_t fnv1a(std::string_view sv) noexcept;
|
||||
|
||||
/**
|
||||
* @brief Compute the FNV-1a 32-bit hash for a trivially copyable POD object.
|
||||
*
|
||||
* The object is hashed as its raw bytes (binary representation).
|
||||
* Use only for POD/trivially-copyable types where this behaviour is intended.
|
||||
*
|
||||
* @tparam T Trivially copyable type.
|
||||
* @param value Reference to the object to hash.
|
||||
* @return std::uint32_t 32-bit FNV-1a hash.
|
||||
*/
|
||||
template<typename T>
|
||||
requires std::is_trivially_copyable_v<T>
|
||||
constexpr std::uint32_t fnv1a(const T &value) noexcept;
|
||||
|
||||
/**
|
||||
* @brief Compute the FNV-1a 64-bit hash for a span of bytes.
|
||||
*
|
||||
* Primary API: accepts a `std::span<const std::byte>` so callers can pass any
|
||||
* contiguous buffer without copies.
|
||||
*
|
||||
* @param data Span of bytes to hash.
|
||||
* @return std::uint64_t 64-bit FNV-1a hash.
|
||||
*/
|
||||
constexpr std::uint64_t fnv1a_64(std::span<const std::byte> data) noexcept;
|
||||
|
||||
/**
|
||||
* @brief Compute the FNV-1a 64-bit hash for a std::string_view.
|
||||
*
|
||||
* Convenience overload forwarding to the byte-based implementation.
|
||||
*
|
||||
* @param sv Input string view.
|
||||
* @return std::uint64_t The 64-bit FNV-1a hash.
|
||||
*/
|
||||
constexpr std::uint64_t fnv1a_64(std::string_view sv) noexcept;
|
||||
|
||||
/**
|
||||
* @brief Compute the FNV-1a 64-bit hash for a trivially copyable POD object.
|
||||
*
|
||||
* The object is hashed as its raw bytes (binary representation).
|
||||
* Use only for POD/trivially-copyable types where this behaviour is intended.
|
||||
*
|
||||
* @tparam T Trivially copyable type.
|
||||
* @param value Reference to the object to hash.
|
||||
* @return std::uint64_t 64-bit FNV-1a hash.
|
||||
*/
|
||||
template<typename T>
|
||||
requires std::is_trivially_copyable_v<T>
|
||||
constexpr std::uint64_t fnv1a_64(const T &value) noexcept;
|
||||
//--------------------------------------------------------------
|
||||
|
||||
//--------------------------------------------------------------
|
||||
/* Compute the FNV-1a 32-bit hash for a span of bytes */
|
||||
inline constexpr std::uint32_t fnv1a(const std::span<const std::byte> data) noexcept
|
||||
{
|
||||
constexpr std::uint32_t FNV_OFFSET_BASIS = 2166136261u;
|
||||
constexpr std::uint32_t FNV_PRIME = 16777619u;
|
||||
|
||||
std::uint32_t hash = FNV_OFFSET_BASIS;
|
||||
for (const auto b : data)
|
||||
{
|
||||
hash ^= static_cast<std::uint32_t>(std::to_integer<std::uint8_t>(b));
|
||||
hash *= FNV_PRIME;
|
||||
}
|
||||
return hash;
|
||||
}
|
||||
//--------------------------------------------------------------
|
||||
/* Compute the FNV-1a 32-bit hash for a std::string_view */
|
||||
inline constexpr std::uint32_t fnv1a(const std::string_view sv) noexcept
|
||||
{
|
||||
// Avoid reinterpret_cast on pointers: iterate characters (constexpr-friendly)
|
||||
constexpr std::uint32_t FNV_OFFSET_BASIS = 2166136261u;
|
||||
constexpr std::uint32_t FNV_PRIME = 16777619u;
|
||||
|
||||
std::uint32_t hash = FNV_OFFSET_BASIS;
|
||||
for (char c : sv)
|
||||
{
|
||||
hash ^= static_cast<std::uint32_t>(static_cast<std::uint8_t>(c));
|
||||
hash *= FNV_PRIME;
|
||||
}
|
||||
return hash;
|
||||
}
|
||||
//--------------------------------------------------------------
|
||||
/* Compute the FNV-1a 32-bit hash for a trivially copyable POD object */
|
||||
template<typename T>
|
||||
requires std::is_trivially_copyable_v<T>
|
||||
inline constexpr std::uint32_t fnv1a(const T &value) noexcept
|
||||
{
|
||||
// Use std::bit_cast to get bytes in a constexpr-friendly way
|
||||
auto bytes = std::bit_cast<std::array<std::byte, sizeof(T)>>(value);
|
||||
return fnv1a(std::span<const std::byte>(bytes.data(), bytes.size()));
|
||||
}
|
||||
//--------------------------------------------------------------
|
||||
/* Compute the FNV-1a 64-bit hash for a span of bytes */
|
||||
inline constexpr std::uint64_t fnv1a_64(const std::span<const std::byte> data) noexcept
|
||||
{
|
||||
constexpr std::uint64_t FNV_OFFSET_BASIS = 14695981039346656037ull;
|
||||
constexpr std::uint64_t FNV_PRIME = 1099511628211ull;
|
||||
|
||||
std::uint64_t hash = FNV_OFFSET_BASIS;
|
||||
for (const auto b : data)
|
||||
{
|
||||
hash ^= static_cast<std::uint64_t>(std::to_integer<std::uint8_t>(b));
|
||||
hash *= FNV_PRIME;
|
||||
}
|
||||
return hash;
|
||||
}
|
||||
//--------------------------------------------------------------
|
||||
/* Compute the FNV-1a 64-bit hash for a std::string_view */
|
||||
inline constexpr std::uint64_t fnv1a_64(const std::string_view sv) noexcept
|
||||
{
|
||||
// Iterate characters to remain constexpr-friendly
|
||||
constexpr std::uint64_t FNV_OFFSET_BASIS = 14695981039346656037ull;
|
||||
constexpr std::uint64_t FNV_PRIME = 1099511628211ull;
|
||||
|
||||
std::uint64_t hash = FNV_OFFSET_BASIS;
|
||||
for (char c : sv)
|
||||
{
|
||||
hash ^= static_cast<std::uint64_t>(static_cast<std::uint8_t>(c));
|
||||
hash *= FNV_PRIME;
|
||||
}
|
||||
return hash;
|
||||
}
|
||||
//--------------------------------------------------------------
|
||||
/* Compute the FNV-1a 64-bit hash for a trivially copyable POD object */
|
||||
template<typename T>
|
||||
requires std::is_trivially_copyable_v<T>
|
||||
inline constexpr std::uint64_t fnv1a_64(const T &value) noexcept
|
||||
{
|
||||
// Use std::bit_cast to get bytes in a constexpr-friendly way
|
||||
auto bytes = std::bit_cast<std::array<std::byte, sizeof(T)>>(value);
|
||||
return fnv1a_64(std::span<const std::byte>(bytes.data(), bytes.size()));
|
||||
}
|
||||
//--------------------------------------------------------------
|
||||
} // namespace sdi_toolBox::common::utils::hash
|
||||
358
sdi_toolBox_2.x.x/toolBox/sdi_toolBox/common/utils/ringBuffer.h
Normal file
358
sdi_toolBox_2.x.x/toolBox/sdi_toolBox/common/utils/ringBuffer.h
Normal file
@@ -0,0 +1,358 @@
|
||||
/*
|
||||
Copyright (c) 2026 - SD-Innovation S.A.S. - FRANCE
|
||||
*/
|
||||
|
||||
/*
|
||||
ver: 2.x.x - build: 2026-04-28
|
||||
*/
|
||||
|
||||
/*
|
||||
The zlib License
|
||||
|
||||
Copyright (c) 2026 SD-Innovation S.A.S.
|
||||
|
||||
This software is provided ‘as-is’, without any express or implied
|
||||
warranty. In no event will the authors be held liable for any damages
|
||||
arising from the use of this software.
|
||||
|
||||
Permission is granted to anyone to use this software for any purpose,
|
||||
including commercial applications, and to alter it and redistribute it
|
||||
freely, subject to the following restrictions:
|
||||
|
||||
1. The origin of this software must not be misrepresented; you must not
|
||||
claim that you wrote the original software. If you use this software
|
||||
in a product, an acknowledgment in the product documentation would be
|
||||
appreciated but is not required.
|
||||
|
||||
2. Altered source versions must be plainly marked as such, and must not be
|
||||
misrepresented as being the original software.
|
||||
|
||||
3. This notice may not be removed or altered from any source
|
||||
distribution.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <array>
|
||||
#include <cstddef>
|
||||
#include <optional>
|
||||
|
||||
namespace sdi_toolBox::common::utils
|
||||
{
|
||||
//--------------------------------------------------------------
|
||||
/**
|
||||
* @brief Fixed-size ring (circular) buffer with compile-time capacity
|
||||
* and overwrite-on-full behavior.
|
||||
*
|
||||
* This template implements a simple circular buffer with a compile-time
|
||||
* fixed capacity. When the buffer is full and a new element is pushed,
|
||||
* the oldest element is overwritten.
|
||||
*
|
||||
* @tparam T Element type stored in the buffer.
|
||||
* @tparam CAPACITY Compile-time buffer capacity. Must be > 0.
|
||||
*
|
||||
* @note The class provides both optional-returning and out-parameter
|
||||
* overloads for pop/front/back to suit different runtime constraints.
|
||||
* @warning Instantiating with CAPACITY == 0 is forbidden (static_assert).
|
||||
*
|
||||
* @par Example:
|
||||
* @code{.cpp}
|
||||
* RingBuffer<int, 8> rb;
|
||||
* rb.push(1);
|
||||
* int v;
|
||||
* if (rb.pop(v))
|
||||
* {
|
||||
* ...
|
||||
* }
|
||||
* @endcode
|
||||
*/
|
||||
template<class T, std::size_t CAPACITY>
|
||||
class RingBuffer
|
||||
{
|
||||
static_assert(CAPACITY > 0, "CAPACITY must be > 0");
|
||||
|
||||
public:
|
||||
///@name Write operations
|
||||
///@{
|
||||
|
||||
/**
|
||||
* @brief Append a value to the buffer.
|
||||
*
|
||||
* If the buffer is full, the oldest element is overwritten.
|
||||
* @param value Value to append (copied).
|
||||
*
|
||||
* @return true if the value was added without overwriting, false if an overwrite occurred.
|
||||
*/
|
||||
bool push(const T &value);
|
||||
|
||||
///@}
|
||||
///@name Read operations
|
||||
///@{
|
||||
|
||||
/**
|
||||
* @brief Remove and return the oldest element from the buffer.
|
||||
*
|
||||
* @return std::optional<T> The oldest element if present, std::nullopt if empty.
|
||||
*/
|
||||
|
||||
std::optional<T> pop();
|
||||
/**
|
||||
* @brief Remove the oldest element and store it in the provided reference.
|
||||
*
|
||||
* @param value Output reference that receives the removed element.
|
||||
*
|
||||
* @return true if an element was removed, false if the buffer was empty.
|
||||
*/
|
||||
bool pop(T &value);
|
||||
|
||||
/**
|
||||
* @brief Return (without removing) the oldest element.
|
||||
*
|
||||
* @return std::optional<T> The oldest element if present, std::nullopt if empty.
|
||||
*/
|
||||
[[nodiscard]] std::optional<T> front() const;
|
||||
|
||||
/**
|
||||
* @brief Copy the oldest element into the provided reference without removing it.
|
||||
*
|
||||
* @param value Output reference that receives the element.
|
||||
*
|
||||
* @return true if the element was copied, false if the buffer is empty.
|
||||
*/
|
||||
[[nodiscard]] bool front(T &value) const;
|
||||
|
||||
/**
|
||||
* @brief Return (without removing) the newest element.
|
||||
*
|
||||
* @return std::optional<T> The newest element if present, std::nullopt if empty.
|
||||
*/
|
||||
[[nodiscard]] std::optional<T> back() const;
|
||||
|
||||
/**
|
||||
* @brief Copy the newest element into the provided reference without removing it.
|
||||
*
|
||||
* @param value Output reference that receives the element.
|
||||
*
|
||||
* @return true if the element was copied, false if the buffer is empty.
|
||||
*/
|
||||
[[nodiscard]] bool back(T &value) const;
|
||||
|
||||
///@}
|
||||
///@name State queries
|
||||
///@{
|
||||
|
||||
/**
|
||||
* @brief Check whether the buffer is empty.
|
||||
*
|
||||
* @return true if empty, false otherwise.
|
||||
*/
|
||||
[[nodiscard]] bool empty() const;
|
||||
|
||||
/**
|
||||
* @brief Check whether the buffer is full.
|
||||
*
|
||||
* @return true if full, false otherwise.
|
||||
*/
|
||||
[[nodiscard]] bool full() const;
|
||||
|
||||
/**
|
||||
* @brief Number of elements currently stored in the buffer.
|
||||
*
|
||||
* @return Current size (0 .. CAPACITY).
|
||||
*/
|
||||
[[nodiscard]] std::size_t size() const;
|
||||
|
||||
/**
|
||||
* @brief Compile-time capacity of the buffer.
|
||||
*
|
||||
* @return The maximum number of elements the buffer can hold.
|
||||
*/
|
||||
[[nodiscard]] constexpr std::size_t capacity() const;
|
||||
|
||||
///@}
|
||||
///@name Modifiers
|
||||
///@{
|
||||
|
||||
/**
|
||||
* @brief Clear the buffer and reset internal indices.
|
||||
*
|
||||
* After calling clear(), empty() returns true and size() returns 0.
|
||||
*/
|
||||
void clear();
|
||||
|
||||
///@}
|
||||
|
||||
private:
|
||||
///@name Internal helpers
|
||||
///@{
|
||||
|
||||
/**
|
||||
* @brief Compute the next index in a circular manner.
|
||||
*
|
||||
* Implemented to avoid expensive modulus operations on some targets.
|
||||
* @param index Current index.
|
||||
*
|
||||
* @return Next index in range [0, CAPACITY-1].
|
||||
*/
|
||||
[[nodiscard]] std::size_t next(std::size_t index) const;
|
||||
|
||||
///@}
|
||||
///@name Data members
|
||||
///@{
|
||||
|
||||
std::array<T, CAPACITY> m_data{}; ///< Storage for the buffer elements
|
||||
std::size_t m_head{ 0 }; ///< Index of the next element to write
|
||||
std::size_t m_tail{ 0 }; ///< Index of the next element to read
|
||||
bool m_full{ false }; ///< Indicates whether the buffer is full
|
||||
|
||||
///@}
|
||||
};
|
||||
//--------------------------------------------------------------
|
||||
|
||||
//--------------------------------------------------------------
|
||||
/* Append a value to the buffer. If the buffer is full, the
|
||||
* oldest value will be overwritten. Return true if the value
|
||||
* was added without overwriting, false if an overwrite occurred. */
|
||||
template<class T, std::size_t CAPACITY>
|
||||
bool RingBuffer<T, CAPACITY>::push(const T &value)
|
||||
{
|
||||
m_data[m_head] = value;
|
||||
m_head = next(m_head);
|
||||
|
||||
if (m_full)
|
||||
{
|
||||
m_tail = m_head; // overwrite the oldest value
|
||||
return false; // indicate that an overwrite occurred
|
||||
}
|
||||
else
|
||||
{
|
||||
m_full = (m_head == m_tail);
|
||||
return true; // indicate that the value was added without overwriting
|
||||
}
|
||||
}
|
||||
//--------------------------------------------------------------
|
||||
/* Remove and return the oldest value from the buffer. If the
|
||||
* buffer is empty, return std::nullopt */
|
||||
template<class T, std::size_t CAPACITY>
|
||||
std::optional<T> RingBuffer<T, CAPACITY>::pop()
|
||||
{
|
||||
if (empty())
|
||||
return std::nullopt;
|
||||
const std::optional<T> result = m_data[m_tail];
|
||||
m_tail = next(m_tail);
|
||||
m_full = false;
|
||||
return result;
|
||||
}
|
||||
//--------------------------------------------------------------
|
||||
/* Remove the oldest value from the buffer and store it in
|
||||
* 'value'. Return true if successful, false if the buffer is empty */
|
||||
template<class T, std::size_t CAPACITY>
|
||||
bool RingBuffer<T, CAPACITY>::pop(T &value)
|
||||
{
|
||||
if (empty())
|
||||
return false;
|
||||
|
||||
value = m_data[m_tail];
|
||||
m_tail = next(m_tail);
|
||||
m_full = false;
|
||||
return true;
|
||||
}
|
||||
//--------------------------------------------------------------
|
||||
/* Return the oldest value without removing it. If the buffer is
|
||||
* empty, return std::nullopt */
|
||||
template<class T, std::size_t CAPACITY>
|
||||
std::optional<T> RingBuffer<T, CAPACITY>::front() const
|
||||
{
|
||||
if (empty())
|
||||
return std::nullopt;
|
||||
return m_data[m_tail];
|
||||
}
|
||||
//--------------------------------------------------------------
|
||||
/* Return the oldest value without removing it and store it in
|
||||
* 'value'. Return true if successful, false if the buffer is empty */
|
||||
template<class T, std::size_t CAPACITY>
|
||||
bool RingBuffer<T, CAPACITY>::front(T &value) const
|
||||
{
|
||||
if (empty())
|
||||
return false;
|
||||
value = m_data[m_tail];
|
||||
return true;
|
||||
}
|
||||
//--------------------------------------------------------------
|
||||
/* Return the newest value without removing it. If the buffer is
|
||||
* empty, return std::nullopt */
|
||||
template<class T, std::size_t CAPACITY>
|
||||
std::optional<T> RingBuffer<T, CAPACITY>::back() const
|
||||
{
|
||||
if (empty())
|
||||
return std::nullopt;
|
||||
return m_data[(m_head == 0) ? CAPACITY - 1 : m_head - 1];
|
||||
}
|
||||
//--------------------------------------------------------------
|
||||
/* Return the newest value without removing it and store it in
|
||||
* 'value'. Return true if successful, false if the buffer is empty */
|
||||
template<class T, std::size_t CAPACITY>
|
||||
bool RingBuffer<T, CAPACITY>::back(T &value) const
|
||||
{
|
||||
if (empty())
|
||||
return false;
|
||||
std::size_t backIndex = (m_head == 0) ? CAPACITY - 1 : m_head - 1;
|
||||
value = m_data[backIndex];
|
||||
return true;
|
||||
}
|
||||
//--------------------------------------------------------------
|
||||
/* Check if the buffer is empty */
|
||||
template<class T, std::size_t CAPACITY>
|
||||
bool RingBuffer<T, CAPACITY>::empty() const
|
||||
{
|
||||
return !m_full && (m_head == m_tail);
|
||||
}
|
||||
//--------------------------------------------------------------
|
||||
/* Check if the buffer is full */
|
||||
template<class T, std::size_t CAPACITY>
|
||||
bool RingBuffer<T, CAPACITY>::full() const
|
||||
{
|
||||
return m_full;
|
||||
}
|
||||
//--------------------------------------------------------------
|
||||
/* Get the number of elements currently in the buffer */
|
||||
template<class T, std::size_t CAPACITY>
|
||||
std::size_t RingBuffer<T, CAPACITY>::size() const
|
||||
{
|
||||
if (m_full)
|
||||
return CAPACITY;
|
||||
if (m_head >= m_tail)
|
||||
return m_head - m_tail;
|
||||
return CAPACITY - (m_tail - m_head);
|
||||
}
|
||||
//--------------------------------------------------------------
|
||||
/* Get the maximum capacity of the buffer */
|
||||
template<class T, std::size_t CAPACITY>
|
||||
[[nodiscard]] constexpr std::size_t RingBuffer<T, CAPACITY>::capacity() const
|
||||
{
|
||||
return CAPACITY;
|
||||
}
|
||||
//--------------------------------------------------------------
|
||||
/* Clear the buffer, resetting it to an empty state */
|
||||
template<class T, std::size_t CAPACITY>
|
||||
void RingBuffer<T, CAPACITY>::clear()
|
||||
{
|
||||
m_head = 0;
|
||||
m_tail = 0;
|
||||
m_full = false;
|
||||
}
|
||||
//--------------------------------------------------------------
|
||||
/* Helper function to calculate the next index in a circular manner */
|
||||
template<class T, std::size_t CAPACITY>
|
||||
std::size_t RingBuffer<T, CAPACITY>::next(const std::size_t index) const
|
||||
{
|
||||
// Calculate the next index in a circular manner without using
|
||||
// modulus operator for better performance
|
||||
const auto nextIndex = index + 1;
|
||||
if (nextIndex == CAPACITY)
|
||||
return 0;
|
||||
|
||||
return nextIndex;
|
||||
}
|
||||
//--------------------------------------------------------------
|
||||
} // namespace sdi_toolBox::common::utils
|
||||
529
sdi_toolBox_2.x.x/toolBox/sdi_toolBox/desktop/eventBus/bus.h
Normal file
529
sdi_toolBox_2.x.x/toolBox/sdi_toolBox/desktop/eventBus/bus.h
Normal file
@@ -0,0 +1,529 @@
|
||||
/*
|
||||
Copyright (c) 2026 - SD-Innovation S.A.S. - FRANCE
|
||||
*/
|
||||
|
||||
/*
|
||||
ver: 2.x.x - build: 2026-04-28
|
||||
*/
|
||||
|
||||
/*
|
||||
The zlib License
|
||||
|
||||
Copyright (c) 2026 SD-Innovation S.A.S.
|
||||
|
||||
This software is provided ‘as-is’, without any express or implied
|
||||
warranty. In no event will the authors be held liable for any damages
|
||||
arising from the use of this software.
|
||||
|
||||
Permission is granted to anyone to use this software for any purpose,
|
||||
including commercial applications, and to alter it and redistribute it
|
||||
freely, subject to the following restrictions:
|
||||
|
||||
1. The origin of this software must not be misrepresented; you must not
|
||||
claim that you wrote the original software. If you use this software
|
||||
in a product, an acknowledgment in the product documentation would be
|
||||
appreciated but is not required.
|
||||
|
||||
2. Altered source versions must be plainly marked as such, and must not be
|
||||
misrepresented as being the original software.
|
||||
|
||||
3. This notice may not be removed or altered from any source
|
||||
distribution.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "defs.h"
|
||||
#include "inode.h"
|
||||
#include "message.h"
|
||||
|
||||
#include <chrono>
|
||||
#include <memory>
|
||||
#include <mutex>
|
||||
#include <ranges>
|
||||
#include <set>
|
||||
#include <unordered_map>
|
||||
|
||||
namespace sdi_toolBox::desktop::eventBus
|
||||
{
|
||||
//--------------------------------------------------------------
|
||||
/**
|
||||
* @class Bus
|
||||
* @brief Central message dispatcher for the event bus system.
|
||||
*
|
||||
* The Bus class is the core component of the event bus architecture.
|
||||
* It manages a routing table that maps message type identifiers to lists
|
||||
* of subscribed nodes, and dispatches messages to the appropriate nodes
|
||||
* when they are emitted or posted.
|
||||
*
|
||||
* Nodes can subscribe to specific message types or to broadcast mode,
|
||||
* in which case they receive all messages regardless of their type.
|
||||
*
|
||||
* The Bus is thread-safe: all operations on the routing table are
|
||||
* protected by an internal mutex.
|
||||
*
|
||||
* @note The Bus is non-copyable and non-movable.
|
||||
* @note The Bus does not take ownership of the nodes it manages.
|
||||
*
|
||||
* @par Example usage:
|
||||
* @code
|
||||
* sdi_toolBox::desktop::eventBus::Bus bus;
|
||||
* sdi_toolBox::desktop::eventBus::Node node(bus);
|
||||
*
|
||||
* node.subscribe(MY_EVENT_TYPE);
|
||||
* bus.emit<MyMessage>(arg1, arg2);
|
||||
*
|
||||
* auto msg = std::dynamic_pointer_cast<MyMessage>(node.popMessage());
|
||||
* @endcode
|
||||
*
|
||||
* @see Node
|
||||
* @see INode
|
||||
* @see Message
|
||||
*/
|
||||
class Bus final
|
||||
{
|
||||
friend class Node; ///< Allow the Node class to access private members
|
||||
|
||||
using VectorNode = std::vector<INode *>;
|
||||
|
||||
public:
|
||||
///@name Construction & Destruction
|
||||
///@{
|
||||
|
||||
/**
|
||||
* @brief Default constructor.
|
||||
*
|
||||
* Initializes the Bus and records the construction timestamp
|
||||
* used as the bus start reference time.
|
||||
*/
|
||||
Bus();
|
||||
|
||||
/**
|
||||
* @brief Default destructor.
|
||||
*/
|
||||
~Bus() = default;
|
||||
|
||||
/**
|
||||
* @brief Copy constructor - deleted.
|
||||
*
|
||||
* The Bus is non-copyable.
|
||||
*/
|
||||
Bus(const Bus &obj) = delete;
|
||||
|
||||
/**
|
||||
* @brief Move constructor - deleted.
|
||||
*
|
||||
* The Bus is non-movable.
|
||||
*/
|
||||
Bus(Bus &&obj) noexcept = delete;
|
||||
|
||||
/**
|
||||
* @brief Copy assignment operator - deleted.
|
||||
*
|
||||
* The Bus is non-copyable.
|
||||
*/
|
||||
Bus &operator=(const Bus &obj) = delete;
|
||||
|
||||
/**
|
||||
* @brief Move assignment operator - deleted.
|
||||
*
|
||||
* The Bus is non-movable.
|
||||
*/
|
||||
Bus &operator=(Bus &&obj) noexcept = delete;
|
||||
|
||||
///@}
|
||||
///@name Subscription Management
|
||||
///@{
|
||||
|
||||
/**
|
||||
* @brief Remove all subscriptions from the routing table.
|
||||
*
|
||||
* Clears both the specific event subscriptions and the broadcast
|
||||
* subscription list. After this call, no node will receive any message
|
||||
* until it re-subscribes.
|
||||
*
|
||||
* @note This operation is thread-safe.
|
||||
*/
|
||||
void clearAllSubscriptions();
|
||||
|
||||
/**
|
||||
* @brief Subscribe a node to a specific message type.
|
||||
*
|
||||
* Registers the given node to receive messages of the specified type.
|
||||
* If the node is already subscribed to this type, this call has no effect
|
||||
* (no duplicates are created).
|
||||
*
|
||||
* @param node Pointer to the node to subscribe. Must not be @c nullptr.
|
||||
* @param eventType The message type identifier to subscribe to.
|
||||
*
|
||||
* @throws std::runtime_error if @p node is @c nullptr.
|
||||
* @note This operation is thread-safe.
|
||||
*/
|
||||
void subscribe(INode *node, MessageTypeID eventType);
|
||||
|
||||
/**
|
||||
* @brief Unsubscribe a node from a specific message type.
|
||||
*
|
||||
* Removes the given node from the list of subscribers for the specified
|
||||
* message type. If the node was not subscribed to this type, this call
|
||||
* has no effect.
|
||||
*
|
||||
* @param node Pointer to the node to unsubscribe. Must not be @c nullptr.
|
||||
* @param eventType The message type identifier to unsubscribe from.
|
||||
*
|
||||
* @throws std::runtime_error if @p node is @c nullptr.
|
||||
* @note This operation is thread-safe.
|
||||
*/
|
||||
void unsubscribe(INode *node, MessageTypeID eventType);
|
||||
|
||||
/**
|
||||
* @brief Unsubscribe a node from all message types and broadcast mode.
|
||||
*
|
||||
* Removes the given node from all specific event subscription lists
|
||||
* and from the broadcast subscription list. This is automatically called
|
||||
* by the Node destructor to ensure no dangling pointers remain in the
|
||||
* routing table.
|
||||
*
|
||||
* @param node Pointer to the node to unsubscribe. Must not be @c nullptr.
|
||||
*
|
||||
* @throws std::runtime_error if @p node is @c nullptr.
|
||||
* @note This operation is thread-safe.
|
||||
*/
|
||||
void unsubscribeFromAll(INode *node);
|
||||
|
||||
/**
|
||||
* @brief Check whether a node is subscribed to a specific message type.
|
||||
*
|
||||
* @param node Pointer to the node to check. Must not be @c nullptr.
|
||||
* @param eventType The message type identifier to check.
|
||||
* @return @c true if the node is subscribed to the given message type,
|
||||
* @c false otherwise.
|
||||
*
|
||||
* @throws std::runtime_error if @p node is @c nullptr.
|
||||
* @note This operation is thread-safe.
|
||||
*/
|
||||
[[nodiscard]] bool isSubscribed(const INode *node, MessageTypeID eventType) const;
|
||||
|
||||
///@}
|
||||
///@name Broadcast management
|
||||
///@{
|
||||
|
||||
/**
|
||||
* @brief Subscribe a node to broadcast mode.
|
||||
*
|
||||
* A node in broadcast mode receives all messages posted to the bus,
|
||||
* regardless of their type. A node can be subscribed to both broadcast
|
||||
* mode and specific message types simultaneously, in which case it will
|
||||
* receive the message twice for matching types.
|
||||
*
|
||||
* @param node Pointer to the node to subscribe. Must not be @c nullptr.
|
||||
*
|
||||
* @throws std::runtime_error if @p node is @c nullptr.
|
||||
* @note This operation is thread-safe.
|
||||
*/
|
||||
void subscribeToBroadcast(INode *node);
|
||||
|
||||
/**
|
||||
* @brief Unsubscribe a node from broadcast mode.
|
||||
*
|
||||
* Removes the given node from the broadcast subscription list.
|
||||
* If the node was not subscribed to broadcast mode, this call has no effect.
|
||||
*
|
||||
* @param node Pointer to the node to unsubscribe. Must not be @c nullptr.
|
||||
*
|
||||
* @throws std::runtime_error if @p node is @c nullptr.
|
||||
* @note This operation is thread-safe.
|
||||
*/
|
||||
void unsubscribeFromBroadcast(INode *node);
|
||||
|
||||
/**
|
||||
* @brief Check whether a node is subscribed to broadcast mode.
|
||||
*
|
||||
* @param node Pointer to the node to check. Must not be @c nullptr.
|
||||
* @return @c true if the node is subscribed to broadcast mode,
|
||||
* @c false otherwise.
|
||||
*
|
||||
* @throws std::runtime_error if @p node is @c nullptr.
|
||||
* @note This operation is thread-safe.
|
||||
*/
|
||||
[[nodiscard]] bool isSubscribedToBroadcast(INode *node) const; // Check if a node is subscribed to broadcast mode
|
||||
|
||||
///@}
|
||||
///@name Message transmission
|
||||
///@{
|
||||
|
||||
/**
|
||||
* @brief Construct and emit a message of type @p T to the bus.
|
||||
*
|
||||
* Creates a new message of type @p T by forwarding the provided arguments
|
||||
* to its constructor, then posts it to the bus via @ref post().
|
||||
*
|
||||
* @tparam T The message type to emit. Must be derived from @ref Message.
|
||||
* @tparam Args Constructor argument types for @p T.
|
||||
* @param args Arguments forwarded to the constructor of @p T.
|
||||
* @return @c true if at least one subscriber received the message,
|
||||
* @c false otherwise.
|
||||
*
|
||||
* @note Enforced at compile time: @p T must derive from @ref Message.
|
||||
* @note This operation is thread-safe.
|
||||
*
|
||||
* @par Example:
|
||||
* @code
|
||||
* bus.emit<MyMessage>(arg1, arg2);
|
||||
* @endcode
|
||||
*/
|
||||
template<class T, class... Args>
|
||||
bool emit(Args &&...args);
|
||||
|
||||
/**
|
||||
* @brief Post an already constructed message to the bus.
|
||||
*
|
||||
* Updates the message timestamp and dispatches it to all nodes subscribed
|
||||
* to the message type, as well as all nodes in broadcast mode.
|
||||
*
|
||||
* @param message Shared pointer to the message to post. Must not be @c nullptr.
|
||||
* @return @c true if at least one specific subscriber received the message,
|
||||
* @c false otherwise.
|
||||
*
|
||||
* @note This operation is thread-safe.
|
||||
* @see emit()
|
||||
*/
|
||||
bool post(const std::shared_ptr<Message> &message) const; // Post a message to the bus
|
||||
|
||||
///@}
|
||||
|
||||
private:
|
||||
/**
|
||||
* @brief Dispatch a message to all nodes subscribed to its type.
|
||||
* @param eventType The message type identifier.
|
||||
* @param message The message to dispatch.
|
||||
* @return The number of nodes that received the message.
|
||||
*/
|
||||
size_t postMessageToSubscribers(MessageTypeID eventType, const std::shared_ptr<Message> &message) const;
|
||||
|
||||
/**
|
||||
* @brief Dispatch a message to all nodes in broadcast mode.
|
||||
* @param message The message to dispatch.
|
||||
* @return The number of broadcast nodes that received the message.
|
||||
*/
|
||||
size_t postMessageToBroadcastSubscribers(const std::shared_ptr<Message> &message) const;
|
||||
|
||||
/**
|
||||
* @brief Retrieve the subscriber list for a given message type.
|
||||
* @param messageType The message type identifier.
|
||||
* @return Pointer to the vector of subscribed nodes, or @c nullptr if none.
|
||||
*/
|
||||
const VectorNode *getSubscribersForMessageType(MessageTypeID messageType) const; // Get the list of subscribers for a specific message type
|
||||
|
||||
/// @brief Timestamp recorded when the Bus was constructed.
|
||||
TimePoint m_busStartTimestamp;
|
||||
|
||||
/// @brief Internal routing table, protected by a mutex for thread-safe access.
|
||||
struct
|
||||
{
|
||||
mutable std::mutex mtx; ///< Mutex for thread-safe access to the nodes map
|
||||
std::unordered_map<MessageTypeID, VectorNode> nodeList; ///< Map of message type IDs to lists of subscribed nodes
|
||||
std::set<INode *> broadcastNodeList; ///< Set of nodes subscribed to receive all messages (broadcast mode)
|
||||
} m_routingTable;
|
||||
};
|
||||
//--------------------------------------------------------------
|
||||
|
||||
//--------------------------------------------------------------
|
||||
/* Default constructor */
|
||||
inline Bus::Bus()
|
||||
{
|
||||
// Initialization
|
||||
m_busStartTimestamp = std::chrono::steady_clock::now();
|
||||
}
|
||||
//--------------------------------------------------------------
|
||||
/* Clear all subscriptions from the bus (remove all nodes from the routing table) */
|
||||
inline void Bus::clearAllSubscriptions()
|
||||
{
|
||||
std::scoped_lock lock(m_routingTable.mtx);
|
||||
|
||||
m_routingTable.nodeList.clear(); // Clear all event subscriptions
|
||||
m_routingTable.broadcastNodeList.clear(); // Clear all broadcast subscriptions
|
||||
}
|
||||
//--------------------------------------------------------------
|
||||
/* Subscribe a listener to a specific event type */
|
||||
inline void Bus::subscribe(INode *node, MessageTypeID eventType)
|
||||
{
|
||||
// Sanity check
|
||||
if (!node)
|
||||
throw std::runtime_error("invalid node handle");
|
||||
|
||||
std::scoped_lock lock(m_routingTable.mtx);
|
||||
|
||||
if (!m_routingTable.nodeList.contains(eventType)) // Event type not registered yet, create a new entry with the node
|
||||
{
|
||||
m_routingTable.nodeList[eventType] = { node };
|
||||
}
|
||||
else // Event type already registered, add the node if not already subscribed
|
||||
{
|
||||
auto &nodes = m_routingTable.nodeList.at(eventType);
|
||||
if (std::ranges::find(nodes, node) == nodes.end())
|
||||
{
|
||||
// Node not already subscribed, add it to the list
|
||||
nodes.push_back(node);
|
||||
}
|
||||
}
|
||||
}
|
||||
//--------------------------------------------------------------
|
||||
/* Unsubscribe a listener from a specific event type */
|
||||
inline void Bus::unsubscribe(INode *node, MessageTypeID eventType)
|
||||
{
|
||||
// Sanity check
|
||||
if (!node)
|
||||
throw std::runtime_error("invalid node handle");
|
||||
|
||||
std::scoped_lock lock(m_routingTable.mtx);
|
||||
|
||||
// Event type not recorded, nothing to do
|
||||
if (!m_routingTable.nodeList.contains(eventType))
|
||||
return;
|
||||
|
||||
// Event type registered, remove the listener if subscribed
|
||||
auto &nodes = m_routingTable.nodeList.at(eventType);
|
||||
std::erase(nodes, node);
|
||||
}
|
||||
//--------------------------------------------------------------
|
||||
/* Unsubscribe a listener from a specific event type */
|
||||
inline void Bus::unsubscribeFromAll(INode *node)
|
||||
{
|
||||
// Sanity check
|
||||
if (!node)
|
||||
throw std::runtime_error("invalid node handle");
|
||||
|
||||
std::scoped_lock lock(m_routingTable.mtx);
|
||||
|
||||
// Iterate through all event types and remove the node from each list
|
||||
for (auto &nodeList : m_routingTable.nodeList | std::views::values)
|
||||
std::erase(nodeList, node);
|
||||
|
||||
// Remove from broadcast subscriptions as well
|
||||
m_routingTable.broadcastNodeList.erase(node);
|
||||
}
|
||||
//--------------------------------------------------------------
|
||||
/* Check if a listener is subscribed to a specific event type */
|
||||
inline bool Bus::isSubscribed(const INode *node, MessageTypeID eventType) const
|
||||
{
|
||||
// Sanity check
|
||||
if (!node)
|
||||
throw std::runtime_error("invalid node handle");
|
||||
|
||||
std::scoped_lock lock(m_routingTable.mtx);
|
||||
|
||||
// Event type not recorded, node not subscribed
|
||||
if (!m_routingTable.nodeList.contains(eventType))
|
||||
return false;
|
||||
|
||||
// Event type recorded, check if the node is in the list
|
||||
const auto &nodes = m_routingTable.nodeList.at(eventType);
|
||||
return std::ranges::find(nodes, node) != nodes.end();
|
||||
}
|
||||
//--------------------------------------------------------------
|
||||
/* Subscribe a node to receive all messages (broadcast mode) */
|
||||
inline void Bus::subscribeToBroadcast(INode *node)
|
||||
{
|
||||
// Sanity check
|
||||
if (!node)
|
||||
throw std::runtime_error("invalid node handle");
|
||||
|
||||
std::scoped_lock lock(m_routingTable.mtx);
|
||||
|
||||
// Add the node to the broadcast nodes set
|
||||
m_routingTable.broadcastNodeList.insert(node);
|
||||
}
|
||||
//--------------------------------------------------------------
|
||||
/* Unsubscribe a node from broadcast mode */
|
||||
inline void Bus::unsubscribeFromBroadcast(INode *node)
|
||||
{
|
||||
// Sanity check
|
||||
if (!node)
|
||||
throw std::runtime_error("invalid node handle");
|
||||
|
||||
std::scoped_lock lock(m_routingTable.mtx);
|
||||
|
||||
// Remove the node from the broadcast nodes set
|
||||
m_routingTable.broadcastNodeList.erase(node);
|
||||
}
|
||||
//--------------------------------------------------------------
|
||||
/* Check if a node is subscribed to broadcast mode */
|
||||
inline bool Bus::isSubscribedToBroadcast(INode *node) const
|
||||
{
|
||||
// Sanity check
|
||||
if (!node)
|
||||
throw std::runtime_error("invalid node handle");
|
||||
|
||||
std::scoped_lock lock(m_routingTable.mtx);
|
||||
|
||||
// Check if the node is in the broadcast nodes set
|
||||
return m_routingTable.broadcastNodeList.contains(node);
|
||||
}
|
||||
//--------------------------------------------------------------
|
||||
/* Emit a message of type T with the given arguments(create a message and post it to the bus) */
|
||||
template<class T, class... Args>
|
||||
bool Bus::emit(Args &&...args)
|
||||
{
|
||||
static_assert(std::derived_from<T, Message>, "T must be derived from IMessage");
|
||||
|
||||
// Create a message of type T with the given arguments
|
||||
auto message = std::make_shared<T>(std::forward<Args>(args)...);
|
||||
|
||||
// Post the message to the bus
|
||||
return post(message);
|
||||
}
|
||||
//--------------------------------------------------------------
|
||||
/* Post a message to the bus */
|
||||
inline bool Bus::post(const std::shared_ptr<Message> &message) const
|
||||
{
|
||||
std::scoped_lock lock(m_routingTable.mtx);
|
||||
|
||||
// Update message state
|
||||
message->updateTimestamp(); // Update the timestamp of when the message was posted to the bus
|
||||
|
||||
// Post the message to all subscribers of the specific
|
||||
// message type and get the number of subscribers that
|
||||
// received the message
|
||||
const auto subscriberCount = postMessageToSubscribers(message->getMessageTypeID(), message);
|
||||
|
||||
// Post the message to all broadcast subscribers and
|
||||
// get the number of subscribers that received the
|
||||
// message
|
||||
(void)postMessageToBroadcastSubscribers(message);
|
||||
|
||||
return subscriberCount > 0;
|
||||
}
|
||||
//--------------------------------------------------------------
|
||||
/* Post a message to all subscribers of a specific event type and return the number of subscribers that received the message */
|
||||
inline size_t Bus::postMessageToSubscribers(const MessageTypeID eventType, const std::shared_ptr<Message> &message) const
|
||||
{
|
||||
const auto subscriberList = getSubscribersForMessageType(eventType);
|
||||
if (!subscriberList)
|
||||
return 0;
|
||||
|
||||
for (const auto &node : *subscriberList)
|
||||
node->append(message);
|
||||
|
||||
return subscriberList->size();
|
||||
}
|
||||
//--------------------------------------------------------------
|
||||
/* Post a message to all broadcast subscribers and return the number of subscribers that received the message */
|
||||
inline size_t Bus::postMessageToBroadcastSubscribers(const std::shared_ptr<Message> &message) const
|
||||
{
|
||||
for (const auto &node : m_routingTable.broadcastNodeList)
|
||||
node->append(message);
|
||||
|
||||
return m_routingTable.broadcastNodeList.size();
|
||||
}
|
||||
//--------------------------------------------------------------
|
||||
/* Get the list of subscribers for a specific message type */
|
||||
inline const Bus::VectorNode *Bus::getSubscribersForMessageType(const MessageTypeID messageType) const
|
||||
{
|
||||
if (!m_routingTable.nodeList.contains(messageType))
|
||||
return nullptr; // No subscribers for this message type
|
||||
|
||||
return &m_routingTable.nodeList.at(messageType);
|
||||
}
|
||||
//--------------------------------------------------------------
|
||||
} // namespace sdi_toolBox::desktop::eventBus
|
||||
@@ -0,0 +1,45 @@
|
||||
/*
|
||||
Copyright (c) 2026 - SD-Innovation S.A.S. - FRANCE
|
||||
*/
|
||||
|
||||
/*
|
||||
ver: 2.x.x - build: 2026-04-28
|
||||
*/
|
||||
|
||||
/*
|
||||
The zlib License
|
||||
|
||||
Copyright (c) 2026 SD-Innovation S.A.S.
|
||||
|
||||
This software is provided ‘as-is’, without any express or implied
|
||||
warranty. In no event will the authors be held liable for any damages
|
||||
arising from the use of this software.
|
||||
|
||||
Permission is granted to anyone to use this software for any purpose,
|
||||
including commercial applications, and to alter it and redistribute it
|
||||
freely, subject to the following restrictions:
|
||||
|
||||
1. The origin of this software must not be misrepresented; you must not
|
||||
claim that you wrote the original software. If you use this software
|
||||
in a product, an acknowledgment in the product documentation would be
|
||||
appreciated but is not required.
|
||||
|
||||
2. Altered source versions must be plainly marked as such, and must not be
|
||||
misrepresented as being the original software.
|
||||
|
||||
3. This notice may not be removed or altered from any source
|
||||
distribution.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <chrono>
|
||||
#include <cstdint>
|
||||
|
||||
//--------------------------------------------------------------
|
||||
namespace sdi_toolBox::desktop::eventBus
|
||||
{
|
||||
using MessageTypeID = uint64_t; ///< Unique identifier for a message type
|
||||
using TimePoint = std::chrono::steady_clock::time_point; ///< Monotonic timestamp type used throughout the event bus
|
||||
//--------------------------------------------------------------
|
||||
} // namespace sdi_toolBox::desktop::eventBus
|
||||
128
sdi_toolBox_2.x.x/toolBox/sdi_toolBox/desktop/eventBus/inode.h
Normal file
128
sdi_toolBox_2.x.x/toolBox/sdi_toolBox/desktop/eventBus/inode.h
Normal file
@@ -0,0 +1,128 @@
|
||||
/*
|
||||
Copyright (c) 2026 - SD-Innovation S.A.S. - FRANCE
|
||||
*/
|
||||
|
||||
/*
|
||||
ver: 2.x.x - build: 2026-04-28
|
||||
*/
|
||||
|
||||
/*
|
||||
The zlib License
|
||||
|
||||
Copyright (c) 2026 SD-Innovation S.A.S.
|
||||
|
||||
This software is provided ‘as-is’, without any express or implied
|
||||
warranty. In no event will the authors be held liable for any damages
|
||||
arising from the use of this software.
|
||||
|
||||
Permission is granted to anyone to use this software for any purpose,
|
||||
including commercial applications, and to alter it and redistribute it
|
||||
freely, subject to the following restrictions:
|
||||
|
||||
1. The origin of this software must not be misrepresented; you must not
|
||||
claim that you wrote the original software. If you use this software
|
||||
in a product, an acknowledgment in the product documentation would be
|
||||
appreciated but is not required.
|
||||
|
||||
2. Altered source versions must be plainly marked as such, and must not be
|
||||
misrepresented as being the original software.
|
||||
|
||||
3. This notice may not be removed or altered from any source
|
||||
distribution.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "message.h"
|
||||
|
||||
namespace sdi_toolBox::desktop::eventBus
|
||||
{
|
||||
//--------------------------------------------------------------
|
||||
/**
|
||||
* @class INode
|
||||
* @brief Abstract interface representing a subscriber node in the event bus system.
|
||||
*
|
||||
* INode is the base interface that all subscriber nodes must implement to
|
||||
* participate in the event bus. It exposes a single private pure virtual method,
|
||||
* @ref append(), which is called exclusively by the @ref Bus when a message is
|
||||
* dispatched to this node.
|
||||
*
|
||||
* The @ref Bus is declared as a friend class to allow it to invoke @ref append()
|
||||
* without exposing it to the rest of the codebase, enforcing a strict
|
||||
* encapsulation of the message delivery mechanism.
|
||||
*
|
||||
* @note INode is non-copyable and non-movable.
|
||||
* @note Direct instantiation is not possible - this class must be subclassed.
|
||||
* The concrete implementation is provided by @ref Node.
|
||||
*
|
||||
* @see Bus
|
||||
* @see Node
|
||||
* @see Message
|
||||
*/
|
||||
class INode
|
||||
{
|
||||
friend class Bus; ///< Allow the Bus class to access private members
|
||||
|
||||
public:
|
||||
///@name Construction & Destruction
|
||||
///@{
|
||||
|
||||
/**
|
||||
* @brief Default constructor.
|
||||
*/
|
||||
INode() = default;
|
||||
|
||||
/**
|
||||
* @brief Default destructor.
|
||||
*/
|
||||
virtual ~INode() = default;
|
||||
|
||||
/**
|
||||
* @brief Copy constructor - deleted.
|
||||
*
|
||||
* INode is non-copyable.
|
||||
*/
|
||||
INode(const INode &obj) = delete;
|
||||
|
||||
/**
|
||||
* @brief Move constructor - deleted.
|
||||
*
|
||||
* INode is non-movable.
|
||||
*/
|
||||
INode(INode &&obj) noexcept = delete;
|
||||
|
||||
/**
|
||||
* @brief Copy assignment operator - deleted.
|
||||
*
|
||||
* INode is non-copyable.
|
||||
*/
|
||||
INode &operator=(const INode &obj) = delete;
|
||||
|
||||
/**
|
||||
* @brief Move assignment operator - deleted.
|
||||
*
|
||||
* INode is non-movable.
|
||||
*/
|
||||
INode &operator=(INode &&obj) noexcept = delete;
|
||||
|
||||
///@}
|
||||
|
||||
private:
|
||||
/**
|
||||
* @brief Insert a message into the node's internal message queue.
|
||||
*
|
||||
* This method is called exclusively by the @ref Bus when a message matching
|
||||
* this node's subscriptions (or a broadcast message) is dispatched.
|
||||
* It must be implemented by all concrete subclasses to define how incoming
|
||||
* messages are stored or processed.
|
||||
*
|
||||
* @param message Shared pointer to the message being delivered.
|
||||
*
|
||||
* @note This method is intentionally private and only accessible to @ref Bus
|
||||
* via the friend declaration, preventing external code from injecting
|
||||
* messages directly into a node.
|
||||
*/
|
||||
virtual void append(const std::shared_ptr<Message> &message) = 0;
|
||||
};
|
||||
//--------------------------------------------------------------
|
||||
} // namespace sdi_toolBox::desktop::eventBus
|
||||
195
sdi_toolBox_2.x.x/toolBox/sdi_toolBox/desktop/eventBus/message.h
Normal file
195
sdi_toolBox_2.x.x/toolBox/sdi_toolBox/desktop/eventBus/message.h
Normal file
@@ -0,0 +1,195 @@
|
||||
/*
|
||||
Copyright (c) 2026 - SD-Innovation S.A.S. - FRANCE
|
||||
*/
|
||||
|
||||
/*
|
||||
ver: 2.x.x - build: 2026-04-28
|
||||
*/
|
||||
|
||||
/*
|
||||
The zlib License
|
||||
|
||||
Copyright (c) 2026 SD-Innovation S.A.S.
|
||||
|
||||
This software is provided ‘as-is’, without any express or implied
|
||||
warranty. In no event will the authors be held liable for any damages
|
||||
arising from the use of this software.
|
||||
|
||||
Permission is granted to anyone to use this software for any purpose,
|
||||
including commercial applications, and to alter it and redistribute it
|
||||
freely, subject to the following restrictions:
|
||||
|
||||
1. The origin of this software must not be misrepresented; you must not
|
||||
claim that you wrote the original software. If you use this software
|
||||
in a product, an acknowledgment in the product documentation would be
|
||||
appreciated but is not required.
|
||||
|
||||
2. Altered source versions must be plainly marked as such, and must not be
|
||||
misrepresented as being the original software.
|
||||
|
||||
3. This notice may not be removed or altered from any source
|
||||
distribution.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "defs.h"
|
||||
|
||||
namespace sdi_toolBox::desktop::eventBus
|
||||
{
|
||||
//--------------------------------------------------------------
|
||||
/**
|
||||
* @class Message
|
||||
* @brief Base class for all messages dispatched through the event bus.
|
||||
*
|
||||
* Every message circulating in the event bus system must derive from this class.
|
||||
* It carries a unique message type identifier (@ref MessageTypeID) used by the
|
||||
* @ref Bus to route the message to the appropriate subscribers, and a timestamp
|
||||
* that is updated when the message is posted to the bus.
|
||||
*
|
||||
* @note The Message class is non-copyable and non-movable.
|
||||
* @note The default constructor is deleted: a @ref MessageTypeID must always
|
||||
* be provided at construction time.
|
||||
* @note The timestamp is set by the @ref Bus internally when @ref Bus::post()
|
||||
* is called; it is not set at construction time.
|
||||
*
|
||||
* @par Example - defining a custom message:
|
||||
* @code
|
||||
* static constexpr sdi_toolBox::desktop::eventBus::MessageTypeID MY_EVENT = 1;
|
||||
*
|
||||
* struct MyMessage : public sdi_toolBox::desktop::eventBus::Message
|
||||
* {
|
||||
* explicit MyMessage(int value)
|
||||
* : Message(MY_EVENT)
|
||||
* , payload(value)
|
||||
* {}
|
||||
* int payload{};
|
||||
* };
|
||||
* @endcode
|
||||
*
|
||||
* @see Bus
|
||||
* @see MessageTypeID
|
||||
* @see TimePoint
|
||||
*/
|
||||
class Message
|
||||
{
|
||||
friend class Bus; ///< Allow the Bus class to access private members
|
||||
|
||||
public:
|
||||
///@name Construction & Destruction
|
||||
///@{
|
||||
|
||||
/**
|
||||
* @brief Default constructor - deleted.
|
||||
*
|
||||
* A @ref MessageTypeID must always be provided at construction time.
|
||||
*/
|
||||
Message() = delete;
|
||||
|
||||
/**
|
||||
* @brief Default destructor.
|
||||
*/
|
||||
virtual ~Message() = default;
|
||||
|
||||
/**
|
||||
* @brief Copy constructor - deleted.
|
||||
*
|
||||
* Message is non-copyable.
|
||||
*/
|
||||
Message(const Message &obj) = delete;
|
||||
|
||||
/**
|
||||
* @brief Move constructor - deleted.
|
||||
*
|
||||
* Message is non-movable.
|
||||
*/
|
||||
Message(Message &&obj) noexcept = delete;
|
||||
|
||||
/**
|
||||
* @brief Copy assignment operator - deleted.
|
||||
*
|
||||
* Message is non-copyable.
|
||||
*/
|
||||
Message &operator=(const Message &obj) = delete;
|
||||
|
||||
/**
|
||||
* @brief Move assignment operator - deleted.
|
||||
*
|
||||
* Message is non-movable.
|
||||
*/
|
||||
Message &operator=(Message &&obj) noexcept = delete;
|
||||
|
||||
/**
|
||||
* @brief Construct a message with the given type identifier.
|
||||
*
|
||||
* @param messageTypeID Unique identifier representing the type of this message.
|
||||
* Used by the @ref Bus to route the message to the correct
|
||||
* subscribers.
|
||||
*/
|
||||
explicit Message(MessageTypeID messageTypeID);
|
||||
|
||||
///@}
|
||||
///@name Accessors
|
||||
///@{
|
||||
|
||||
/**
|
||||
* @brief Get the unique type identifier of this message.
|
||||
*
|
||||
* @return The @ref MessageTypeID assigned at construction time.
|
||||
*/
|
||||
[[nodiscard]] MessageTypeID getMessageTypeID() const;
|
||||
|
||||
/**
|
||||
* @brief Get the timestamp of when this message was posted to the bus.
|
||||
*
|
||||
* The timestamp is recorded by the @ref Bus when @ref Bus::post() is called.
|
||||
* It is left at its default-constructed (zero) value if the message has not
|
||||
* yet been posted.
|
||||
*
|
||||
* @return A @ref TimePoint representing the moment the message was dispatched.
|
||||
* @see Bus::post()
|
||||
*/
|
||||
[[nodiscard]] TimePoint getTimestamp() const;
|
||||
|
||||
///@}
|
||||
|
||||
private:
|
||||
/**
|
||||
* @brief Update the message timestamp to the current time.
|
||||
*
|
||||
* Called internally by @ref Bus::post() just before the message is dispatched
|
||||
* to subscribers. Not accessible from outside the bus.
|
||||
*/
|
||||
void updateTimestamp();
|
||||
|
||||
MessageTypeID m_messageTypeID; ///< Unique identifier for the message type
|
||||
TimePoint m_messagePostTimestamp{}; ///< Timestamp of when the message was posted to the bus
|
||||
};
|
||||
//--------------------------------------------------------------
|
||||
|
||||
//--------------------------------------------------------------
|
||||
/* Constructor */
|
||||
inline Message::Message(const MessageTypeID messageTypeID)
|
||||
{
|
||||
m_messageTypeID = messageTypeID;
|
||||
}
|
||||
//--------------------------------------------------------------
|
||||
/* Get the unique identifier for the message type */
|
||||
inline MessageTypeID Message::getMessageTypeID() const
|
||||
{
|
||||
return m_messageTypeID;
|
||||
}
|
||||
//--------------------------------------------------------------
|
||||
/* Get the timestamp of when the message was posted to the bus */
|
||||
inline TimePoint Message::getTimestamp() const
|
||||
{
|
||||
return m_messagePostTimestamp;
|
||||
}
|
||||
//--------------------------------------------------------------
|
||||
/* Update the timestamp of when the message was posted to the bus */
|
||||
inline void Message::updateTimestamp()
|
||||
{
|
||||
m_messagePostTimestamp = std::chrono::steady_clock::now();
|
||||
}
|
||||
//--------------------------------------------------------------
|
||||
} // namespace sdi_toolBox::desktop::eventBus
|
||||
390
sdi_toolBox_2.x.x/toolBox/sdi_toolBox/desktop/eventBus/node.h
Normal file
390
sdi_toolBox_2.x.x/toolBox/sdi_toolBox/desktop/eventBus/node.h
Normal file
@@ -0,0 +1,390 @@
|
||||
/*
|
||||
Copyright (c) 2026 - SD-Innovation S.A.S. - FRANCE
|
||||
*/
|
||||
|
||||
/*
|
||||
ver: 2.x.x - build: 2026-04-28
|
||||
*/
|
||||
|
||||
/*
|
||||
The zlib License
|
||||
|
||||
Copyright (c) 2026 SD-Innovation S.A.S.
|
||||
|
||||
This software is provided ‘as-is’, without any express or implied
|
||||
warranty. In no event will the authors be held liable for any damages
|
||||
arising from the use of this software.
|
||||
|
||||
Permission is granted to anyone to use this software for any purpose,
|
||||
including commercial applications, and to alter it and redistribute it
|
||||
freely, subject to the following restrictions:
|
||||
|
||||
1. The origin of this software must not be misrepresented; you must not
|
||||
claim that you wrote the original software. If you use this software
|
||||
in a product, an acknowledgment in the product documentation would be
|
||||
appreciated but is not required.
|
||||
|
||||
2. Altered source versions must be plainly marked as such, and must not be
|
||||
misrepresented as being the original software.
|
||||
|
||||
3. This notice may not be removed or altered from any source
|
||||
distribution.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "bus.h"
|
||||
#include "inode.h"
|
||||
|
||||
#include <mutex>
|
||||
#include <queue>
|
||||
|
||||
namespace sdi_toolBox::desktop::eventBus
|
||||
{
|
||||
//--------------------------------------------------------------
|
||||
/**
|
||||
* @class Node
|
||||
* @brief Concrete subscriber node in the event bus system.
|
||||
*
|
||||
* Node is the concrete implementation of @ref INode. It represents a participant
|
||||
* in the event bus that can subscribe to specific message types or to broadcast
|
||||
* mode, emit and post messages through the bus, and consume received messages
|
||||
* from its internal FIFO queue.
|
||||
*
|
||||
* Each Node holds a reference to the @ref Bus it belongs to. Subscriptions and
|
||||
* message transmissions are delegated to the bus. Incoming messages are stored
|
||||
* in an internal thread-safe queue and can be retrieved via @ref popMessage().
|
||||
*
|
||||
* The Node also supports synchronous waiting: a thread can block on
|
||||
* @ref syncWaitForMessage() until at least one message is available in the queue.
|
||||
*
|
||||
* On destruction, the Node automatically unsubscribes from all event types and
|
||||
* broadcast mode, preventing dangling pointers in the bus routing table.
|
||||
*
|
||||
* @note Node is non-copyable and non-movable.
|
||||
* @note A @ref Bus reference must be provided at construction time.
|
||||
* @note The Node does not take ownership of the @ref Bus.
|
||||
*
|
||||
* @par Example usage:
|
||||
* @code
|
||||
* sdi_toolBox::desktop::eventBus::Bus bus;
|
||||
* sdi_toolBox::desktop::eventBus::Node node(bus);
|
||||
*
|
||||
* node.subscribe(MY_EVENT_TYPE);
|
||||
* node.emit<MyMessage>(42);
|
||||
*
|
||||
* node.syncWaitForMessage();
|
||||
* auto msg = std::dynamic_pointer_cast<MyMessage>(node.popMessage());
|
||||
* @endcode
|
||||
*
|
||||
* @see Bus
|
||||
* @see INode
|
||||
* @see Message
|
||||
*/
|
||||
class Node : public INode
|
||||
{
|
||||
public:
|
||||
///@name Construction & Destruction
|
||||
///@{
|
||||
|
||||
Node() = delete; ///< Default constructor - deleted. A @ref Bus reference must be provided.
|
||||
|
||||
/**
|
||||
* @brief Destructor.
|
||||
*
|
||||
* Automatically unsubscribes the node from all specific event types and
|
||||
* broadcast mode via @ref unsubscribeFromAll(), preventing dangling pointers
|
||||
* in the bus routing table. Also notifies any thread blocked in
|
||||
* @ref syncWaitForMessage() to unblock it gracefully.
|
||||
*/
|
||||
virtual ~Node();
|
||||
|
||||
/**
|
||||
* @brief Copy constructor - deleted.
|
||||
*
|
||||
* Node is non-copyable.
|
||||
*/
|
||||
Node(const Node &obj) = delete;
|
||||
|
||||
/**
|
||||
* @brief Move constructor - deleted.
|
||||
*
|
||||
* Node is non-movable.
|
||||
*/
|
||||
Node(Node &&obj) noexcept = delete;
|
||||
|
||||
/**
|
||||
* @brief Copy assignment operator - deleted.
|
||||
*
|
||||
* Node is non-copyable.
|
||||
*/
|
||||
Node &operator=(const Node &obj) = delete;
|
||||
|
||||
/**
|
||||
* @brief Move assignment operator - deleted.
|
||||
*
|
||||
* Node is non-movable.
|
||||
*/
|
||||
Node &operator=(Node &&obj) noexcept = delete;
|
||||
|
||||
/**
|
||||
* @brief Construct a Node attached to the given @ref Bus.
|
||||
*
|
||||
* @param bus Reference to the @ref Bus this node belongs to.
|
||||
* The bus must outlive the node.
|
||||
*/
|
||||
explicit Node(Bus &bus);
|
||||
|
||||
///@}
|
||||
///@name Synchronization
|
||||
///@{
|
||||
|
||||
/**
|
||||
* @brief Block the calling thread until a message is received.
|
||||
*
|
||||
* Suspends the calling thread using an atomic wait until at least one message
|
||||
* has been appended to the node's internal queue by the @ref Bus. This method
|
||||
* is intended for synchronous event-driven patterns where a thread should idle
|
||||
* until work is available.
|
||||
*
|
||||
* @note Returns immediately if a message is already pending in the queue
|
||||
* at the time of the call.
|
||||
* @note If the Node is destroyed while a thread is blocked here, the destructor
|
||||
* triggers a notification to unblock the waiting thread gracefully.
|
||||
* @warning The caller is responsible for checking the queue after this call
|
||||
* returns, as the notification may also be triggered by the destructor
|
||||
* with an empty queue.
|
||||
*/
|
||||
void syncWaitForMessage();
|
||||
|
||||
///@}
|
||||
///@name Subscription Management
|
||||
///@{
|
||||
|
||||
/**
|
||||
* @brief Subscribe this node to a specific message type.
|
||||
*
|
||||
* Delegates to @ref Bus::subscribe(). The node will receive all messages
|
||||
* of the given type posted to the bus. Duplicate subscriptions are ignored.
|
||||
*
|
||||
* @param eventType The message type identifier to subscribe to.
|
||||
* @see Bus::subscribe()
|
||||
*/
|
||||
void subscribe(MessageTypeID eventType);
|
||||
|
||||
/**
|
||||
* @brief Unsubscribe this node from a specific message type.
|
||||
*
|
||||
* Delegates to @ref Bus::unsubscribe(). If the node was not subscribed
|
||||
* to the given type, this call has no effect.
|
||||
*
|
||||
* @param eventType The message type identifier to unsubscribe from.
|
||||
* @see Bus::unsubscribe()
|
||||
*/
|
||||
void unsubscribe(MessageTypeID eventType);
|
||||
|
||||
/**
|
||||
* @brief Unsubscribe this node from all message types and broadcast mode.
|
||||
*
|
||||
* Delegates to @ref Bus::unsubscribeFromAll(). After this call, the node
|
||||
* will no longer receive any messages until it re-subscribes.
|
||||
*
|
||||
* @see Bus::unsubscribeFromAll()
|
||||
*/
|
||||
void unsubscribeFromAll();
|
||||
|
||||
///@}
|
||||
///@name Message Transmission
|
||||
///@{
|
||||
|
||||
/**
|
||||
* @brief Construct and emit a message of type @p T through the bus.
|
||||
*
|
||||
* Forwards the call to @ref Bus::emit(). Creates a new message of type @p T
|
||||
* using the provided arguments and posts it to the bus.
|
||||
*
|
||||
* @tparam T The message type to emit. Must be derived from @ref Message.
|
||||
* @tparam Args Constructor argument types for @p T.
|
||||
* @param args Arguments forwarded to the constructor of @p T.
|
||||
* @return @c true if at least one subscriber received the message,
|
||||
* @c false otherwise.
|
||||
*
|
||||
* @see Bus::emit()
|
||||
*/
|
||||
template<class T, class... Args>
|
||||
bool emit(Args &&...args);
|
||||
|
||||
/**
|
||||
* @brief Post an already constructed message through the bus.
|
||||
*
|
||||
* Forwards the call to @ref Bus::post().
|
||||
*
|
||||
* @param message Shared pointer to the message to post. Must not be @c nullptr.
|
||||
* @return @c true if at least one subscriber received the message,
|
||||
* @c false otherwise.
|
||||
*
|
||||
* @see Bus::post()
|
||||
*/
|
||||
bool post(const std::shared_ptr<Message> &message) const;
|
||||
|
||||
/**
|
||||
* @brief Notify the node that a message has been received.
|
||||
*
|
||||
* Sets the atomic waiting flag to @c true and triggers a wake-up for any
|
||||
* thread blocked in @ref syncWaitForMessage(). Has no effect if the flag
|
||||
* is already set.
|
||||
*/
|
||||
void messageNotify();
|
||||
|
||||
///@}
|
||||
///@name Message queue management
|
||||
///@{
|
||||
|
||||
/**
|
||||
* @brief Get the number of messages currently in the node's queue.
|
||||
*
|
||||
* @return The number of pending messages waiting to be consumed.
|
||||
* @note This operation is thread-safe.
|
||||
*/
|
||||
size_t getMessageCount() const;
|
||||
|
||||
/**
|
||||
* @brief Remove and return the front message from the node's queue (FIFO).
|
||||
*
|
||||
* Retrieves the oldest message in the queue and removes it. If the queue
|
||||
* is empty, returns @c nullptr.
|
||||
*
|
||||
* @return A shared pointer to the front @ref Message, or @c nullptr if the
|
||||
* queue is empty.
|
||||
* @note This operation is thread-safe.
|
||||
*/
|
||||
std::shared_ptr<Message> popMessage(); // Pop a message from the node's message queue (remove and return the front message)
|
||||
|
||||
///@}
|
||||
|
||||
private:
|
||||
/**
|
||||
* @brief Append a message to the node's internal queue.
|
||||
*
|
||||
* Called exclusively by @ref Bus when dispatching a message to this node.
|
||||
* Pushes the message onto the queue and notifies any thread waiting in
|
||||
* @ref syncWaitForMessage().
|
||||
*
|
||||
* @param message Shared pointer to the message being delivered.
|
||||
*/
|
||||
void append(const std::shared_ptr<Message> &message) override;
|
||||
|
||||
Bus &m_bus; ///< Reference to the event bus
|
||||
std::atomic_bool m_nodeWaitingFlag{ false }; ///< Flag to indicate if the node is waiting for a message (used for synchronous waiting)
|
||||
|
||||
/// @brief Internal message queue, protected by a mutex for thread-safe access.
|
||||
struct
|
||||
{
|
||||
mutable std::mutex mtx; ///< Mutex for thread-safe access to the message queue
|
||||
std::queue<std::shared_ptr<Message>> messageQueue; ///< Queue of messages received by the node
|
||||
} m_busMessages;
|
||||
};
|
||||
|
||||
//--------------------------------------------------------------
|
||||
/* Constructor */
|
||||
inline Node::Node(Bus &bus)
|
||||
: m_bus(bus)
|
||||
{
|
||||
// Nothing to do here
|
||||
}
|
||||
//--------------------------------------------------------------
|
||||
/* Default destructor */
|
||||
inline Node::~Node()
|
||||
{
|
||||
// Unsubscribe from all event types when the node is destroyed
|
||||
unsubscribeFromAll();
|
||||
|
||||
// Notify the bus that the node is being destroyed (in case it is waiting for a message)
|
||||
messageNotify();
|
||||
}
|
||||
//--------------------------------------------------------------
|
||||
/* Synchronously wait for a message to be posted to the bus and received by the node (block the calling thread until a message is received) */
|
||||
inline void Node::syncWaitForMessage()
|
||||
{
|
||||
// Wait until at least one message is received in the node's
|
||||
// message queue
|
||||
m_nodeWaitingFlag = false;
|
||||
m_nodeWaitingFlag.wait(false);
|
||||
}
|
||||
//--------------------------------------------------------------
|
||||
/* Subscribe to receive messages of a specific event type */
|
||||
inline void Node::subscribe(const MessageTypeID eventType)
|
||||
{
|
||||
m_bus.subscribe(this, eventType);
|
||||
}
|
||||
//--------------------------------------------------------------
|
||||
/* Unsubscribe from receiving messages of a specific event type */
|
||||
inline void Node::unsubscribe(const MessageTypeID eventType)
|
||||
{
|
||||
m_bus.unsubscribe(this, eventType);
|
||||
}
|
||||
//--------------------------------------------------------------
|
||||
/* Unsubscribe from receiving messages of all event types */
|
||||
inline void Node::unsubscribeFromAll()
|
||||
{
|
||||
m_bus.unsubscribeFromAll(this);
|
||||
}
|
||||
//--------------------------------------------------------------
|
||||
/* Emit a message of type T with the given arguments (create a message and post it to the bus) */
|
||||
template<class T, class... Args>
|
||||
bool Node::emit(Args &&...args)
|
||||
{
|
||||
return m_bus.emit<T>(std::forward<Args>(args)...);
|
||||
}
|
||||
//--------------------------------------------------------------
|
||||
/* Post a message to the bus */
|
||||
inline bool Node::post(const std::shared_ptr<Message> &message) const
|
||||
{
|
||||
return m_bus.post(message);
|
||||
}
|
||||
//--------------------------------------------------------------
|
||||
/* Get the number of messages in the node's message queue */
|
||||
inline size_t Node::getMessageCount() const
|
||||
{
|
||||
std::scoped_lock lock(m_busMessages.mtx);
|
||||
|
||||
return m_busMessages.messageQueue.size();
|
||||
}
|
||||
//--------------------------------------------------------------
|
||||
/* Pop a message from the node's message queue (remove and return the front message) */
|
||||
inline std::shared_ptr<Message> Node::popMessage()
|
||||
{
|
||||
std::scoped_lock lock(m_busMessages.mtx);
|
||||
|
||||
if (m_busMessages.messageQueue.empty())
|
||||
return nullptr; // No messages in the queue
|
||||
|
||||
auto message = m_busMessages.messageQueue.front(); // Get the front message
|
||||
m_busMessages.messageQueue.pop(); // Remove the front message from the queue
|
||||
|
||||
return message; // Return the popped message
|
||||
}
|
||||
//--------------------------------------------------------------
|
||||
/* Insert a message into the node's message queue (called by the bus when a message is posted to the bus) */
|
||||
inline void Node::append(const std::shared_ptr<Message> &message)
|
||||
{
|
||||
std::scoped_lock lock(m_busMessages.mtx);
|
||||
|
||||
m_busMessages.messageQueue.push(message);
|
||||
|
||||
// If the node is waiting for a message, notify it that a
|
||||
// message has been received
|
||||
messageNotify();
|
||||
}
|
||||
//--------------------------------------------------------------
|
||||
/* Notify the node that a message has been received (used for synchronous waiting) */
|
||||
inline void Node::messageNotify()
|
||||
{
|
||||
if (!m_nodeWaitingFlag.load())
|
||||
{
|
||||
m_nodeWaitingFlag = true;
|
||||
m_nodeWaitingFlag.notify_one();
|
||||
}
|
||||
}
|
||||
//--------------------------------------------------------------
|
||||
} // namespace sdi_toolBox::desktop::eventBus
|
||||
121
sdi_toolBox_2.x.x/toolBox/sdi_toolBox/desktop/msw/win32Console.h
Normal file
121
sdi_toolBox_2.x.x/toolBox/sdi_toolBox/desktop/msw/win32Console.h
Normal file
@@ -0,0 +1,121 @@
|
||||
/*
|
||||
{{copyright}}
|
||||
*/
|
||||
|
||||
/*
|
||||
{{version}}
|
||||
*/
|
||||
|
||||
/*
|
||||
{{license}}
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#ifdef _WIN32
|
||||
|
||||
# ifndef WIN32_LEAN_AND_MEAN
|
||||
# define WIN32_LEAN_AND_MEAN // Disable messy Windows headers, which can cause conflicts with other libraries and increase compilation time
|
||||
# endif
|
||||
|
||||
# include <Windows.h>
|
||||
# include <iostream>
|
||||
# include <memory>
|
||||
|
||||
namespace sdi_toolBox::desktop::msw
|
||||
{
|
||||
//--------------------------------------------------------------
|
||||
class Win32Console
|
||||
{
|
||||
public:
|
||||
static void initConsole(); // Init application console and attach debug console under MSW
|
||||
static void releaseConsole(); // Release application console
|
||||
|
||||
public:
|
||||
virtual ~Win32Console(); // Default destructor
|
||||
Win32Console(const Win32Console &obj) = delete; // Copy constructor
|
||||
Win32Console(Win32Console &&obj) noexcept = delete; // Move constructor
|
||||
Win32Console &operator=(const Win32Console &obj) = delete; // Copy assignment operator
|
||||
Win32Console &operator=(Win32Console &&obj) noexcept = delete; // Move assignment operator
|
||||
|
||||
static bool hasAttachedConsole(); // Returns true if console is attached, false otherwise
|
||||
|
||||
protected:
|
||||
static inline std::unique_ptr<Win32Console> m_singleton;
|
||||
Win32Console(); // Default constructor
|
||||
|
||||
FILE *m_stdoutFile; // Reopened stdout file pointer
|
||||
FILE *m_stderrFile; // Reopened stderr file pointer
|
||||
};
|
||||
//--------------------------------------------------------------
|
||||
/* Init application console and attach debug console under MSW */
|
||||
inline void Win32Console::initConsole()
|
||||
{
|
||||
if (!m_singleton)
|
||||
m_singleton = std::unique_ptr<Win32Console>(new Win32Console());
|
||||
}
|
||||
//--------------------------------------------------------------
|
||||
/* Release application console */
|
||||
inline void Win32Console::releaseConsole()
|
||||
{
|
||||
if (m_singleton)
|
||||
m_singleton.reset();
|
||||
}
|
||||
//--------------------------------------------------------------
|
||||
/* Default constructor */
|
||||
inline Win32Console::Win32Console()
|
||||
{
|
||||
bool consoleIsCreated = false;
|
||||
|
||||
// Try to attach application to the current console
|
||||
AttachConsole(ATTACH_PARENT_PROCESS);
|
||||
|
||||
if (!GetConsoleWindow()) // No console was available
|
||||
{
|
||||
// Create console and attach application to it
|
||||
if (!AllocConsole())
|
||||
throw std::logic_error("Unable to attach application to debug console"); // Error during creating console
|
||||
consoleIsCreated = true;
|
||||
}
|
||||
|
||||
// Reopen stdout and stderr streams to console window
|
||||
if (freopen_s(&m_stdoutFile, "CONOUT$", "w", stdout) != 0)
|
||||
throw std::logic_error("Unable to reopen stdout"); // Error during reopen on stdout
|
||||
|
||||
if (freopen_s(&m_stderrFile, "CONOUT$", "w", stderr) != 0)
|
||||
throw std::logic_error("Unable to reopen stderr"); // Error during reopen on stderr
|
||||
|
||||
std::cout.clear();
|
||||
std::cerr.clear();
|
||||
|
||||
if (!consoleIsCreated)
|
||||
std::cout << std::endl; // Add a new line if console was already existing
|
||||
}
|
||||
//--------------------------------------------------------------
|
||||
/* Default destructor */
|
||||
inline Win32Console::~Win32Console()
|
||||
{
|
||||
std::cout.clear();
|
||||
std::cerr.clear();
|
||||
|
||||
// Free console
|
||||
FreeConsole();
|
||||
|
||||
// Close reopened stdout and stderr streams
|
||||
(void)fclose(m_stdoutFile);
|
||||
(void)fclose(m_stderrFile);
|
||||
}
|
||||
//--------------------------------------------------------------
|
||||
/* Returns true if console is attached, false otherwise */
|
||||
inline bool Win32Console::hasAttachedConsole()
|
||||
{
|
||||
if (GetConsoleWindow())
|
||||
return true;
|
||||
return false;
|
||||
}
|
||||
//--------------------------------------------------------------
|
||||
} // namespace sdi_toolBox::desktop::msw
|
||||
|
||||
#else
|
||||
# error This calss is only available under Windows systems
|
||||
#endif // defined WIN32
|
||||
261
sdi_toolBox_2.x.x/toolBox/sdi_toolBox/desktop/utils/base64.h
Normal file
261
sdi_toolBox_2.x.x/toolBox/sdi_toolBox/desktop/utils/base64.h
Normal file
@@ -0,0 +1,261 @@
|
||||
/*
|
||||
Copyright (c) 2026 - SD-Innovation S.A.S. - FRANCE
|
||||
*/
|
||||
|
||||
/*
|
||||
ver: 2.x.x - build: 2026-04-28
|
||||
*/
|
||||
|
||||
/*
|
||||
The zlib License
|
||||
|
||||
Copyright (c) 2026 SD-Innovation S.A.S.
|
||||
|
||||
This software is provided ‘as-is’, without any express or implied
|
||||
warranty. In no event will the authors be held liable for any damages
|
||||
arising from the use of this software.
|
||||
|
||||
Permission is granted to anyone to use this software for any purpose,
|
||||
including commercial applications, and to alter it and redistribute it
|
||||
freely, subject to the following restrictions:
|
||||
|
||||
1. The origin of this software must not be misrepresented; you must not
|
||||
claim that you wrote the original software. If you use this software
|
||||
in a product, an acknowledgment in the product documentation would be
|
||||
appreciated but is not required.
|
||||
|
||||
2. Altered source versions must be plainly marked as such, and must not be
|
||||
misrepresented as being the original software.
|
||||
|
||||
3. This notice may not be removed or altered from any source
|
||||
distribution.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <optional>
|
||||
#include <span>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
namespace sdi_toolBox::desktop::utils
|
||||
{
|
||||
//--------------------------------------------------------------
|
||||
/**
|
||||
* @brief Utility class providing Base64 encoding and decoding functionality.
|
||||
*
|
||||
* @details This class implements the Base64 encoding scheme as defined in RFC 4648.
|
||||
* All methods are static and the class is not meant to be instantiated.
|
||||
* Padding is handled via the '=' character.
|
||||
*
|
||||
* @note The standard Base64 alphabet is used ('A-Z', 'a-z', '0-9', '+', '/').
|
||||
*
|
||||
* @par Example - Encoding binary data:
|
||||
* @code{.cpp}
|
||||
* std::vector<std::uint8_t> data = { 0x48, 0x65, 0x6C, 0x6C, 0x6F };
|
||||
* std::string encoded = Base64::encode(data);
|
||||
* // encoded == "SGVsbG8="
|
||||
* @endcode
|
||||
*
|
||||
* @par Example - Encoding a string:
|
||||
* @code{.cpp}
|
||||
* std::string encoded = Base64::encode("Hello, World!");
|
||||
* // encoded == "SGVsbG8sIFdvcmxkIQ=="
|
||||
* @endcode
|
||||
*
|
||||
* @par Example - Decoding:
|
||||
* @code{.cpp}
|
||||
* auto result = Base64::decode_to_string("SGVsbG8sIFdvcmxkIQ==");
|
||||
* if (result)
|
||||
* std::cout << *result; // prints: Hello, World!
|
||||
* @endcode
|
||||
*/
|
||||
class Base64
|
||||
{
|
||||
public:
|
||||
/**
|
||||
* @brief Deleted default constructor - this class is not meant to be instantiated.
|
||||
*/
|
||||
Base64() = delete;
|
||||
|
||||
///@name Encoding
|
||||
///@{
|
||||
|
||||
/**
|
||||
* @brief Encodes binary data into a Base64 string.
|
||||
*
|
||||
* @param data A span of bytes to encode.
|
||||
* @return A Base64-encoded string, padded with `'='` characters if necessary.
|
||||
*/
|
||||
[[nodiscard]] static std::string encode(std::span<const std::uint8_t> data);
|
||||
|
||||
/**
|
||||
* @brief Encodes a text string into a Base64 string.
|
||||
*
|
||||
* @param text The input string to encode.
|
||||
* @return A Base64-encoded string, padded with `'='` characters if necessary.
|
||||
*/
|
||||
[[nodiscard]] static std::string encode(std::string_view text);
|
||||
|
||||
///@}
|
||||
///@name Decoding
|
||||
///@{
|
||||
|
||||
/**
|
||||
* @brief Decodes a Base64 string into raw binary data.
|
||||
*
|
||||
* @param input The Base64-encoded string to decode. Must have a length
|
||||
* that is a multiple of 4.
|
||||
* @return A vector of decoded bytes, or `std::nullopt` if the input is
|
||||
* not a valid Base64 string.
|
||||
*/
|
||||
[[nodiscard]] static std::optional<std::vector<std::uint8_t>> decode(std::string_view input);
|
||||
|
||||
/**
|
||||
* @brief Decodes a Base64 string into a text string.
|
||||
*
|
||||
* @param input The Base64-encoded string to decode. Must have a length
|
||||
* that is a multiple of 4.
|
||||
* @return The decoded string, or `std::nullopt` if the input is not a
|
||||
* valid Base64 string.
|
||||
*/
|
||||
[[nodiscard]] static std::optional<std::string> decode_to_string(std::string_view input);
|
||||
|
||||
///@}
|
||||
|
||||
private:
|
||||
///@name Internal helpers
|
||||
///@{
|
||||
|
||||
/**
|
||||
* @brief Decodes a single Base64 character into its 6-bit value.
|
||||
*
|
||||
* @param c The Base64 character to decode.
|
||||
* @return The 6-bit value corresponding to @p c, or `std::nullopt` if
|
||||
* @p c is not a valid Base64 character.
|
||||
*/
|
||||
[[nodiscard]] static std::optional<std::uint8_t> decode_char(char c) noexcept;
|
||||
|
||||
///@}
|
||||
///@name Constants
|
||||
///@{
|
||||
|
||||
/** @brief The Base64 encoding lookup table (RFC 4648 standard alphabet). */
|
||||
static constexpr std::string_view ENCODE_LOOKUP = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
|
||||
|
||||
/** @brief The padding character used in Base64 encoding. */
|
||||
static constexpr char PADDING_CAR = '=';
|
||||
|
||||
///@}
|
||||
};
|
||||
//--------------------------------------------------------------
|
||||
|
||||
//--------------------------------------------------------------
|
||||
/* Encode binary data to Base64 string */
|
||||
inline std::string Base64::encode(const std::span<const std::uint8_t> data)
|
||||
{
|
||||
std::string result;
|
||||
result.reserve(((data.size() + 2) / 3) * 4);
|
||||
|
||||
for (std::size_t i = 0; i < data.size(); i += 3)
|
||||
{
|
||||
const std::uint32_t b0 = data[i];
|
||||
const std::uint32_t b1 = (i + 1 < data.size()) ? data[i + 1] : 0u;
|
||||
const std::uint32_t b2 = (i + 2 < data.size()) ? data[i + 2] : 0u;
|
||||
|
||||
const std::uint32_t triple = (b0 << 16) | (b1 << 8) | b2;
|
||||
|
||||
result += ENCODE_LOOKUP[(triple >> 18) & 0x3F];
|
||||
result += ENCODE_LOOKUP[(triple >> 12) & 0x3F];
|
||||
result += (i + 1 < data.size()) ? ENCODE_LOOKUP[(triple >> 6) & 0x3F] : PADDING_CAR;
|
||||
result += (i + 2 < data.size()) ? ENCODE_LOOKUP[(triple >> 0) & 0x3F] : PADDING_CAR;
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
//--------------------------------------------------------------
|
||||
/* Encode a text string to Base64 string */
|
||||
inline std::string Base64::encode(const std::string_view text)
|
||||
{
|
||||
return encode(std::span(reinterpret_cast<const std::uint8_t *>(text.data()), text.size()));
|
||||
}
|
||||
//--------------------------------------------------------------
|
||||
/* Decode a Base64 string to binary data (returns std::nullopt if invalid) */
|
||||
inline std::optional<std::vector<std::uint8_t>> Base64::decode(const std::string_view input)
|
||||
{
|
||||
if (input.size() % 4 != 0)
|
||||
return std::nullopt;
|
||||
|
||||
if (input.empty())
|
||||
return std::vector<std::uint8_t>{};
|
||||
|
||||
// Validate padding: '=' is only allowed in the last group, in position 2 or 3
|
||||
// Valid forms: "xxx=" or "xx=="
|
||||
for (std::size_t i = 0; i < input.size() - 4; ++i)
|
||||
{
|
||||
if (input[i] == PADDING_CAR)
|
||||
return std::nullopt; // '=' found outside the last group
|
||||
}
|
||||
|
||||
const std::string_view last = input.substr(input.size() - 4);
|
||||
// "xx==" : positions 0,1 must be valid chars, positions 2,3 must be '='
|
||||
// "xxx=" : positions 0,1,2 must be valid chars, position 3 must be '='
|
||||
// "xxxx" : all positions must be valid chars
|
||||
if (last[2] == PADDING_CAR && last[3] != PADDING_CAR)
|
||||
return std::nullopt; // "xx=x" is invalid
|
||||
|
||||
std::vector<std::uint8_t> result;
|
||||
result.reserve((input.size() / 4) * 3);
|
||||
|
||||
for (std::size_t i = 0; i < input.size(); i += 4)
|
||||
{
|
||||
const auto v0 = decode_char(input[i]);
|
||||
const auto v1 = decode_char(input[i + 1]);
|
||||
const auto v2 = input[i + 2] == PADDING_CAR ? std::optional<std::uint8_t>{ 0 } : decode_char(input[i + 2]);
|
||||
const auto v3 = input[i + 3] == PADDING_CAR ? std::optional<std::uint8_t>{ 0 } : decode_char(input[i + 3]);
|
||||
|
||||
if (!v0 || !v1 || !v2 || !v3)
|
||||
return std::nullopt;
|
||||
|
||||
const std::uint32_t triple =
|
||||
(static_cast<std::uint32_t>(*v0) << 18) |
|
||||
(static_cast<std::uint32_t>(*v1) << 12) |
|
||||
(static_cast<std::uint32_t>(*v2) << 6) |
|
||||
(static_cast<std::uint32_t>(*v3));
|
||||
|
||||
result.push_back(static_cast<std::uint8_t>((triple >> 16) & 0xFF));
|
||||
if (input[i + 2] != PADDING_CAR)
|
||||
result.push_back(static_cast<std::uint8_t>((triple >> 8) & 0xFF));
|
||||
if (input[i + 3] != PADDING_CAR)
|
||||
result.push_back(static_cast<std::uint8_t>((triple >> 0) & 0xFF));
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
//--------------------------------------------------------------
|
||||
/* Decode a Base64 string to a text string (returns std::nullopt if invalid) */
|
||||
inline std::optional<std::string> Base64::decode_to_string(const std::string_view input)
|
||||
{
|
||||
const auto bytes = decode(input);
|
||||
if (!bytes)
|
||||
return std::nullopt;
|
||||
return std::string(reinterpret_cast<const char *>(bytes->data()), bytes->size());
|
||||
}
|
||||
//--------------------------------------------------------------
|
||||
/* Helper to decode a single Base64 character to its 6-bit value (returns std::nullopt if invalid) */
|
||||
inline std::optional<std::uint8_t> Base64::decode_char(const char c) noexcept
|
||||
{
|
||||
if (c >= 'A' && c <= 'Z')
|
||||
return static_cast<std::uint8_t>(c - 'A');
|
||||
if (c >= 'a' && c <= 'z')
|
||||
return static_cast<std::uint8_t>(c - 'a' + 26);
|
||||
if (c >= '0' && c <= '9')
|
||||
return static_cast<std::uint8_t>(c - '0' + 52);
|
||||
if (c == '+')
|
||||
return 62;
|
||||
if (c == '/')
|
||||
return 63;
|
||||
return std::nullopt;
|
||||
}
|
||||
//--------------------------------------------------------------
|
||||
} // namespace sdi_toolBox::desktop::utils
|
||||
219
sdi_toolBox_2.x.x/toolBox/sdi_toolBox/desktop/utils/uuid.h
Normal file
219
sdi_toolBox_2.x.x/toolBox/sdi_toolBox/desktop/utils/uuid.h
Normal file
@@ -0,0 +1,219 @@
|
||||
/*
|
||||
Copyright (c) 2026 - SD-Innovation S.A.S. - FRANCE
|
||||
*/
|
||||
|
||||
/*
|
||||
ver: 2.x.x - build: 2026-04-28
|
||||
*/
|
||||
|
||||
/*
|
||||
The zlib License
|
||||
|
||||
Copyright (c) 2026 SD-Innovation S.A.S.
|
||||
|
||||
This software is provided ‘as-is’, without any express or implied
|
||||
warranty. In no event will the authors be held liable for any damages
|
||||
arising from the use of this software.
|
||||
|
||||
Permission is granted to anyone to use this software for any purpose,
|
||||
including commercial applications, and to alter it and redistribute it
|
||||
freely, subject to the following restrictions:
|
||||
|
||||
1. The origin of this software must not be misrepresented; you must not
|
||||
claim that you wrote the original software. If you use this software
|
||||
in a product, an acknowledgment in the product documentation would be
|
||||
appreciated but is not required.
|
||||
|
||||
2. Altered source versions must be plainly marked as such, and must not be
|
||||
misrepresented as being the original software.
|
||||
|
||||
3. This notice may not be removed or altered from any source
|
||||
distribution.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <array>
|
||||
#include <chrono>
|
||||
#include <format>
|
||||
#include <iomanip>
|
||||
#include <random>
|
||||
#include <sstream>
|
||||
#include <string>
|
||||
|
||||
namespace sdi_toolBox::desktop::utils::uuid
|
||||
{
|
||||
//--------------------------------------------------------------
|
||||
/**
|
||||
* @brief Generate a unique identifier based on the current time (microseconds).
|
||||
*
|
||||
* Produces a string identifier derived from the current timestamp in microseconds.
|
||||
* If @p moreEntropy is true, additional random bytes are appended to increase
|
||||
* uniqueness (useful when multiple identifiers are generated in quick succession).
|
||||
*
|
||||
* @param moreEntropy If true, append extra random entropy to the identifier.
|
||||
* @return std::string Generated identifier (ASCII string, format not strictly defined).
|
||||
*/
|
||||
std::string uniqid(bool moreEntropy = false);
|
||||
|
||||
/**
|
||||
* @brief Generate a random UUID (version 4).
|
||||
*
|
||||
* Produces a RFC 4122 compliant UUID version 4 using random data.
|
||||
* The returned string uses the canonical textual representation:
|
||||
* "8-4-4-4-12" hexadecimal digits.
|
||||
*
|
||||
* @return std::string UUID v4 as a text string.
|
||||
*/
|
||||
std::string v4();
|
||||
|
||||
/**
|
||||
* @brief Generate a time-ordered UUID (version 7).
|
||||
*
|
||||
* Produces a UUID v7 combining a timestamp and random bits to allow
|
||||
* roughly time-ordered identifiers while keeping sufficient entropy for uniqueness.
|
||||
* The returned string uses the canonical textual representation:
|
||||
* "8-4-4-4-12" hexadecimal digits.
|
||||
*
|
||||
* @note UUID v7 is a newer specification intended for sortable UUIDs.
|
||||
*
|
||||
* @return std::string UUID v7 as a text string.
|
||||
*/
|
||||
std::string v7();
|
||||
//--------------------------------------------------------------
|
||||
|
||||
//--------------------------------------------------------------
|
||||
/* Generates a unique identifier based on the current time in
|
||||
* microseconds. If the `moreEntropy` parameter is set to
|
||||
* `true`, additional random data is appended to the identifier
|
||||
* to increase its uniqueness. The generated identifier is
|
||||
* returned as a string. */
|
||||
inline std::string uniqid(const bool moreEntropy)
|
||||
{
|
||||
const auto now = std::chrono::system_clock::now(); // Get current time point
|
||||
const auto epoch = now.time_since_epoch(); // Get duration since epoch
|
||||
const auto us_since_epoch = std::chrono::duration_cast<std::chrono::microseconds>(epoch).count();
|
||||
|
||||
// PHP format: "%08x%05x" -> 8 hex chars (sec) + 5 hex chars (usec fraction)
|
||||
// sec = full seconds since epoch
|
||||
// usec = microseconds fraction within the current second (0..999999)
|
||||
const auto sec = static_cast<uint32_t>(us_since_epoch / 1'000'000);
|
||||
const auto usec = static_cast<uint32_t>(us_since_epoch % 1'000'000);
|
||||
std::string result = std::format("{:08x}{:05x}", sec, usec);
|
||||
|
||||
// PHP moreEntropy: appends a random float in [0, 10) with 8 decimal places
|
||||
// Example output: "5f68b2056fbac7.12345678"
|
||||
if (moreEntropy)
|
||||
{
|
||||
thread_local auto rng = std::mt19937{ std::random_device{}() };
|
||||
auto f_dist = std::uniform_real_distribution<double>(0.0, 10.0);
|
||||
|
||||
// Append the random data in hexadecimal format
|
||||
result = result + std::format("{:.8f}", f_dist(rng));
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
//--------------------------------------------------------------
|
||||
/* Generates a random UUID (version 4) and returns it as a
|
||||
* string. The UUID is generated using random numbers, and it
|
||||
* follows the standard format of 8-4-4-4-12 hexadecimal
|
||||
* characters. */
|
||||
inline std::string v4()
|
||||
{
|
||||
/*
|
||||
* UUID version 4 is a randomly generated UUID.
|
||||
* It's structured as follows:
|
||||
* - 122 bits are random
|
||||
* - 6 bits are fixed to indicate the version and variant
|
||||
* The total is 128 bits, formatted as 8-4-4-4-12 hexadecimal characters.
|
||||
*
|
||||
* The version (4) is set in the 7th byte (index 6) and the variant
|
||||
* is set in the 9th byte (index 8).
|
||||
*/
|
||||
std::array<uint8_t, 16> bytes;
|
||||
|
||||
// Fast thread-local RNG seeded from random_device once per thread
|
||||
thread_local auto rng = std::mt19937{ std::random_device{}() };
|
||||
auto dist = std::uniform_int_distribution<uint32_t>(0, 0xFF);
|
||||
|
||||
// Fill the random bytes
|
||||
for (auto &byte : bytes)
|
||||
byte = static_cast<uint8_t>(dist(rng)); // Generate random bytes (8 bits each)
|
||||
|
||||
// Set version to 4 -> xxxx0100 in the 7th byte (index 6)
|
||||
bytes[6] = static_cast<uint8_t>((bytes[6] & 0x0F) | 0x40);
|
||||
|
||||
// Set variant to 10xxxxxx in the 9th byte (index 8)
|
||||
bytes[8] = static_cast<uint8_t>((bytes[8] & 0x3F) | 0x80);
|
||||
|
||||
// Format as 8-4-4-4-12 hex
|
||||
std::ostringstream oss;
|
||||
oss << std::hex << std::nouppercase << std::setfill('0');
|
||||
for (int i = 0; i < 16; ++i)
|
||||
{
|
||||
oss << std::setw(2) << static_cast<int>(bytes[i]);
|
||||
if (i == 3 || i == 5 || i == 7 || i == 9)
|
||||
oss << '-';
|
||||
}
|
||||
return oss.str();
|
||||
}
|
||||
//--------------------------------------------------------------
|
||||
/* Generates a UUID (version 7) based on the current timestamp
|
||||
* and random data. The UUID is generated using a combination of
|
||||
* the current time and random numbers, and it follows the
|
||||
* standard format of 8-4-4-4-12 hexadecimal characters. */
|
||||
inline std::string v7()
|
||||
{
|
||||
/*
|
||||
* UUID version 7 is a time-ordered UUID that combines a timestamp with random bits.
|
||||
* It's structured as follows:
|
||||
* - 48 bits for the timestamp (milliseconds since Unix epoch)
|
||||
* - 12 bits for the version (7)
|
||||
* - 62 bits for random data (to ensure uniqueness)
|
||||
* The total is 128 bits, formatted as 8-4-4-4-12 hexadecimal characters.
|
||||
*
|
||||
* The version (7) is set in the 7th byte (index 6) and the variant
|
||||
* is set in the 9th byte (index 8).
|
||||
*/
|
||||
std::array<uint8_t, 16> bytes;
|
||||
|
||||
// Fast thread-local RNG seeded from random_device once per thread
|
||||
thread_local auto rng = std::mt19937{ std::random_device{}() };
|
||||
auto dist = std::uniform_int_distribution<uint32_t>(0, 0xFF);
|
||||
|
||||
const auto now = std::chrono::system_clock::now(); // Get current time point
|
||||
const auto epoch = now.time_since_epoch(); // Get duration since epoch
|
||||
const uint64_t ms_since_epoch = std::chrono::duration_cast<std::chrono::milliseconds>(epoch).count();
|
||||
|
||||
// Fill timestamp (Big-endian)
|
||||
bytes[0] = static_cast<uint8_t>((ms_since_epoch >> 40) & 0xFF);
|
||||
bytes[1] = static_cast<uint8_t>((ms_since_epoch >> 32) & 0xFF);
|
||||
bytes[2] = static_cast<uint8_t>((ms_since_epoch >> 24) & 0xFF);
|
||||
bytes[3] = static_cast<uint8_t>((ms_since_epoch >> 16) & 0xFF);
|
||||
bytes[4] = static_cast<uint8_t>((ms_since_epoch >> 8) & 0xFF);
|
||||
bytes[5] = static_cast<uint8_t>(ms_since_epoch & 0xFF);
|
||||
|
||||
// Generate randomness for the rest
|
||||
for (size_t i = 6; i < 16; ++i)
|
||||
bytes[i] = static_cast<uint8_t>(dist(rng)); // Generate random bytes (8 bits each)
|
||||
|
||||
// Set Version 7 (bits 48-51 -> 0111)
|
||||
bytes[6] = static_cast<uint8_t>((bytes[6] & 0x0F) | 0x70);
|
||||
|
||||
// Set Variant (bits 64-65 -> 10)
|
||||
bytes[8] = static_cast<uint8_t>((bytes[8] & 0x3F) | 0x80);
|
||||
|
||||
// Format as 8-4-4-4-12 hex
|
||||
std::ostringstream oss;
|
||||
oss << std::hex << std::nouppercase << std::setfill('0');
|
||||
for (int i = 0; i < 16; ++i)
|
||||
{
|
||||
oss << std::setw(2) << static_cast<int>(bytes[i]);
|
||||
if (i == 3 || i == 5 || i == 7 || i == 9)
|
||||
oss << '-';
|
||||
}
|
||||
return oss.str();
|
||||
}
|
||||
//--------------------------------------------------------------
|
||||
} // namespace sdi_toolBox::desktop::utils::uuid
|
||||
@@ -0,0 +1,174 @@
|
||||
/*
|
||||
Copyright (c) 2026 - SD-Innovation S.A.S. - FRANCE
|
||||
*/
|
||||
|
||||
/*
|
||||
ver: 2.x.x - build: 2026-04-28
|
||||
*/
|
||||
|
||||
/*
|
||||
The zlib License
|
||||
|
||||
Copyright (c) 2026 SD-Innovation S.A.S.
|
||||
|
||||
This software is provided ‘as-is’, without any express or implied
|
||||
warranty. In no event will the authors be held liable for any damages
|
||||
arising from the use of this software.
|
||||
|
||||
Permission is granted to anyone to use this software for any purpose,
|
||||
including commercial applications, and to alter it and redistribute it
|
||||
freely, subject to the following restrictions:
|
||||
|
||||
1. The origin of this software must not be misrepresented; you must not
|
||||
claim that you wrote the original software. If you use this software
|
||||
in a product, an acknowledgment in the product documentation would be
|
||||
appreciated but is not required.
|
||||
|
||||
2. Altered source versions must be plainly marked as such, and must not be
|
||||
misrepresented as being the original software.
|
||||
|
||||
3. This notice may not be removed or altered from any source
|
||||
distribution.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <vector>
|
||||
#include <wx/wx.h>
|
||||
|
||||
namespace sdi_toolBox::desktop::wxWidgets
|
||||
{
|
||||
//--------------------------------------------------------------
|
||||
/**
|
||||
* @brief Manages a list of file wildcard entries for use in wxWidgets file dialogs.
|
||||
*
|
||||
* The Wildcard class provides a convenient way to build and format wildcard filter strings
|
||||
* compatible with wxWidgets file dialogs (e.g., `wxFileDialog`).
|
||||
*
|
||||
* Each entry consists of a human-readable description and a file pattern (e.g., `*.txt`).
|
||||
* The formatted string follows the wxWidgets wildcard format:
|
||||
* @code{.cpp}
|
||||
* "Description (*.ext)|*.ext|Other (*.other)|*.other"
|
||||
* @endcode
|
||||
*
|
||||
* @par Example
|
||||
* @code{.cpp}
|
||||
* sdi_toolBox::desktop::wxWidgets::Wildcard wc;
|
||||
* wc.addEntry("Text files", "*.txt");
|
||||
* wc.addEntry("CSV files", "*.csv");
|
||||
* wxString filter = wc.getWildcards(true); // includes "All files (*.*)|*.*"
|
||||
* wxFileDialog dlg(this, "Open file", "", "", filter, wxFD_OPEN);
|
||||
* @endcode
|
||||
*/
|
||||
class Wildcard
|
||||
{
|
||||
public:
|
||||
/**
|
||||
* @struct WildcardEntry
|
||||
* @brief Represents a single wildcard filter entry.
|
||||
*/
|
||||
struct WildcardEntry
|
||||
{
|
||||
wxString description; ///< Human-readable description of the file type (e.g., "Text files").
|
||||
wxString pattern; ///< File pattern used for filtering (e.g., "*.txt").
|
||||
};
|
||||
|
||||
public:
|
||||
/**
|
||||
* @brief Default constructor.
|
||||
*/
|
||||
Wildcard() = default;
|
||||
|
||||
/**
|
||||
* @brief Default destructor.
|
||||
*/
|
||||
virtual ~Wildcard() = default;
|
||||
|
||||
/**
|
||||
* @brief Copy constructor.
|
||||
* @param other The Wildcard instance to copy from.
|
||||
*/
|
||||
Wildcard(const Wildcard &other) = default;
|
||||
|
||||
/**
|
||||
* @brief Move constructor.
|
||||
* @param other The Wildcard instance to move from.
|
||||
*/
|
||||
Wildcard(Wildcard &&other) noexcept = default;
|
||||
|
||||
/**
|
||||
* @brief Copy assignment operator.
|
||||
* @param other The Wildcard instance to copy from.
|
||||
* @return Reference to this instance.
|
||||
*/
|
||||
Wildcard &operator=(const Wildcard &other) = default;
|
||||
|
||||
/**
|
||||
* @brief Move assignment operator.
|
||||
* @param other The Wildcard instance to move from.
|
||||
* @return Reference to this instance.
|
||||
*/
|
||||
Wildcard &operator=(Wildcard &&other) noexcept = default;
|
||||
|
||||
/**
|
||||
* @brief Removes all wildcard entries.
|
||||
*/
|
||||
void clear();
|
||||
|
||||
/**
|
||||
* @brief Adds a new wildcard filter entry.
|
||||
* @param description Human-readable description of the file type (e.g., "Text files").
|
||||
* @param pattern File pattern used for filtering (e.g., "*.txt").
|
||||
*/
|
||||
void addEntry(const wxString &description, const wxString &pattern);
|
||||
|
||||
/**
|
||||
* @brief Builds and returns the formatted wildcard string for use in wxWidgets file dialogs.
|
||||
* @param addAllFiles If @c true, appends an "All files (*.*)|*.*" entry at the end.
|
||||
* @return A formatted wildcard string compatible with wxWidgets file dialog filters.
|
||||
*/
|
||||
[[nodiscard]] wxString getWildcards(bool addAllFiles = false) const;
|
||||
|
||||
protected:
|
||||
std::vector<WildcardEntry> m_wildcards; // List of wildcard strings
|
||||
};
|
||||
//--------------------------------------------------------------
|
||||
/* Clear all wildcard entries */
|
||||
inline void Wildcard::clear()
|
||||
{
|
||||
m_wildcards.clear();
|
||||
}
|
||||
//--------------------------------------------------------------
|
||||
/* Add a wildcard entry */
|
||||
inline void Wildcard::addEntry(const wxString &description, const wxString &pattern)
|
||||
{
|
||||
m_wildcards.push_back({ .description = description, .pattern = pattern });
|
||||
}
|
||||
//--------------------------------------------------------------
|
||||
/* Get wildcard list */
|
||||
inline wxString Wildcard::getWildcards(const bool addAllFiles) const
|
||||
{
|
||||
wxString wildcardList;
|
||||
|
||||
// Add wildcard entries to the list
|
||||
for (const auto &entry : m_wildcards)
|
||||
{
|
||||
if (!wildcardList.empty())
|
||||
wildcardList += _T("|");
|
||||
|
||||
wildcardList += entry.description + _T(" (") + entry.pattern + _T(")|") + entry.pattern;
|
||||
}
|
||||
|
||||
// Optionally add "All files" entry
|
||||
if (addAllFiles)
|
||||
{
|
||||
if (!wildcardList.empty())
|
||||
wildcardList += _T("|");
|
||||
|
||||
wildcardList += _("All files") + _T(" (") + _T("*.*") + _T(")|") + _T("*.*");
|
||||
}
|
||||
|
||||
return wildcardList;
|
||||
}
|
||||
//--------------------------------------------------------------
|
||||
} // namespace sdi_toolBox::desktop::wxWidgets
|
||||
@@ -0,0 +1,32 @@
|
||||
#include "wxBusEvent.h"
|
||||
|
||||
wxDEFINE_EVENT(wx_BUSEVENT_MESSAGE, wxBusEvent);
|
||||
//--------------------------------------------------------------
|
||||
/* Constructor */
|
||||
wxBusEvent::wxBusEvent(message_t message, const int id)
|
||||
: wxCommandEvent(wx_BUSEVENT_MESSAGE, id)
|
||||
, m_message(std::move(message))
|
||||
{
|
||||
// Nothing to do here
|
||||
}
|
||||
//--------------------------------------------------------------
|
||||
/* Clone method for wxWidgets event system */
|
||||
wxEvent *wxBusEvent::Clone() const
|
||||
{
|
||||
auto *clonedEvent = new wxBusEvent(m_message, GetId());
|
||||
clonedEvent->SetEventObject(GetEventObject()); // Copy the event object
|
||||
return clonedEvent;
|
||||
}
|
||||
//--------------------------------------------------------------
|
||||
/* Get the message type ID */
|
||||
wxBusEvent::MessageTypeID wxBusEvent::getMessageTypeID() const
|
||||
{
|
||||
return m_message ? m_message->getMessageTypeID() : 0; // Return 0 if message is null
|
||||
}
|
||||
//--------------------------------------------------------------
|
||||
/* Get the message data */
|
||||
wxBusEvent::message_t wxBusEvent::getMessage() const
|
||||
{
|
||||
return m_message;
|
||||
}
|
||||
//--------------------------------------------------------------
|
||||
@@ -0,0 +1,33 @@
|
||||
#pragma once
|
||||
|
||||
#include <sdi_toolBox/desktop/eventBus/defs.h>
|
||||
#include <sdi_toolBox/desktop/eventBus/message.h>
|
||||
#include <wx/wx.h>
|
||||
|
||||
//--------------------------------------------------------------
|
||||
class wxBusEvent : public wxCommandEvent
|
||||
{
|
||||
using MessageTypeID = sdi_toolBox::desktop::eventBus::MessageTypeID;
|
||||
using message_t = std::shared_ptr<sdi_toolBox::desktop::eventBus::Message>;
|
||||
|
||||
public:
|
||||
wxBusEvent() = delete; // Default constructor
|
||||
virtual ~wxBusEvent() = default; // Default destructor
|
||||
wxBusEvent(const wxBusEvent &other) = default; // Copy constructor
|
||||
wxBusEvent(wxBusEvent &&other) noexcept = default; // Move constructor
|
||||
wxBusEvent &operator=(const wxBusEvent &other) = delete; // Copy assignment
|
||||
wxBusEvent &operator=(wxBusEvent &&other) noexcept = delete; // Move assignment
|
||||
|
||||
explicit wxBusEvent(message_t message, int id = wxID_ANY); // Constructor
|
||||
|
||||
[[nodiscard]] wxEvent *Clone() const override; // Clone method for wxWidgets event system
|
||||
|
||||
// Data management
|
||||
[[nodiscard]] MessageTypeID getMessageTypeID() const; // Get the message type ID
|
||||
[[nodiscard]] message_t getMessage() const; // Get the message data
|
||||
|
||||
protected:
|
||||
message_t m_message;
|
||||
};
|
||||
//--------------------------------------------------------------
|
||||
wxDECLARE_EVENT(wx_BUSEVENT_MESSAGE, wxBusEvent);
|
||||
25796
sdi_toolBox_2.x.x/toolBox/sdi_toolBox/external/nlohmann/json/json.hpp
vendored
Normal file
25796
sdi_toolBox_2.x.x/toolBox/sdi_toolBox/external/nlohmann/json/json.hpp
vendored
Normal file
File diff suppressed because it is too large
Load Diff
187
sdi_toolBox_2.x.x/toolBox/sdi_toolBox/external/nlohmann/json/json_fwd.hpp
vendored
Normal file
187
sdi_toolBox_2.x.x/toolBox/sdi_toolBox/external/nlohmann/json/json_fwd.hpp
vendored
Normal file
@@ -0,0 +1,187 @@
|
||||
// __ _____ _____ _____
|
||||
// __| | __| | | | JSON for Modern C++
|
||||
// | | |__ | | | | | | version 3.12.0
|
||||
// |_____|_____|_____|_|___| https://github.com/nlohmann/json
|
||||
//
|
||||
// SPDX-FileCopyrightText: 2013-2026 Niels Lohmann <https://nlohmann.me>
|
||||
// SPDX-License-Identifier: MIT
|
||||
|
||||
#ifndef INCLUDE_NLOHMANN_JSON_FWD_HPP_
|
||||
#define INCLUDE_NLOHMANN_JSON_FWD_HPP_
|
||||
|
||||
#include <cstdint> // int64_t, uint64_t
|
||||
#include <map> // map
|
||||
#include <memory> // allocator
|
||||
#include <string> // string
|
||||
#include <vector> // vector
|
||||
|
||||
// #include <nlohmann/detail/abi_macros.hpp>
|
||||
// __ _____ _____ _____
|
||||
// __| | __| | | | JSON for Modern C++
|
||||
// | | |__ | | | | | | version 3.12.0
|
||||
// |_____|_____|_____|_|___| https://github.com/nlohmann/json
|
||||
//
|
||||
// SPDX-FileCopyrightText: 2013-2026 Niels Lohmann <https://nlohmann.me>
|
||||
// SPDX-License-Identifier: MIT
|
||||
|
||||
|
||||
|
||||
// This file contains all macro definitions affecting or depending on the ABI
|
||||
|
||||
#ifndef JSON_SKIP_LIBRARY_VERSION_CHECK
|
||||
#if defined(NLOHMANN_JSON_VERSION_MAJOR) && defined(NLOHMANN_JSON_VERSION_MINOR) && defined(NLOHMANN_JSON_VERSION_PATCH)
|
||||
#if NLOHMANN_JSON_VERSION_MAJOR != 3 || NLOHMANN_JSON_VERSION_MINOR != 12 || NLOHMANN_JSON_VERSION_PATCH != 0
|
||||
#warning "Already included a different version of the library!"
|
||||
#endif
|
||||
#endif
|
||||
#endif
|
||||
|
||||
#define NLOHMANN_JSON_VERSION_MAJOR 3 // NOLINT(modernize-macro-to-enum)
|
||||
#define NLOHMANN_JSON_VERSION_MINOR 12 // NOLINT(modernize-macro-to-enum)
|
||||
#define NLOHMANN_JSON_VERSION_PATCH 0 // NOLINT(modernize-macro-to-enum)
|
||||
|
||||
#ifndef JSON_DIAGNOSTICS
|
||||
#define JSON_DIAGNOSTICS 0
|
||||
#endif
|
||||
|
||||
#ifndef JSON_DIAGNOSTIC_POSITIONS
|
||||
#define JSON_DIAGNOSTIC_POSITIONS 0
|
||||
#endif
|
||||
|
||||
#ifndef JSON_USE_LEGACY_DISCARDED_VALUE_COMPARISON
|
||||
#define JSON_USE_LEGACY_DISCARDED_VALUE_COMPARISON 0
|
||||
#endif
|
||||
|
||||
#if JSON_DIAGNOSTICS
|
||||
#define NLOHMANN_JSON_ABI_TAG_DIAGNOSTICS _diag
|
||||
#else
|
||||
#define NLOHMANN_JSON_ABI_TAG_DIAGNOSTICS
|
||||
#endif
|
||||
|
||||
#if JSON_DIAGNOSTIC_POSITIONS
|
||||
#define NLOHMANN_JSON_ABI_TAG_DIAGNOSTIC_POSITIONS _dp
|
||||
#else
|
||||
#define NLOHMANN_JSON_ABI_TAG_DIAGNOSTIC_POSITIONS
|
||||
#endif
|
||||
|
||||
#if JSON_USE_LEGACY_DISCARDED_VALUE_COMPARISON
|
||||
#define NLOHMANN_JSON_ABI_TAG_LEGACY_DISCARDED_VALUE_COMPARISON _ldvcmp
|
||||
#else
|
||||
#define NLOHMANN_JSON_ABI_TAG_LEGACY_DISCARDED_VALUE_COMPARISON
|
||||
#endif
|
||||
|
||||
#ifndef NLOHMANN_JSON_NAMESPACE_NO_VERSION
|
||||
#define NLOHMANN_JSON_NAMESPACE_NO_VERSION 0
|
||||
#endif
|
||||
|
||||
// Construct the namespace ABI tags component
|
||||
#define NLOHMANN_JSON_ABI_TAGS_CONCAT_EX(a, b, c) json_abi ## a ## b ## c
|
||||
#define NLOHMANN_JSON_ABI_TAGS_CONCAT(a, b, c) \
|
||||
NLOHMANN_JSON_ABI_TAGS_CONCAT_EX(a, b, c)
|
||||
|
||||
#define NLOHMANN_JSON_ABI_TAGS \
|
||||
NLOHMANN_JSON_ABI_TAGS_CONCAT( \
|
||||
NLOHMANN_JSON_ABI_TAG_DIAGNOSTICS, \
|
||||
NLOHMANN_JSON_ABI_TAG_LEGACY_DISCARDED_VALUE_COMPARISON, \
|
||||
NLOHMANN_JSON_ABI_TAG_DIAGNOSTIC_POSITIONS)
|
||||
|
||||
// Construct the namespace version component
|
||||
#define NLOHMANN_JSON_NAMESPACE_VERSION_CONCAT_EX(major, minor, patch) \
|
||||
_v ## major ## _ ## minor ## _ ## patch
|
||||
#define NLOHMANN_JSON_NAMESPACE_VERSION_CONCAT(major, minor, patch) \
|
||||
NLOHMANN_JSON_NAMESPACE_VERSION_CONCAT_EX(major, minor, patch)
|
||||
|
||||
#if NLOHMANN_JSON_NAMESPACE_NO_VERSION
|
||||
#define NLOHMANN_JSON_NAMESPACE_VERSION
|
||||
#else
|
||||
#define NLOHMANN_JSON_NAMESPACE_VERSION \
|
||||
NLOHMANN_JSON_NAMESPACE_VERSION_CONCAT(NLOHMANN_JSON_VERSION_MAJOR, \
|
||||
NLOHMANN_JSON_VERSION_MINOR, \
|
||||
NLOHMANN_JSON_VERSION_PATCH)
|
||||
#endif
|
||||
|
||||
// Combine namespace components
|
||||
#define NLOHMANN_JSON_NAMESPACE_CONCAT_EX(a, b) a ## b
|
||||
#define NLOHMANN_JSON_NAMESPACE_CONCAT(a, b) \
|
||||
NLOHMANN_JSON_NAMESPACE_CONCAT_EX(a, b)
|
||||
|
||||
#ifndef NLOHMANN_JSON_NAMESPACE
|
||||
#define NLOHMANN_JSON_NAMESPACE \
|
||||
nlohmann::NLOHMANN_JSON_NAMESPACE_CONCAT( \
|
||||
NLOHMANN_JSON_ABI_TAGS, \
|
||||
NLOHMANN_JSON_NAMESPACE_VERSION)
|
||||
#endif
|
||||
|
||||
#ifndef NLOHMANN_JSON_NAMESPACE_BEGIN
|
||||
#define NLOHMANN_JSON_NAMESPACE_BEGIN \
|
||||
namespace nlohmann \
|
||||
{ \
|
||||
inline namespace NLOHMANN_JSON_NAMESPACE_CONCAT( \
|
||||
NLOHMANN_JSON_ABI_TAGS, \
|
||||
NLOHMANN_JSON_NAMESPACE_VERSION) \
|
||||
{
|
||||
#endif
|
||||
|
||||
#ifndef NLOHMANN_JSON_NAMESPACE_END
|
||||
#define NLOHMANN_JSON_NAMESPACE_END \
|
||||
} /* namespace (inline namespace) NOLINT(readability/namespace) */ \
|
||||
} // namespace nlohmann
|
||||
#endif
|
||||
|
||||
|
||||
/*!
|
||||
@brief namespace for Niels Lohmann
|
||||
@see https://github.com/nlohmann
|
||||
@since version 1.0.0
|
||||
*/
|
||||
NLOHMANN_JSON_NAMESPACE_BEGIN
|
||||
|
||||
/*!
|
||||
@brief default JSONSerializer template argument
|
||||
|
||||
This serializer ignores the template arguments and uses ADL
|
||||
([argument-dependent lookup](https://en.cppreference.com/w/cpp/language/adl))
|
||||
for serialization.
|
||||
*/
|
||||
template<typename T = void, typename SFINAE = void>
|
||||
struct adl_serializer;
|
||||
|
||||
/// a class to store JSON values
|
||||
/// @sa https://json.nlohmann.me/api/basic_json/
|
||||
template<template<typename U, typename V, typename... Args> class ObjectType =
|
||||
std::map,
|
||||
template<typename U, typename... Args> class ArrayType = std::vector,
|
||||
class StringType = std::string, class BooleanType = bool,
|
||||
class NumberIntegerType = std::int64_t,
|
||||
class NumberUnsignedType = std::uint64_t,
|
||||
class NumberFloatType = double,
|
||||
template<typename U> class AllocatorType = std::allocator,
|
||||
template<typename T, typename SFINAE = void> class JSONSerializer =
|
||||
adl_serializer,
|
||||
class BinaryType = std::vector<std::uint8_t>, // cppcheck-suppress syntaxError
|
||||
class CustomBaseClass = void>
|
||||
class basic_json;
|
||||
|
||||
/// @brief JSON Pointer defines a string syntax for identifying a specific value within a JSON document
|
||||
/// @sa https://json.nlohmann.me/api/json_pointer/
|
||||
template<typename RefStringType>
|
||||
class json_pointer;
|
||||
|
||||
/*!
|
||||
@brief default specialization
|
||||
@sa https://json.nlohmann.me/api/json/
|
||||
*/
|
||||
using json = basic_json<>;
|
||||
|
||||
/// @brief a minimal map-like container that preserves insertion order
|
||||
/// @sa https://json.nlohmann.me/api/ordered_map/
|
||||
template<class Key, class T, class IgnoredLess, class Allocator>
|
||||
struct ordered_map;
|
||||
|
||||
/// @brief specialization that maintains the insertion order of object keys
|
||||
/// @sa https://json.nlohmann.me/api/ordered_json/
|
||||
using ordered_json = basic_json<nlohmann::ordered_map>;
|
||||
|
||||
NLOHMANN_JSON_NAMESPACE_END
|
||||
|
||||
#endif // INCLUDE_NLOHMANN_JSON_FWD_HPP_
|
||||
94
sdi_toolBox_2.x.x/toolBox/sdi_toolBox/namespace_doc.h
Normal file
94
sdi_toolBox_2.x.x/toolBox/sdi_toolBox/namespace_doc.h
Normal file
@@ -0,0 +1,94 @@
|
||||
#pragma once
|
||||
|
||||
// This file contains only Doxygen documentation for namespaces.
|
||||
// No code should be added here.
|
||||
|
||||
/**
|
||||
* @namespace sdi_toolBox
|
||||
* @brief Root namespace for the sdi_toolBox library.
|
||||
*/
|
||||
|
||||
/**
|
||||
* @namespace sdi_toolBox::common
|
||||
* @brief Cross-platform, reusable components shared across all sdi_toolBox modules.
|
||||
*
|
||||
* The common namespace groups utilities and building blocks that are not
|
||||
* tied to any specific platform or subsystem (desktop, embedded, etc.)
|
||||
* and can be freely used across the entire library.
|
||||
*/
|
||||
|
||||
/**
|
||||
* @namespace sdi_toolBox::common::utils
|
||||
* @brief General-purpose utility functions shared across all sdi_toolBox modules.
|
||||
*
|
||||
* Provides lightweight, header-only helpers with no dependency on any
|
||||
* specific platform or subsystem.
|
||||
*/
|
||||
|
||||
/**
|
||||
* @namespace sdi_toolBox::common::utils::hash
|
||||
* @brief Lightweight hashing utilities (FNV-1a implementations).
|
||||
*
|
||||
* Provides constexpr, header-only implementations of the FNV-1a hashing
|
||||
* algorithms (32-bit and 64-bit) with overloads that accept:
|
||||
* - std::span<const std::byte>
|
||||
* - std::string_view
|
||||
* - trivially-copyable POD objects
|
||||
*
|
||||
* These utilities are intended for fast, deterministic hashing of byte
|
||||
* sequences and simple value-based hashing of POD types. Implementations
|
||||
* are usable in constexpr contexts for string literals and other compile-time
|
||||
* scenarios.
|
||||
*
|
||||
* @note Use the POD overloads only for trivially-copyable types where
|
||||
* hashing the raw memory representation is intended.
|
||||
* @see fnv1a(), fnv1a_64()
|
||||
*/
|
||||
|
||||
/**
|
||||
* @namespace sdi_toolBox::desktop
|
||||
* @brief Desktop-specific components of the sdi_toolBox library.
|
||||
*/
|
||||
|
||||
/**
|
||||
* @namespace sdi_toolBox::desktop::eventBus
|
||||
* @brief Lightweight thread-safe event bus for decoupled message passing.
|
||||
*
|
||||
* The eventBus namespace provides a publish/subscribe messaging system that
|
||||
* allows decoupled communication between components. It is built around three
|
||||
* core concepts:
|
||||
*
|
||||
* - **Bus** : the central dispatcher that maintains a routing table and
|
||||
* delivers messages to the appropriate subscribers.
|
||||
* - **Node** : a subscriber attached to a Bus that can send and receive
|
||||
* messages through its internal FIFO queue.
|
||||
* - **Message** : the base class for all messages circulating in the bus,
|
||||
* identified by a unique @ref MessageTypeID.
|
||||
*
|
||||
* Nodes can subscribe to specific message types or to broadcast mode.
|
||||
* All operations are thread-safe.
|
||||
*
|
||||
* @see Bus
|
||||
* @see Node
|
||||
* @see Message
|
||||
*/
|
||||
|
||||
/**
|
||||
* @namespace sdi_toolBox::desktop::utils
|
||||
* @brief General-purpose utility functions and helpers for desktop applications.
|
||||
*/
|
||||
|
||||
/**
|
||||
* @namespace sdi_toolBox::desktop::utils::uuid
|
||||
* @brief Utility functions for UUID and unique identifier generation.
|
||||
*
|
||||
* Provides functions to generate unique identifiers in various formats:
|
||||
* - @ref uniqid() : timestamp-based unique identifier (PHP-style).
|
||||
* - @ref v4() : randomly generated UUID (RFC 4122 version 4).
|
||||
* - @ref v7() : time-ordered UUID (RFC 4122 version 7).
|
||||
*/
|
||||
|
||||
/**
|
||||
* @namespace sdi_toolBox::desktop::wxWidgets
|
||||
* @brief Namespace containing desktop UI utilities built on top of the wxWidgets framework.
|
||||
*/
|
||||
Reference in New Issue
Block a user