请考虑以下代码:
typedef float (*MathsOperation)(float _1, float _2);
struct Data
{
float x, y;
MathsOperation op;
};
Data data[100];
float Add(float _1, float _2){//add}
float Sub(float _1, float _2){//subtract}
float Mul(float _1, float _2){//multiply}
// other maths operations
for (int i = 0; i < 100; ++i)
{
// assign one of the maths operators above to data struct member op
// according to some condition (maybe some user input):
if(condition1) data[i].op = &Add;
if(condition2) data[i].op = ⋐
if(condition3) data[i].op = &Mul;
// etc.
}
现在我想以某种方式将data
数组保存到文件中并稍后加载(可能在另一个程序中,该程序不知道用于分配运算符的条件每个数组元素)。显然,每次运行应用程序时指针都会有所不同。所以,我的问题是最好的方法是什么?
答案 0 :(得分:3)
无论如何,您无法将“函数”存储为数据,正如您所说,将指针存储在外部媒体中不起作用。因此,在这种情况下您需要做的是存储运算符值,例如
enum Operator
{
Op_Add,
Op_Sub,
Op_Mul,
Op_Largest // For array size below.
};
而不是:
if(condition1) data[i].op = &Add;
if(condition2) data[i].op = ⋐
if(condition3) data[i].op = &Mul;
有:
if(condition1) data[i].op = Op_Add;
if(condition2) data[i].op = Op_Sub;
if(condition3) data[i].op = Op_Mul;
由于这是一个整数类型的值,它可以存储在一个文件中,然后你可以这样做:
// Or `fin.read(reinterpret_cast<char*>(data), sizeof(data))
fin >> op >> x >> y;
if (op == Op_Add) ...
else if (op == Op_Sub) ...
或者有一个用op
索引的函数指针数组......换句话说:
typedef float (*MathsOperation)(float _1, float _2);
...
MathsOperation mathsOps[Op_Largest] = { &Add, &Sub, &Mul };
...
mathsOps[op](x, y);
...
答案 1 :(得分:1)
如果我在哪里建立索引,您可以在哪里注册运营商
static std::array<MathsOperation> MathsOperations;
MathsOperations.push_back(Add);
MathsOperations.push_back(Sub);
MathsOperations.push_back(Mul);
int getIdx(MathsOperation op) {
return std::find(MathsOperations.begin(), MathsOperations.end(), op) - MathsOperations.begin();
}
并将其放在.h
定义
MathsOperation
文件中
然后保存函数指针,然后可以保存相关索引并随后访问运算符
int opidx = getIdx(Add);
MathsOperator op = MathsOperator[idx];
答案 2 :(得分:1)
非便携式,但如果所有功能都在同一个模块中,几乎肯定能够正常工作:
template<typename FuncT>
intptr_t FunctionPointerToId( FuncT* fptr )
{
return reinterpret_cast<intptr_t>(fptr) - reinterpret_cast<intptr_t>(&Add);
}
template<typename FuncT>
FuncT* FunctionPointerFromId( intptr_t id )
{
return reinterpret_cast<FuncT*>(i + reinterpret_cast<intptr_t>(&Add));
}
这假设您的实现保留了同一模块中函数的相对地址(大多数平台确实将此保证为特定于实现的行为,因为动态加载器依赖于此)。使用相对地址(也称为“基础指针”),即使模块是每次加载到不同基址的共享库(例如ASLR),它仍然可以工作。
如果您的功能来自多个模块,请不要尝试这样做。
如果你有能力构建和维护一个函数列表,那么将索引存储到该列表中肯定是一种更好的方法(即使在重新链接之后这些索引仍然可以保持良好状态,而相对代码地址会发生变化)。
答案 3 :(得分:1)
每个功能都需要一些永久标识符。您可以在阅读后保存此标识符,而不是函数地址和恢复地址。
最简单的是整数标识符,它是数组的索引
const MathsOperation Operations[] = { &Add, &Sub };
在这种情况下,您绝不能更改操作项的顺序。
如果不可能,请使用字符串:
const std::map<std::string, MathsOperation> OpNames
{
{ "Add", &Add },
{ "Sub", &Sub },
};