问题
Consider the following code:
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.
}
Now I'd like to somehow save the data
array to a file and load it later (maybe in another program which doesn't know about the conditions that were used to assign the operators to each array element). Obviously, the pointers would be different every time I ran the application. So, my question is what is the best way to do this?
回答1:
You can't store "functions" as data anyway, and as you say, storing pointers in external media doesn't work. So, what you have to do in this case is store an operator value, e.g.
enum Operator
{
Op_Add,
Op_Sub,
Op_Mul,
Op_Largest // For array size below.
};
And instead of:
if(condition1) data[i].op = &Add;
if(condition2) data[i].op = ⋐
if(condition3) data[i].op = &Mul;
have:
if(condition1) data[i].op = Op_Add;
if(condition2) data[i].op = Op_Sub;
if(condition3) data[i].op = Op_Mul;
Since that is an integer type value, it can be stored in a file, and you can then do:
// Or `fin.read(reinterpret_cast<char*>(data), sizeof(data))
fin >> op >> x >> y;
if (op == Op_Add) ...
else if (op == Op_Sub) ...
Or have a function pointer array that you index with op
... In other words:
typedef float (*MathsOperation)(float _1, float _2);
...
MathsOperation mathsOps[Op_Largest] = { &Add, &Sub, &Mul };
...
mathsOps[op](x, y);
...
回答2:
If I where you I would build an index, where you could register your operators
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();
}
and put it in a .h
file just after the MathsOperation
definitions
Then rather then saving the function pointer, you could just save the relevant index and access the operator afterwards
int opidx = getIdx(Add);
MathsOperator op = MathsOperator[idx];
回答3:
Non-portable, but almost certain to work if all your functions are in the same module:
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));
}
This assumes that your implementation preserves relative addresses of functions within the same module (most platforms do guarantee this as implementation-specific behavior, since dynamic loaders rely on this). Using relative addresses (aka "based pointers") allows it to still work even if the module is a shared library that gets loaded at a different base address each time (e.g. ASLR).
Don't try this if your functions come from multiple modules, though.
If you have the ability to build and maintain a list of the functions, storing an index into that list is definitely a better approach (those indexes can remain good even after relinking, while relative code addresses get changed).
回答4:
You need some permanent identifier for each function. You save this identifier instead of function address and restore address after reading.
The simpliest is the integer identifier which is an index of array
const MathsOperation Operations[] = { &Add, &Sub };
In this case you must never change the order of Operations items.
If it is impossible, use strings:
const std::map<std::string, MathsOperation> OpNames
{
{ "Add", &Add },
{ "Sub", &Sub },
};
来源:https://stackoverflow.com/questions/28518367/save-and-load-function-pointers-to-file