C++ 在AVR程序内存中构建编译时任意长度数组
我正试图找出一种聪明的方法来为AVR体系结构构建一个复合编译时数组。阵列的结构应如下所示:C++ 在AVR程序内存中构建编译时任意长度数组,c++,c,arrays,avr-gcc,C++,C,Arrays,Avr Gcc,我正试图找出一种聪明的方法来为AVR体系结构构建一个复合编译时数组。阵列的结构应如下所示: 它应该完全驻留在程序内存中 它由一系列连续的(无符号)字节组成,也称为uint8\u t 它应该使用任意长度的字节段构建 段按顺序由一个长度字节和一系列数据字节组成,长度字节是数据字节数 下面是这样一个数组的示例: static const uint8_t data[] PROGMEM = { 1, 0x01, 3, 0xBE, 0x02, 0x00, 3, 0x3D, 0x3
- 它应该完全驻留在程序内存中李>
- 它由一系列连续的(无符号)字节组成,也称为
李>uint8\u t
- 它应该使用任意长度的字节段构建李>
- 段按顺序由一个长度字节和一系列数据字节组成,长度字节是数据字节数
static const uint8_t data[] PROGMEM = {
1, 0x01,
3, 0xBE, 0x02, 0x00,
3, 0x3D, 0x33, 0x33,
15, 0xE1, 0xD0, 0x00, 0x05, 0x0D, 0x0C, 0x06, 0x2D, 0x44, 0x40, 0x0E, 0x1C, 0x18, 0x16, 0x19,
0 /* end of the sequence */
};
我希望避免每次在序列中添加或删除字节时调整长度字节的负担,例如,以某种形式的伪代码:
BEGINNING_OF_THE_SEQUENCE(identifier)
SEGMENT(0x01),
SEGMENT(0xBE, 0x02, 0x00),
...
END_OF_THE_SEQUENCE()
在上面的示例中,我选择了显式字节数组声明,但它可以以任何方式构建,例如使用结构。唯一的先决条件是必须保证出现的顺序
简而言之,我想“串联”一系列字节,这些字节的长度必须在编译时计算,并作为序列本身的长度字节放在每个字节序列的前面
我曾考虑过使用,但我也想研究其他方法,比如类和函数模板、元编程等等,并考虑使用最小的代码。我也不想求助于C++11的细节,因为目前我使用的avr-gcc
编译器的支持是有限的
我有一种预感,使用模板是可能的,但我被卡住了。有什么想法吗?下面是一个简单的C++11及更高版本的示例,可能会有所帮助:
template <typename ...Args>
constexpr std::size_t n_args(Args...) { return sizeof...(Args); }
#define ELEM(...) n_args(__VA_ARGS__), __VA_ARGS__
#include <iostream>
int main()
{
unsigned int a[] = { ELEM(4, 9, 16),
ELEM(100),
ELEM(10, 20, 30, 40, 50),
};
for (auto n : a ) std::cout << n << " ";
std::cout << '\n';
}
我不知道有一种类似的简单方法可以在C++03中使用。这里有一种方法:
#include <stdint.h>
#include <stdio.h>
#define PROGMEM
#define _ARGSIZE(...) sizeof((uint8_t[]){__VA_ARGS__})/sizeof(uint8_t)
#define _SEGMENT(...) _ARGSIZE( __VA_ARGS__ ), __VA_ARGS__
#define BEGINNING_OF_THE_SEQUENCE(__id) uint8_t static const __id[] PROGMEM = {
#define END_OF_THE_SEQUENCE() }
BEGINNING_OF_THE_SEQUENCE(data)
_SEGMENT(0x01),
_SEGMENT(0xBE, 0x02, 0x00),
_SEGMENT(0xDE, 0xAD, 0xBE, 0xEF)
END_OF_THE_SEQUENCE();
int main() {
int k, counter = data[0];
for (k = 0; k < sizeof(data); k++) {
fprintf(stderr, "%02x ", data[k]);
if(counter-- == 0) {
counter = data[1+k];
fprintf(stderr, "\n");
}
}
}
#包括
#包括
#定义程序
#定义ARGSIZE(…)sizeof((uint8_ut[]){{uuu VA_u-ARGS})/sizeof(uint8_t)
#定义_段(…)_参数大小(uu-VA_-ARGS_uu),u-VA_参数__
#定义\u序列(\uuuu id)uint8\t静态常量\uuu id[]PROGMEM={
#定义\u序列()的结束\u
_序列的开始_(数据)
_段(0x01),
_段(0xBE、0x02、0x00),
_段(0xDE、0xAD、0xBE、0xEF)
_序列()的_结尾;
int main(){
int k,计数器=数据[0];
对于(k=0;k
这种方法与C99兼容
可以谨慎地修改上面的宏,以处理传递的任何类型的数据结构,而不是uint8_t(包括:结构中的p结构)无宏C++11方法(v2):
<代码> >包含不能使用运行时机制来改变编译时结构。它可以用一些巧妙的方法来完成:不带可变模板和CONTXPR,这看起来不现实,而用较新的C++标准,这绝对是可能的。C++。用你实际使用的语言标记。@Nasha:我相应地更新了我的答案。没关系,我用
constepr
检查了你的建议,令人惊讶的是,avr gcc 6.4正确地编译了这个,将所有内容都放在程序内存中。谢谢你指出这一点!顺便说一句,我只是想知道。。。有可能建造整个。。。使用模板而不是混合使用模板和宏的事情(仍在编译时)?@Nasha是的,这是可能的,你应该检查我下面的答案。@Nasha:是的,但要注意你的编译时间。我不知道您的应用程序域有多大,但无限递归变量模板并不一定便宜。我理解并完全赞同你对宏的立场,但我会在这里做一个务实的权衡,以找到合适的解决方案。这还很大程度上取决于你是需要将其放在一个源文件中还是作为一个可重用的库,你是否能找到一个好的、唯一的名称,等等。在抽屉里记下可能的替代方案。谢谢你的见解@KerrekSB。我怀疑编译时间会增加——毕竟递归必须要花费一定的时间,如果不是在运行时,那么它将在编译时,这是合乎逻辑的。这就是我将你的答案标记为解决方案的原因。(也是因为我还在努力理解模板化解决方案是如何工作的。)谢谢。在我发布消息之前,我写了一些类似的东西。它在C和C++中都很好。我猜你的答案涉及元编程,对吧?@ NSAHA显然是因为它包含模板。我倾向于避免在我的MyCulor项目中STL。另外,我不完全理解你的答案,尤其是using
关键字代表什么。事实上,C++的编程细节和结构细节至今仍不清楚。我天真地认为我很容易理解,但我没有。你介意加上一个详细的解释吗?抱歉,我觉得自己像个傻瓜…@Nasha实际上给出的方法并不太依赖于标准库,我只使用了整数类型(uint8\u t
和size\u t
)和std::array
包装器(可以用普通的C样式数组替换,只需稍作修改)<代码>使用关键字用于声明。类中的每个t_Pack
模板实例化t_PackImpl
模板,并将新项目添加到VItems
。确定。现在是否需要to_array()
方法?我仍然需要弄清楚如何将整个字节数组static const
放入程序内存部分。
#include <stdint.h>
#include <stdio.h>
#define PROGMEM
#define _ARGSIZE(...) sizeof((uint8_t[]){__VA_ARGS__})/sizeof(uint8_t)
#define _SEGMENT(...) _ARGSIZE( __VA_ARGS__ ), __VA_ARGS__
#define BEGINNING_OF_THE_SEQUENCE(__id) uint8_t static const __id[] PROGMEM = {
#define END_OF_THE_SEQUENCE() }
BEGINNING_OF_THE_SEQUENCE(data)
_SEGMENT(0x01),
_SEGMENT(0xBE, 0x02, 0x00),
_SEGMENT(0xDE, 0xAD, 0xBE, 0xEF)
END_OF_THE_SEQUENCE();
int main() {
int k, counter = data[0];
for (k = 0; k < sizeof(data); k++) {
fprintf(stderr, "%02x ", data[k]);
if(counter-- == 0) {
counter = data[1+k];
fprintf(stderr, "\n");
}
}
}
#include <array>
#include <iostream>
#include <cstddef>
#include <cstdint>
// This template will be instantiated repeatedly with VItems list
// populated with new items.
template<typename TItem, TItem... VItems> class
t_PackImpl
{
// This template will be selected for second and all other blocks.
public: template<TItem... VInnerItems> using
t_Pack = t_PackImpl
<
TItem
// add all previous items
, VItems...
// add item holding amount of items in new block
, TItem{static_cast<TItem>(sizeof...(VInnerItems))}
// add new block items
, VInnerItems...
>;
// This method will be called on the last instantiated
// template with VItems containing all the items.
// Returns array containing all the items with extra 0 item at the end.
public: static constexpr auto
to_array(void) -> ::std::array<TItem, sizeof...(VItems) + ::std::size_t{1}>
{
return {VItems..., TItem{}};
}
};
// This template will be instantiated just once.
// Starts t_PackImpl instantiation chain.
template<typename TItem> class
t_BeginPack
{
// This template will be selected for first block.
public: template<TItem... VInnerItems> using
t_Pack = t_PackImpl
<
TItem
// add item holding amount of items in new block
, TItem{static_cast<TItem>(sizeof...(VInnerItems))}
// add new block items
, VInnerItems...
>;
};
int main()
{
{
constexpr auto items
{
t_BeginPack<::std::uint8_t>::t_Pack<42>::to_array()
};
for(auto const & item: items)
{
::std::cout << static_cast<::std::uint32_t>(item) << ::std::endl;
}
}
::std::cout << "----------------" << ::std::endl;
{
constexpr auto items
{
t_BeginPack<::std::uint8_t>::t_Pack<0, 1, 2>::to_array()
};
for(auto const & item: items)
{
::std::cout << static_cast<::std::uint32_t>(item) << ::std::endl;
}
}
::std::cout << "----------------" << ::std::endl;
{
constexpr auto items
{
t_BeginPack<::std::uint8_t>::
t_Pack<0, 1, 2>::
t_Pack<0, 1>::
t_Pack<0, 1, 2, 3, 4, 5>::to_array()
};
for(auto const & item: items)
{
::std::cout << static_cast<::std::uint32_t>(item) << ::std::endl;
}
}
return(0);
}