#pragma once #include #include #include #include #include "../util/util.hpp" #define F(str) (reinterpret_cast(PSTR(str))) #define GF(name, str) \ const char __##name[] PROGMEM = str; \ const auto *name = reinterpret_cast(__##name) namespace detail { // Only used for C++ type safety, because otherwise a PSTR would be indistinguishable from a normal c-string struct FlashString; } // namespace detail namespace flash { template static inline T load(const T &object) { T buffer; auto byteBuffer = reinterpret_cast(&buffer); for (auto i = std::size_t{0}; i < sizeof(T); ++i) { byteBuffer[i] = static_cast(pgm_read_byte(&reinterpret_cast(&object)[i])); } return buffer; } template struct [[gnu::progmem]] Wrapper { using value_type = T; inline T operator*() const { return load(value); } inline std::byte operator[](const std::size_t idx) const { const auto bytePtr = reinterpret_cast(&value); return load(bytePtr[idx]); } const T value; }; template struct RamWrapper { using value_type = T; inline constexpr T &operator*() { return value; } inline constexpr const T &operator*() const { return value; } inline std::byte &operator[](const std::size_t idx) { const auto bytePtr = reinterpret_cast(&value); return bytePtr[idx]; } inline const std::byte &operator[](const std::size_t idx) const { const auto bytePtr = reinterpret_cast(&value); return bytePtr[idx]; } T value; }; template struct is_flash_wrapper : std::false_type { }; template struct is_flash_wrapper> : std::true_type { }; template static inline constexpr auto is_flash_wrapper_v = is_flash_wrapper::value; template struct is_ram_wrapper : std::false_type { }; template struct is_ram_wrapper> : std::true_type { }; template static inline constexpr auto is_ram_wrapper_v = is_ram_wrapper::value; template static inline decltype(auto) loadLike(const T &object) { if constexpr (is_ram_wrapper_v>) { return object; } else if constexpr (is_flash_wrapper_v>) { return load(object); } else { static_assert(util::always_false_v, "Invalid wrapper type"); } } } // namespace flash