I开发者_运维技巧 am building a part of a simulator. We are building off of a legacy simulator, but going in different direction, incorporating live bits along side of the simulated bits. The piece I am working on has to, effectively route commands from the central controller to the various bits.
In the legacy code, there is a const array populated with an enumerated type. A command comes in, it is looked up in the table, then shipped off to a switch statement keyed by the enumerated type.
The type enumeration has a choice VALID_BUT_NOT_SIMULATED, which is effectively a no-op from the point of the sim. I need to turn those no-ops into commands to actual other things [new simulated bits| live bits]. The new stuff and the live stuff have different interfaces than the old stuff [which makes me laugh about the shill job that it took to make it all happen, but that is a topic for a different discussion].
I like the array because it is a very apt description of the live thing this chunk is simulating [latching circuits by row and column]. I thought that I would try to replace the enumerated types in the array with pointers to functions and call them directly. This would be in lieu of the lookup+switch.
Can't be done. However, you could do something sort of like it with a functor. I'd put example code but as I was writing it I realized such a construct would necessarily be quite complicated. You might look at boost::bind for some ideas.
One way to do it, though ugly, is to use a generic table of pointers and cast your function pointers to such generic type (losing information about the arguments' types):
void (*myFunctions[]) () = {
(void (*)())myFirstFunction,
(void (*)())mySecondFunction
};
But then you'll have to know, for each of the pointers, what arguments to pass to the corresponding functions. You can extend your table of pointers and make a table of more sophisticated objects which hold some enumeration variable informing about the arguments of a function to which a particular pointer points.
Unfortunately, each time you'll want to use a function from the array, you will need to cast the pointer back to the given type (and you'll have to care not to cast it incorrectly), like so:
((void (*)(int))tab[0])(1);
In order to call myFirstFunction with x = 1.
As I think about it now (after you changed the question), I come to the conclusion that if you have to call the functions differently, there really is no point complicating the whole thing (lookup table), unless there are just a few signatures and many functions available. You need a very consistent calling policy and really few possible signatures to achieve a good-looking solution with a lookup table. Needless to mention what will happen if you need to store pointers to member functions or even worse - virtuals.
Based on your updated question, I'm still not sure how you're going to invoke the functions via the pointer if the functions need different parameter lists.
However, if one parameter list is a subset of the other, could you write thunks to adapt one interface to look like the other? (i.e. discarding irrelevant parameters or synthesising fake parameters).
In your original question, you were describing a scenario that is very common when working with Javascript libraries. In Javascript, libraries often provide a way for "interested parties" to be notified of events that are published by the library, and all that the interested parties need to do is register their own Function
object callbacks. In one version of the library, the documentation might say that the callbacks will be passed n arguments (a
, b
, c
, ... in that order), but a future version might want to provide n + m arguments. This change does not have to break existing code because the library can just append the extra m arguments to the argument list, and this works because Javascript uses a caller-cleans-up calling convention (essentially).
In C++, you could do something similar (provide additional arguments to callbacks) as long as you can guarantee that the calling convention that is used by the callbacks and to call the callbacks is a caller-cleans-up calling convention such as the C calling convention for the x86 architecture:
#include <cstdlib>
#include <iostream>
#include <vector>
extern "C" void old_api_callback_in_old_archive(int x) {
std::cout << "`old_api_callback_in_old_archive` was called with x = " << x << std::endl;
}
extern "C" void new_api_callback(int x, int otherInfo) {
std::cout << "`new_api_callback` was called with x = " << x
<< ", otherInfo = " << otherInfo << std::endl;
}
extern "C" {
typedef void (*callback_type)(int, int);
}
int main()
{
std::vector<callback_type> callbacks;
callbacks.push_back(&new_api_callback);
callbacks.push_back(reinterpret_cast<callback_type>(&old_api_callback_in_old_archive));
std::vector<callback_type>::iterator it;
for (it = callbacks.begin(); it != callbacks.end(); ++it) {
(*it)(7, -8);
}
return EXIT_SUCCESS;
}
精彩评论