#include namespace AddrSpaces { template struct IntegralConstant {}; template struct is_const { static constexpr bool value = false; }; template struct is_const{ static constexpr bool value = true; }; template struct remove_const { typedef T type; }; template struct remove_const { typedef T type; }; template< class T > using remove_const_t = typename remove_const::type; template struct remove_pointer { typedef T type; }; template struct remove_pointer { typedef T type; }; template< class T > using remove_pointer_t = typename remove_pointer::type; template struct is_same { static constexpr bool value = false; }; template struct is_same{ static constexpr bool value = true; }; template< class T, class U > constexpr bool is_same_v = is_same::value; template< class T > constexpr bool is_const_v = is_const::value; template struct enable_if {}; template struct enable_if { typedef T type; }; template< bool B, class T = void > using enable_if_t = typename enable_if::type; template< class T > constexpr T* addressof( T& arg ) noexcept { return __builtin_addressof (arg); // Should use std::addressof but not available on AVR } template class Ref { public: using space = AddrSpace_; using type = T; using pointer = typename space::pointer; static_assert (!space::readOnly || is_const_v, "Attempting to put non-const variable into read-only address space"); inline __attribute__((always_inline)) explicit constexpr Ref (pointer ptr) : ptr (ptr) {} template >> inline __attribute__((always_inline)) constexpr Ref (const Ref>& src) : ptr (src.ptr) {} template && !is_same_v && !is_const_v>> inline __attribute__((always_inline)) constexpr Ref (const Ref& src) : ptr (src.ptr) {} template && !is_same_v>> inline __attribute__((always_inline)) constexpr Ref (const Ref& src) : ptr (src.ptr) {} inline __attribute__((always_inline)) constexpr Ref (const Ref&) = default; inline __attribute__((always_inline)) constexpr Ref (Ref&&) = default; inline __attribute__((always_inline)) Ref& operator = (const Ref&) = default; inline __attribute__((always_inline)) Ref& operator = (Ref&&) = default; inline __attribute__((always_inline)) Ref operator + (size_t offset) const { return Ref { ptr + sizeof(T) * offset }; } inline __attribute__((always_inline)) T get () const { return space::template get (ptr); } pointer ptr; }; template class Ref { public: using space = AddrSpace_; using type = T; using pointer = typename space::pointer; static_assert (!space::readOnly || is_const_v, "Attempting to put non-const variable into read-only address space"); inline __attribute__((always_inline)) explicit constexpr Ref (pointer ptr) : ptr (ptr) {} template >> inline __attribute__((always_inline)) constexpr Ref (const Ref [N]>& src) : ptr (src.ptr) {} inline __attribute__((always_inline)) constexpr Ref (const Ref&) = default; inline __attribute__((always_inline)) constexpr Ref (Ref&&) = default; inline __attribute__((always_inline)) Ref& operator = (const Ref&) = default; inline __attribute__((always_inline)) Ref& operator = (Ref&&) = default; // Decays inline __attribute__((always_inline)) Ref operator + (size_t offset) const { return Ref { space::ptrAdd (ptr, sizeof(T) * offset) }; } inline __attribute__((always_inline)) T get (size_t index) const { return space::template get (((*this) + index).ptr); } static constexpr size_t size () { return N; } pointer ptr; }; template struct Obj { using space = AddrSpace_; using type = T; type obj; inline __attribute__((always_inline)) type* operator -> () { return &obj; } inline __attribute__((always_inline)) const type* operator -> () const { return &obj; } inline __attribute__((always_inline)) type& operator * () { return obj; } inline __attribute__((always_inline)) const type& operator * () const { return obj; } template static inline __attribute__((always_inline)) constexpr Ref ref (const Obj&) { return Ref { space::template getAddr () }; } template static inline __attribute__((always_inline)) constexpr Ref ref (Obj&) { return Ref { space::template getAddr () }; } }; class Linear { public: using pointer = void*; static constexpr bool readOnly = false; template static inline __attribute__((always_inline)) remove_const_t get (pointer ptr) { return *reinterpret_cast (ptr); } static inline __attribute__((always_inline)) size_t strlen (pointer ptr) { return ::strlen (static_cast (ptr)); } static inline __attribute__((always_inline)) int memcmp (const void* lhs, const void* rhs, size_t count) { return ::memcmp (lhs, rhs, count); } template static inline __attribute__((always_inline)) constexpr pointer getAddr () { return const_cast (reinterpret_cast (addressof (Obj))); } static inline __attribute__((always_inline)) constexpr pointer ptrAdd (pointer a, size_t b) { return static_cast (static_cast (a) + b); } }; class ProgmemNear { public: using pointer = const void*; static constexpr bool readOnly = false; private: template static inline __attribute__((always_inline)) T get (pointer ptr, IntegralConstant<1>) { auto res = pgm_read_byte_near (ptr); return reinterpret_cast (res); } template static inline __attribute__((always_inline)) T get (pointer ptr, IntegralConstant<2>) { auto res = pgm_read_word_near (ptr); return reinterpret_cast (res); } template static inline __attribute__((always_inline)) T get (pointer ptr, IntegralConstant<4>) { auto res = pgm_read_dword_near (ptr); return reinterpret_cast (res); } #ifdef pgm_read_qword_near template static inline __attribute__((always_inline)) T get (pointer ptr, IntegralConstant<8>) { auto res = pgm_read_qword_near (ptr); return reinterpret_cast (res); } #endif public: template static inline __attribute__((always_inline)) T get (pointer ptr) { return get (ptr, IntegralConstant {}); } static inline __attribute__((always_inline)) size_t strlen (pointer ptr) { return ::strlen_P (static_cast (ptr)); } static inline __attribute__((always_inline)) int memcmp (const void* lhs, pointer rhs, size_t count) { return ::memcmp_P (lhs, rhs, count); } template static inline __attribute__((always_inline)) constexpr pointer getAddr () { return reinterpret_cast (addressof (Obj)); } // Should use std::addressof but not available on AVR static inline __attribute__((always_inline)) constexpr pointer ptrAdd (pointer a, size_t b) { return static_cast (static_cast (a) + b); } }; #ifdef pgm_read_byte_far class ProgmemFar { public: using pointer = uint_farptr_t; static constexpr bool readOnly = false; private: template static inline __attribute__((always_inline)) T get (pointer ptr, IntegralConstant<1>) { auto res = pgm_read_byte_far (ptr); return reinterpret_cast (res); } template static inline __attribute__((always_inline)) T get (pointer ptr, IntegralConstant<2>) { auto res = pgm_read_word_far (ptr); return reinterpret_cast (res); } template static inline __attribute__((always_inline)) T get (pointer ptr, IntegralConstant<4>) { auto res = pgm_read_dword_far (ptr); return reinterpret_cast (res); } #ifdef pgm_read_qword_far template static inline __attribute__((always_inline)) T get (pointer ptr, IntegralConstant<8>) { auto res = pgm_read_qword_far (ptr); return reinterpret_cast (res); } #endif public: template static inline __attribute__((always_inline)) T get (pointer ptr) { return get (ptr, IntegralConstant {}); } static inline __attribute__((always_inline)) size_t strlen (pointer ptr) { return ::strlen_PF (ptr); } static inline __attribute__((always_inline)) int memcmp (const void* lhs, pointer rhs, size_t count) { return ::memcmp_PF (lhs, rhs, count); } template static inline __attribute__((always_inline)) pointer getAddr () { // pgm_get_far_address uses the '&' operator which might be overloaded for Obj. // Use C-Cast to char* to get rid of all qualifiers and dereference such that pgm_get_far_address gets the address correctly using the '&' operator. return pgm_get_far_address(*((char*) (__builtin_addressof (Obj)))); } static inline __attribute__((always_inline)) constexpr pointer ptrAdd (pointer a, size_t b) { return a + b; } }; #endif template inline __attribute__((always_inline)) constexpr decltype(auto) ref () { return O.template ref (O); } template inline __attribute__((always_inline)) constexpr Ref linear (T& obj) { return Ref { addressof (obj) }; } template auto ref_static_cast (const Ref& ref) -> Ref (static_cast (nullptr)))>> { return Ref { ref.ptr }; } template inline __attribute__((always_inline)) size_t strlen (Ref ptr) { return AddrSpace::strlen (ptr.ptr); } template inline __attribute__((always_inline)) int memcmp (Ref lhs, Ref rhs, size_t count) { return Linear::memcmp (lhs.ptr, rhs.ptr, count); } template inline __attribute__((always_inline)) int memcmp (Ref lhs, Ref rhs, size_t count) { return AddrSpace::memcmp (lhs.ptr, rhs.ptr, count); } template inline __attribute__((always_inline)) int memcmp (Ref lhs, Ref rhs, size_t count) { return AddrSpace::memcmp (rhs.ptr, lhs.ptr, count); } } #define SPACES_LINEAR(type,name,...) static ::AddrSpaces::Obj<::AddrSpaces::Linear, type> spacesObj_##name { __VA_ARGS__ }; constexpr const inline ::AddrSpaces::Ref<::AddrSpaces::Linear, type> name = ::AddrSpaces::ref < spacesObj_##name > (); #define SPACES_PROGMEM_NEAR(type,name,...) static constexpr const ::AddrSpaces::Obj<::AddrSpaces::ProgmemNear, type> spacesObj_##name PROGMEM { __VA_ARGS__ }; constexpr const inline ::AddrSpaces::Ref<::AddrSpaces::ProgmemNear, const type> name = ::AddrSpaces::ref < spacesObj_##name > (); #ifdef PROGMEM_FAR #define SPACES_PROGMEM_FAR(type,name,...) static constexpr const ::AddrSpaces::Obj<::AddrSpaces::ProgmemFar, type> spacesObj_##name PROGMEM_FAR { __VA_ARGS__ }; inline ::AddrSpaces::Ref<::AddrSpaces::ProgmemFar, const type> name = ::AddrSpaces::ref < spacesObj_##name > (); #else // Mostly for testing, Arduino doesn't support PROGMEM_FAR ? #define SPACES_PROGMEM_FAR(type,name,...) static constexpr const ::AddrSpaces::Obj<::AddrSpaces::ProgmemFar, type> spacesObj_##name PROGMEM { __VA_ARGS__ }; inline ::AddrSpaces::Ref<::AddrSpaces::ProgmemFar, const type> name = ::AddrSpaces::ref < spacesObj_##name > (); #endif char ramArr [3]; const AddrSpaces::Obj progmemArr PROGMEM { { 1, 2, 3 } }; SPACES_LINEAR(char [3], ramArr2, 1, 2, 3); SPACES_LINEAR(const char [3], ramArr3, 1, 2, 3); SPACES_PROGMEM_NEAR(char [3], progmemArr2, 1, 2, 3); SPACES_PROGMEM_FAR(char [4], progmemArr3, 1, 2, 3); SPACES_PROGMEM_NEAR(int, progmemVar, 42); SPACES_PROGMEM_FAR(int, progmemVar2, 33); void setup() { (void) strlen (ramArr); (void) strlen (AddrSpaces::ref ()); strlen (progmemArr2); strlen (progmemArr3); memcmp (AddrSpaces::ref_static_cast (AddrSpaces::linear (ramArr)), AddrSpaces::ref_static_cast (progmemArr2), 3); memcmp (AddrSpaces::ref_static_cast (ramArr2), AddrSpaces::ref_static_cast (progmemArr2), 3); memcmp (AddrSpaces::ref_static_cast (progmemArr2), AddrSpaces::ref_static_cast (ramArr2), 3); memcmp (AddrSpaces::ref_static_cast (progmemArr2), AddrSpaces::ref_static_cast (ramArr3), 3); ramArr2.get (2); ramArr3.get (2); progmemArr2.get (2); progmemArr3.get (2); progmemVar.get (); progmemVar2.get (); } void loop() { // put your main code here, to run repeatedly: }