在 AVR 的程序内存中构建编译时任意长度数组
Building compile-time arbitrary length arrays in program memory for AVR
我正在尝试寻找一种聪明的方法来为 AVR 体系结构构建复合编译时数组。该数组的结构应如下所示:
- 它应该完全驻留在程序内存中;
- 它由一系列连续的(无符号)字节组成,又名
uint8_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()
在上面的示例中,我选择了一个显式字节数组声明,但它可以以任何方式构建,例如使用结构等。唯一的前提是必须保证出场顺序
所以简而言之,我想要 "concatenate" 系列字节,其长度必须在编译时计算,并作为系列本身的长度字节放在每个字节系列的前面。
我考虑过使用 variadic macros,但我也想研究其他方法,例如 class 和函数模板、元编程等等,并考虑使用最少的代码。我也不想求助于 C++11-specific,因为目前我使用的 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';
}
或者,您可以使用 sizeof
复合字符数组文字代替 n_args
,如果您想要 C99 解决方案而不是 C++11 解决方案:
#define ELEM(...) sizeof((char[]){__VA_ARGS__}), __VA_ARGS__
我不知道在 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");
}
}
}
此方法与 C99 兼容。
可以对上面的宏进行一些修改,以处理传递的任何类型的数据结构而不是 uint8_t(包括结构中的 :P 结构)
无宏的 C++11 方法(v2):
#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);
}
我正在尝试寻找一种聪明的方法来为 AVR 体系结构构建复合编译时数组。该数组的结构应如下所示:
- 它应该完全驻留在程序内存中;
- 它由一系列连续的(无符号)字节组成,又名
uint8_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()
在上面的示例中,我选择了一个显式字节数组声明,但它可以以任何方式构建,例如使用结构等。唯一的前提是必须保证出场顺序
所以简而言之,我想要 "concatenate" 系列字节,其长度必须在编译时计算,并作为系列本身的长度字节放在每个字节系列的前面。
我考虑过使用 variadic macros,但我也想研究其他方法,例如 class 和函数模板、元编程等等,并考虑使用最少的代码。我也不想求助于 C++11-specific,因为目前我使用的 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';
}
或者,您可以使用 sizeof
复合字符数组文字代替 n_args
,如果您想要 C99 解决方案而不是 C++11 解决方案:
#define ELEM(...) sizeof((char[]){__VA_ARGS__}), __VA_ARGS__
我不知道在 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");
}
}
}
此方法与 C99 兼容。
可以对上面的宏进行一些修改,以处理传递的任何类型的数据结构而不是 uint8_t(包括结构中的 :P 结构)
无宏的 C++11 方法(v2):
#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);
}