-3

我想从一组可变参数模板参数中表示静态嵌入程序代码(最好在 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在第一轮要求更多澄清后的扩展回答,我现在有一些东西正在生产中。


另外关于评论:

  1. 在这种情况下,我知道切片问题,并且可以保证存储在其中的派生(模板)类std::array<ProcessedEntryBase>不会添加任何更多的数据成员等。功能绑定(强制转换)是单独完成的。

  2. 提出的索引技巧,也是一个很好的提示,关于如何在运行时解压缩可变参数模板参数以进行索引访问,迭代。

4

1 回答 1

3

为了发生编译时间累积,您必须有一个编译时间序列。

一个简单的方法是使用可变参数模板。每个条目将是特定元素的标识符和大小,或特定元素的标识符和类型。

顶级条目包将是Layout

template<std::size_t offset, typename Key, typename... Entries>
struct LayoutHelper {
  typedef std::tuple<> type;
};
template<typename Key, typename... Entries>
struct Layout:LayoutHelper<0, Key, Entries...> {};

每个条目将是:

template<typename Key, Key identifier, typename Data>
struct Entry {};

然后,我们做这样的事情:

template<typename Key, Key identifier, typename Data, std::size_t Offset>
struct ProcessedEntry {};

template<std::size_t offset, typename Key, Key id0, typename D0, typename... Entries>
struct LayoutHelper<offset, Key, Entry<Key, id0, D0>, Entries...>
{
    typedef typename prepend
        < ProcessedEntry< Key, id0, D0, offset >
        , typename LayoutHelper<offset+sizeof(D0), Key, Entries...>::type
        >::type type;
};

使用看起来像:

Layout< FooEnum, Entry< FooEnum, eFoo, char[10] >, Entry< FooEnum, eFoo2, double > > layout;

在编写或找到 a 之后prepend,它需要一个元素和 a tuple,并在前面添加元素,这意味着Layout<blah>::type它将包含tuple描述数据布局的 a 。

template<typename T, typename Pack>
struct prepend;
template<typename T, template<typename...>class Pack, typename... Ts>
struct prepend<T, Pack<Ts...>> {
  typedef Pack<T, Ts...> type;
};
// use: prepend<int, std::tuple<double>::type is std::tuple<int, double>
// this removes some ::type and typename boilerplate, if it works in your compiler:
template<typename T, typename Pack>
using Prepend = typename prepend<T, Pack>::type;

如果你愿意,你可以把它解压tuple成一个std::array。您将使用索引技巧来做到这一点(有很多关于堆栈溢出的示例以不同的方式使用相同的技巧)。

或者,您可以使用您的ProcessedEntryand add in 方法来访问数据,然后编写一个Key搜索编译时程序,该程序遍历tuple,寻找匹配的Key,然后返回offsetand size(甚至类型)作为编译时代码。也许将 aarray<N, unsigned char>作为参数并执行reintepret_cast,返回对- 的引用data

通过别名删除重复项FooEnum会很好。using

于 2013-08-15T15:58:46.993 回答