首先让我说我正在为微控制器创建软件,因此RAM的使用很重要,将大量的const数据块放到非易失性(闪存)内存中是有意义的。
我想要实现的目标是找到一种很好的方式来创建一个"分裂" C ++中的对象。作为一个例子,让我们假设有一个字节的数据(读/写)和一个多字节"收据"用于访问它。让我们说"收据"是一个长字符串,它是一个文件名,它指向的媒体很慢,所以在内存中缓冲单个字节是有意义的,而不是在每个请求上实际读取它。
class Data
{
uint8_t byte;
bool valid;
const char filename[128];
uint8_t read()
{
if (!valid)
performReallySlowRead(filename, &byte);
valid = true;
return byte;
};
void write(uint8_t new_value)
{
byte = new_value;
performRealWriteToMedia(filename, byte);
valid = true;
};
}
这种方法的明显问题是整个130字节最终都在RAM中,而其中只有两个需要更改。所以我想出了分裂对象的想法:
class DataNonConst
{
uint8_t byte;
bool valid;
}
class DataConst
{
const char filename[128];
DataNonConst * const data;
}
static DataNonConst storage;
const DataConst holder("some_long_name_here", &storage);
现在唯一的问题是,如果我想拥有几百个这样的分裂对象,那么创建它们的过程(因此创建两个对象并将第二个链接到第一个)会变得非常无聊和有问题......
所以问题是 - 是否有一些很好的方法可以让它更容易使用,最好是一个聪明的C ++技巧或者一些模板魔术?那就是 - 如何使用单个语句创建链接在一起的两个对象,最好隐藏一个对象?我不认为宏解决方案是可行的,因为没有简单的方法来自动创建存储对象的名称...对象需要是相同的类型,因为我需要嵌入指针在其他地方使用这样的对象(一个函数处理它们,另一个只关心阅读)......我想到的所有解决方案都需要使用虚拟接口来模板(所以你可以通过vtable指针和可能获得奖金模板膨胀)或导致巨大的模板膨胀......
修改
实际上整个问题的一部分可以简化为一个简单的问题 - 有没有办法来“绑定”#34; C ++中成员字段的匿名变量?就像:
const ConstData holder("...", NonConstData()); // this object HAS TO be ROMable
在上面"一厢情愿的想法" holder是ROM中的const对象,它有一个指针/引用/任何一个匿名对象NonConstData创建"某处"在RAM中。 或者:
std:pair<const ConstData &, NonConstData &> holder(ConstData(), NonConstData());
任何允许我不要手动创建两个对象并将它们绑定到另一个对象的东西。
答案 0 :(得分:2)
您可以用整数替换指针,并为所有DataNonConst
个对象创建一个静态数组,如下所示:
class DataNonConst {
uint8_t byte;
bool valid;
};
static DataNonConst storages[MAX_DATA];
class DataConst {
const char filename[128];
const int dataIndex;
DataNonConst *data() {
return &storages[dataIndex];
}
};
const DataConst holderOne("first_long_name_here", 0);
const DataConst holderTwo("second_long_name_here", 1);
const DataConst holderThree("third_long_name_here", 2);
这种方法受到Flyweight Pattern的启发,虽然它在这里显然用于不同的目的。
明显的缺点是您需要手动计算您的条目以避免重复。但是,只有一个storages
对象,因此没有其他内容可供创建。
答案 1 :(得分:2)
据我所知,这里唯一的问题是你的“Handle”对象有一个char[]
成员。我敢打赌,如果用const char*
替换它,那就解决了你的问题,因为字符串文字可能还在非易失性存储器中。
class Data
{
const char* filename;
uint8_t byte;
bool valid;
Data(const char* filename_) : filename(filename_), valid(false) {}
uint8_t read()
{
if (!valid)
performReallySlowRead(filename, &byte);
valid = true;
return byte;
};
void write(uint8_t new_value)
{
byte = new_value;
performRealWriteToMedia(filename, byte);
valid = true;
};
};
Data holder("some_long_name_here"); //name is in the non-volatile memory anyway
char buffer[128] = "\var\bin\omg";
Data holder2(buffer); //runtime names work fine, but have to manage the storage yourself.
或者,如果你想变得狡猾,你可以把它变成一个模板并节省大量的内存(代价是稍微更加混乱。这样做还有额外的好处轻松添加static
成员,这些成员也将位于非易失性内存中,但不会按文件名共享。这并不明显,但可以用于名称未知的文件运行时,但我不建议这样做。
template<const char* filename>
class Data
{
uint8_t byte;
bool valid;
uint8_t read()
{
if (!valid)
performReallySlowRead(filename, &byte);
valid = true;
return byte;
};
void write(uint8_t new_value)
{
byte = new_value;
performRealWriteToMedia(filename, byte);
valid = true;
};
};
Data<"some_long_name_here"> holder;
extern char buffer[128] = "\var\bin\omg";
A<buffer> holder2; //this always refers to the filename currently in buffer
但是,如果真的想要一个拆分对象,那么你所拥有的拆分建议实际上是我能想到的最佳答案。
答案 2 :(得分:2)
通过一些限制,您可以利用模板创建每个ROMable DataConst对象引用的DataNonConst静态实例。
限制:
代码使用__LINE__
宏,因此每行只能有一个DataConst声明。或者,如果__COUNT__
宏可用,它可以代替__LINE__
来允许
每行多个声明。
DataNonConst
个对象始终为零初始化。
代码:
struct DataNonConst
{
uint8_t byte;
bool valid;
};
struct DataConst
{
const char filename[128];
DataNonConst * const data;
};
namespace {
template <long n>
struct DataNonConstHolder
{
static DataNonConst data;
};
template <long n> DataNonConst DataNonConstHolder<n>::data;
}
// If __COUNT__ macro is supported, use it instead of __LINE__
#define Initializer(s) { s, &DataNonConstHolder<__LINE__>::data }
const DataConst dc1 = Initializer("filename1");
const DataConst dc2 = Initializer("filename2");
答案 3 :(得分:0)
只需声明const
成员static const
。
static
成员都与对象实例数据分开存储。您的编译器可能会自动将static const
个成员放在ROM中,但如果没有,则可能会有强制执行的编译器指令。
答案 4 :(得分:0)
如果您愿意拥有最大的对象数,则可以使用索引作为隐式指针:
class Data;
Data *dataArray;
char **filenameArray;
class Data {
uint8_t byte;
bool valid;
char*& filename() {
int index = this - dataArray;
return filenameArray[index];
}
public:
byte read() {
if (!valid)
performReallySlowRead(filename(), &byte);
valid = true;
return byte;
}
init(char* fn) {
filename() = allocateInFlash(strlen(fn));
strdup(filename(), fn);
valid = FALSE;
}
};
// Somewhere before things get run
dataArray = allocateInRam(sizeof(Data)*nDatas);
filenameArray = allocateInFlash(sizeof(char*)*nDatas);
这意味着您不需要任何开销,并且您无需触摸闪存即可从ram读取。您也不再使用标准内存管理,可能是加号或减号。
答案 5 :(得分:0)
如果你愿意有一些纯粹的闪存开销,你可以这样做:
class Data;
std::map<Data*,string, less<Data*>, FlashAllocator<pair<Data*,string>> fns;
class Data {
uint8_t byte;
bool valid;
public:
byte read(){
if (!valid)
performReallySlowRead(fns[this],&byte);
valid = true;
return byte;
}
Data(string fn) {
fns[this] = fn;
}
~Data() {
fns.erase(this);
}
};
地图有非常重要的开销,但它都是闪存。