Program Listing for File common.h

Program Listing for File common.h#

Return to documentation for file (src/generated/ifgen/common.h)

#pragma once
#ifndef EXAMPLE_IFGEN_COMMON_H
#define EXAMPLE_IFGEN_COMMON_H

#include <bit>
#include <concepts>
#include <cstdint>
#include <istream>
#include <ostream>
#include <spanstream>
#include <streambuf>
#include <utility>

namespace Example
{

/* Enforce that this isn't a mixed-endian system. */
static_assert(std::endian::native == std::endian::big or
              std::endian::native == std::endian::little);

/* Default endianness configured. */
static constexpr auto default_endian = std::endian::little;

/* Detect primitives that don't need byte swapping. */
template <typename T>
concept byte_size = sizeof(T) == 1;

/* No action for byte-sized primitives. */
template <std::endian endianness, byte_size T> inline void handle_endian_p(T *)
{
}
template <std::endian endianness, byte_size T> inline T handle_endian(T elem)
{
    return elem;
}

/* No action if endianness is native. */
template <std::endian endianness, std::integral T>
inline void handle_endian_p(T *)
    requires(not byte_size<T>) && (endianness == std::endian::native)
{
}
template <std::endian endianness, std::integral T>
inline T handle_endian(T elem)
    requires(not byte_size<T>) && (endianness == std::endian::native)
{
    return elem;
}

/* Swap any integral type. */
template <std::endian endianness, std::integral T>
inline T handle_endian(T elem)
    requires(not byte_size<T>) && (endianness != std::endian::native)
{
    return std::byteswap(elem);
}
template <std::endian endianness, std::integral T>
inline void handle_endian_p(T *elem)
    requires(not byte_size<T>) && (endianness != std::endian::native)
{
    *elem = std::byteswap(*elem);
}

/* Handler for 32-bit float. */
template <std::endian endianness, std::floating_point T>
inline void handle_endian_p(T *elem)
    requires(sizeof(T) == sizeof(uint32_t))
{
    handle_endian_p<endianness>(reinterpret_cast<uint32_t *>(elem));
}
template <std::endian endianness, std::floating_point T>
inline T handle_endian(T elem)
    requires(sizeof(T) == sizeof(uint32_t))
{
    return std::bit_cast<T>(
        handle_endian<endianness>(std::bit_cast<uint32_t>(elem)));
}

/* Handler for 64-bit float. */
template <std::endian endianness, std::floating_point T>
inline void handle_endian_p(T *elem)
    requires(sizeof(T) == sizeof(uint64_t))
{
    handle_endian_p<endianness>(reinterpret_cast<uint64_t *>(elem));
}
template <std::endian endianness, std::floating_point T>
inline T handle_endian(T elem)
    requires(sizeof(T) == sizeof(uint64_t))
{
    return std::bit_cast<T>(
        handle_endian<endianness>(std::bit_cast<uint64_t>(elem)));
}

/* Handler for enum class types. */
template <std::endian endianness, typename T>
inline void handle_endian_p(T *elem)
    requires(std::is_enum_v<T>)
{
    using underlying = std::underlying_type_t<T>;
    handle_endian_p<endianness>(reinterpret_cast<underlying *>(elem));
}
template <std::endian endianness, typename T>
inline T handle_endian(T elem)
    requires(std::is_enum_v<T>)
{
    return static_cast<T>(handle_endian<endianness>(std::to_underlying(elem)));
}

/* Configured primitives for identifiers. */
using struct_id_t = uint8_t;
using enum_id_t = uint8_t;

/* Create useful aliases for bytes. */
template <std::size_t Extent = std::dynamic_extent>
using byte_span = std::span<std::byte, Extent>;
template <std::size_t size> using byte_array = std::array<std::byte, size>;

/* Abstract byte-stream interfaces. */
using byte_istream = std::basic_istream<std::byte>;
using byte_ostream = std::basic_ostream<std::byte>;

/* Concrete byte-stream interfaces (based on span). */
using byte_spanbuf = std::basic_spanbuf<std::byte>;
using byte_spanstream = std::basic_spanstream<std::byte>;

/* Constraint for generated structs. */
template <typename T>
concept ifgen_struct = requires {
    std::is_integral_v<decltype(T::id)>;
    std::is_same_v<decltype(T::size), std::size_t>;
};

}; // namespace Example

#endif