如何让编译时 const 对象配置有一块可变长度的数据?

Posted

技术标签:

【中文标题】如何让编译时 const 对象配置有一块可变长度的数据?【英文标题】:How can I have a compile-time const object configured with a chunk of data of variable length? 【发布时间】:2017-11-22 08:16:48 【问题描述】:

目前我正在为这样的事情苦苦挣扎:

class Command final 
    const std::vector<uint8_t> cmd;
public:
    constexpr Command(const std::initializer_list<uint8_t> cmd_)
        :cmd(cmd_)
        
    void copyToWithSlightModifications(uint8_t* buf, size_t len)  /*...*/ 
    std::string getSomeInformation()  /*...*/ 


const Command cmd1( 0x01, 0x02, 0x03 );
const Command cmd2( 0x01, 0x02, 0x03, 0x04 );
// lots more of these

当然,std::vector 在这里不起作用,但它在这里最能表达我的意图。我尝试了std::array 和其他一些想法,但也都失败了。

背景:这是针对嵌入式开发的,所以我的资源很紧张,而且构造东西倾向于将它们放在内存便宜的闪存上。将其作为真正的向量进行操作会将其放入稀缺的 RAM 内存中。这是在 gcc 版本 5.2.0 上。

TL;DR:我需要一个可变长度的容器,我可以将它作为一个字段放在 const 对象上,并在 constexpr 上下文中对其进行初始化。知道那会是什么样子吗?不必花哨,如果是 uint8_t*size_t 的大小,也可以。

【问题讨论】:

这实际上看起来只需要在编译时存储几个可变大小的数组,然后使用数组视图进行Command 构造。 @VTT:你的解决方案是合适的,但我正在寻找更容易控制的东西。 【参考方案1】:

根据您的描述和代码 sn-p,这似乎符合标准。

template<size_t N>
class Command

    uint8_t cmd[N];
public:
    template<typename... Args>
    constexpr Command(Args... args) : cmduint8_t(args)...
    
        static_assert(sizeof...(Args) == N,
                      "argument count mismatch");
    
;

constexpr Command<4> cmd40, 1, 2, 3;
constexpr Command<2> cmd20, 1;

您也可以编写一个辅助函数来避免显式输入参数计数

template<typename... Args>
constexpr Command<sizeof...(Args)> make_command(Args... args)

    return args...;


constexpr auto cmd4 = make_command(0, 1, 2, 3);
constexpr auto cmd2 = make_command(0, 1);

或者,在 C++17 中有模板推导指南

template<typename... Args>
Command(Args...) -> Command<sizeof...(Args)>;

constexpr Command cmd40, 1, 2, 3;
constexpr Command cmd20, 1;

但是请注意,不同大小的 Command 是不同的类型,这意味着它们无法放入容器中。

【讨论】:

谢谢,我已经设法使用您的解决方案,只需稍作修改并引入基接口类。对于任何感兴趣的人,这里有一个最小的例子:ideone.com/Mg32QW 还有一件事-我的编译器对此答案有一个问题,即我可以做Command c1 uint8_t1, uint8_t2 ,但不能做Command c20x01, 0x02,因为narrowing conversion of 'args#...' from 'int' to 'uint8_t aka unsigned char' inside -对此有什么想法,@Passer By? 对于pre-C++17的情况,可以加一个扣分点:auto make_command(Args&amp;&amp;... args) -&gt; Command&lt;sizeof...(Args)&gt; return Command&lt;sizeof...(Args)&gt;(std::forward&lt;Args...&gt;(args...); @KrzysztofBociurko 文字被推断为具有int 类型,并且由于编译器警告的限制,即使在编译时已知值获胜,它仍会警告您缩小转换'不会溢出。 @KrzysztofBociurko 我添加了一个明确的演员表来解决这个问题。【参考方案2】:

在深度嵌入中,您通常使用循环缓冲区来解决此类问题。 它们具有固定的最大大小,但取决于实现,它们的行为就像列表一样。 不幸的是,标准 c++ 库没有任何循环缓冲区,但网上有很多教程。

我怀疑你可以在受限系统上使用 boost 库,但如果你这样做了,你可以简单地使用 boost::circular_buffer

可能会帮助您实现适合您的循环缓冲区的其他一些页面可能是this 或this

【讨论】:

不明白为什么在这里可能使用循环缓冲区。循环缓冲区用于 FIFO 等,其中排序的数据不断移动 - 这应该是一个在任何时候都不会改变的存储类。你能澄清一下吗? 您可以对环形缓冲区的元素执行随机访问。你可以给它们一个足够大的固定尺寸,并像动态一样使用它。 所以你的意思是使用一个循环缓冲区作为一个非循环的普通旧静态数组,其中有指向特定位置的指针?或者它增加的复杂性有什么隐藏的好处? 另外,据我所知,正确启动它会有问题。一切都必须手动完成,包括计算单独命令的偏移量。 看来我误解了你的问题。我知道你想要一个元素数量可变但静态初始化的数据结构。如果这不是你的问题,我会删除这个答案。

以上是关于如何让编译时 const 对象配置有一块可变长度的数据?的主要内容,如果未能解决你的问题,请参考以下文章

C99 在运行时如何计算可变长度数组的大小?

将节点数组(可变长度)转换为 const float** 以调用 opencv.calcHist

char *和const char *之间的区别?

const和violate

使用可变长度数组是不是有任何开销?

如何让这个 OCR 模型与可变长度示例一起工作?