我想从一组可变参数模板参数中表示静态嵌入程序代码(最好在 ROM 部分)中的持久内存布局(例如闪存或 EEPROM 设备)的描述,其中必要的偏移量在编译时自动计算.
目标是创建一个适当的数组初始值设定项,它可以在运行时进行迭代,而不会受到std::get(std::tuple)
需要编译时索引的限制。
第一种方法
我创建了一个简单的数据项描述符类,它将特定 ID(应由客户端作为枚举类型提供)绑定到数据布局(偏移量和大小):
template
< typename ItemIdType
>
struct DataItemDescBase
{
const ItemIdType id;
const std::size_t size;
const std::size_t offset;
DataItemDescBase(ItemIdType id_, std::size_t size_, std::size_t offset_)
: id(id_)
, size(size_)
, offset(offset_)
{
}
DataItemDescBase(const DataItemDescBase<ItemIdType>& rhs)
: id(rhs.id)
, size(rhs.size)
, offset(rhs.offset)
{
}
};
客户端应使用绑定到特定数据类型和偏移量的此类:
template
< typename DataType
, typename ItemIdType
>
struct DataItemDesc
: public DataItemDescBase<ItemIdType>
{
typedef DataType DataTypeSpec;
DataItemDesc(ItemIdType id_, std::size_t offset_ = 0)
: DataItemDescBase(id_,sizeof(DataTypeSpec),offset_)
{
}
DataItemDesc(const DataItemDesc<DataType,ItemIdType>& rhs)
: DataItemDescBase(rhs)
{
}
};
最后我想用 astd::array
来存储具体的数据布局:
const std::array<DataItemDescBase<ItemIdType>,NumDataItems> dataItemDescriptors;
对于客户端,我想从一个std::tuple
或可变参数模板参数列表中提供一个数组初始值设定项,因此后续数组元素的偏移量是在编译时根据偏移量 + 前一个元素的大小自动计算的。
当前的工作是客户端可以使用以下代码来初始化数组:
namespace
{
static const std::array<DataItemDescBase<DataItemId::Values>,4> theDataLayout =
{ { DataItemDesc<int,DataItemId::Values>
( DataItemId::DataItem1 )
, DataItemDesc<short,DataItemId::Values>
( DataItemId::DataItem2
, sizeof(int))
, DataItemDesc<double,DataItemId::Values>
( DataItemId::DataItem3
, sizeof(int) + sizeof(short))
, DataItemDesc<char[10],DataItemId::Values>
( DataItemId::DataItem4
, sizeof(int) + sizeof(short) + sizeof(double))
} };
}
但是让客户手动计算偏移量看起来容易出错且乏味。
TL;博士; 是否可以在编译时计算偏移量,如果可以,你能给我一个草图吗?
第二种方法
我已经尝试了@Yakk 回答中的建议,并且刚刚引入了一个数据感知基类,ProcessedEntry
如下所示:
template<typename Key>
struct ProcessedEntryBase {
const Key id;
const std::size_t offset;
const std::size_t size;
ProcessedEntryBase(Key id_ = Key(), std::size_t offset_ = 0, std::size_t size_ = 0)
: id(id_)
, offset(offset_)
, size(size_) {
}
ProcessedEntryBase(const ProcessedEntryBase<Key>& rhs)
: id(rhs.id)
, offset(rhs.offset)
, size(rhs.size) {
}
};
template<typename Key, Key identifier, typename T, std::size_t Offset>
struct ProcessedEntry
: public ProcessedEntryBase<Key> {
ProcessedEntry()
: ProcessedEntryBase<Key>(identifier,Offset,sizeof(T)) {
}
};
我打算使用一个LayoutManager
可以从构造函数参数继承并提供具体布局的基类:
template<typename Key, std::size_t NumEntries>
class LayoutManager {
public:
typedef std::array<ProcessedEntryBase<Key>,NumEntries> LayoutEntriesArray;
const LayoutEntriesArray& layoutEntries;
// ...
// methods to lookup particular entries by id
// ...
protected:
LayoutManager(LayoutEntriesArray layoutEntries_)
: layoutEntries(layoutEntries_) {
}
};
客户端代码
具体布局.hpp;
struct DataItemId {
enum Values {
DataItem1 ,
DataItem2 ,
DataItem3 ,
DataItem4 ,
};
};
class ConcretePersistentLayout
: public LayoutManager<DataItemId::Values,4> {
public:
ConcretePersistentLayout();
};
具体布局.cpp:
Layout< DataItemId::Values
, Entry< DataItemId::Values, DataItemId::DataItem1, int>
, Entry< DataItemId::Values, DataItemId::DataItem2, short >
, Entry< DataItemId::Values, DataItemId::DataItem3, double >
, Entry< DataItemId::Values, DataItemId::DataItem4, char[10] >
>::type theDataLayout; // using like this gives me a compile error,
// because I have no proper type 'prepend'
// I'd guess
}
ConcretePersistentLayout::ConcretePersistentLayout()
: LayoutManager<DataItemId::Values,4>(theDataLayout)
// ^^^^^^ Would this work to 'unpack' the tuple?
{
}
我想将访问器类与LayoutManager
获取 id、计算持久内存设备地址、获取数据并转换为绑定到键/id 的数据类型的访问器类松散耦合。我计划让客户端明确指定键/数据类型绑定,因此可以对访问器函数进行静态检查。
最后
根据@Yakk在第一轮要求更多澄清后的扩展回答,我现在有一些东西正在生产中。
另外关于评论: