我正在使用微控制器,只能在闪存 RAM 中的固定地址静态分配一定大小的数组。如果所需的大小超过最大大小,我还需要在另一个固定地址分配第二个数组,其大小与差异相同。
unsigned char buffer[CONF_BUFF_SIZE] __attribute__ ((section ("SRAM_BANK2"))); // allocated on free SRAM at 0x1000
#ifdef CONF_BUFF_SIZE > BUFF_MAX
unsigned char buffer[CONF_BUFF_SIZE] __attribute__ ((section ("SRAM_BANK4"))); // allocated on 0xF000
#endif
// lots of yuck and muck with indexing and switching addresses
Run Code Online (Sandbox Code Playgroud)
我想将代码转换为现代 C++ (c++2x)。我还想对两个缓冲区进行迭代和索引,就好像它是一个连续的数组一样,而无需显式计算索引和切换数组。我如何在惯用的现代 C++ 中实现这一点?
缓冲区[1000] = 0xFE;// 修改“buffer2”中的内存
如果使用缓冲区的函数可以使用迭代器,则可以连接两个std::spans:
#include <ranges>
#include <span>
auto glue = {std::span<unsigned char>(buffer),
std::span<unsigned char>(buffer2)};
auto joined = glue | std::views::join;
for (auto& ch : joined) {
ch = ...; // assign
}
Run Code Online (Sandbox Code Playgroud)
请注意,您没有获得随机访问迭代器,因此这适合读取/写入大数据块,但是unsigned char如果不实际迭代begin()或通过手动在哪个缓冲区中进行计算,则无法访问连接缓冲区中的特定内容unsigned char已放置您要访问的内容。
用于组合支持迭代器和可被视为随机访问的内容的类可能如下所示。
从本质上讲,它是sstd::array之一和 s之上的 sstd::span之一(如上所示)。ranges::join_viewspan
重载operator[]执行运行时计算(在get成员函数内部)以在正确的缓冲区中进行查找 - 因此在缓冲区 1000 中查找元素将比在缓冲区 1 中查找元素花费更长的时间。尝试越界访问,它不会进行边界检查,但如果您尝试进行 OOB 访问,缓冲区索引计算将以std::terminate(). 如果您愿意,可以用抛出异常来替换它。
我尝试get通过像 // 链一样呈现折叠表达式来使缓冲区索引计算可读if,但呈现它并不容易,因此很容易阅读else if。else
#include <algorithm>
#include <array>
#include <iostream>
#include <ranges>
#include <span>
#include <utility>
template <class T, std::size_t... Ss>
class glued_view {
using IS = std::make_index_sequence<sizeof...(Ss)>;
using span_array =
std::array<std::span<T, std::dynamic_extent>, sizeof...(Ss)>;
static constexpr auto view_builder(span_array& sps) noexcept {
return sps | std::views::join;
}
public:
constexpr glued_view(T (&... arrays)[Ss]) noexcept
: spans{arrays...}, view(view_builder(spans)) {}
constexpr glued_view& operator=(std::initializer_list<T> il) {
std::copy_n(il.begin(), std::min(size(), il.size()), begin());
return *this;
}
constexpr std::size_t size() const noexcept { return (... + Ss); }
// semi-random access
constexpr T& operator[](std::size_t index) noexcept {
return get(index, IS{});
}
constexpr const T& operator[](std::size_t index) const noexcept {
return get(index, IS{});
}
// iterators
constexpr auto begin() { return view.begin(); }
constexpr auto end() { return view.end(); }
private:
span_array spans;
decltype(view_builder(spans)) view;
// lookup helper
template <std::size_t... I>
constexpr T& get(std::size_t idx,
std::index_sequence<I...>) const noexcept {
T* p;
(void)(((
/*if*/ ((idx < Ss) // correct buffer?
/*then*/ && (p = &spans[I][idx])) // then store pointer
/*else...*/ || ((void)(idx -= Ss), false)) // else subtract size of current
/*...if*/ || ...) // next if // and try next buffer
/*else*/ || (std::terminate(), false)); // OOB => terminate
return *p;
}
};
Run Code Online (Sandbox Code Playgroud)
一个更简单的方法get是循环,而不是用它来生成一个大的布尔表达式:
constexpr T& get(std::size_t idx) const noexcept {
for(std::size_t i = 0; i < sizeof...(Ss); ++i) {
if(idx < spans[i].size()) return spans[i][idx];
idx -= spans[i].size();
}
std::terminate();
}
Run Code Online (Sandbox Code Playgroud)
一个解决方案是创建一个如下所示的视图类:
#include <cassert>
#include <array>
template<std::size_t N1, std::size_t N2>
class arrays_view
{
public:
arrays_view(std::array<int,N1>& arr1, std::array<int,N2>& arr2) :
m_arr1{arr1},
m_arr2{arr2}
{
}
int& operator[](std::size_t index)
{
assert( index < m_arr1.size() + m_arr2.size() );
if ( index < m_arr1.size() )
{
return m_arr1[index];
}
index -= m_arr1.size();
return m_arr2[index];
}
private:
std::array<int,N1>& m_arr1;
std::array<int,N2>& m_arr2;
};
int main()
{
std::array<int,3> values1{1,2,3};
std::array<int,4> values2{4,5,6,7};
arrays_view view{values1,values2};
assert(view[0] == 1);
assert(view[2] == 3);
assert(view[3] == 4);
assert(view[6] == 7);
view[2] = -1;
assert(view[2] == -1);
}
Run Code Online (Sandbox Code Playgroud)