在现代 C++ 中,如何将不同地址处的两个或多个数组视为一个数组?

ATL*_*DEV 5 c++ c++20

我正在使用微控制器,只能在闪存 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”中的内存

Ted*_*gmo 6

如果使用缓冲区的函数可以使用迭代器,则可以连接两个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 ifelse

#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)

演示


P K*_*mer 3

一个解决方案是创建一个如下所示的视图类:

#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)