Reputation: 257
I'm still new to OOP and c++ and I am trying to learn good habits.
I am in the midst of restructuring a c++ project that I've inherited. One big gripe I have is how they implemented a command-shell interpreter/parser. They basically have the largest if/elseif list in existence and each and every possible response-function is handled in a single file. It quickly blew up to thousands of lines of code in a single file. This is not very efficient (a binary search of commands would be a LOT quicker), and it is a huge struggle to navigate the file to add new commands/responses, or even trace through the behavior of an existing response.
What I'd like to do is 2 things:
1) Take all of these hundreds of functions in the single file and group them into several external files, grouped by some high-level abstract concept that I decide.
2) Have a large table/array that has a string command and it's appropriate function pointer response. I'll sort this and binary search it when appropriate. I'd like to have this table initialized in one place so if there is ever a new command I need to add, I can just append to the table. One table in one spot to map a command to a response
So I am wondering what you c++/oop gurus would suggest in the case on implementing these files and the table. Like I said I'm pretty new, so I'm wondering if all of these new external files should contain just static functions and variables (just a "namespace" file), or if I should just make them full-blown classes. The class concept sounds nice, but I wonder if there will be some kind of instantiation-ordering problem on using these object's method pointers into a compile-time initialized table. Or would a singleton class make sense in this case?
Upvotes: 3
Views: 748
Reputation: 44268
Pretty straightforward solution would be to have a singleton of class that registers and invokes your functions by name:
class FunctionRegistry {
public:
typedef std::function< void() > Function;
void registerFunction( const std::string &name, Function f );
void call( const std::string &name );
static FunctionRegistry &instance();
private:
FunctionRegistry();
FunctionRegistry( const FunctionRegistry & );
...
};
// cpp file
FunctionRegistry &FunctionRegistry::instance()
{
static FunctionRegistry theRegistry;
return theRegistry;
}
You may modify Function signature and method call if you would like to pass something to that function and use std::bind to assign functions which signatures does not exactly match. Inside that class you would have a map or hash map <std::string, Function>
. You may need to add diagnostics like when function with particular name is already registered and when you call a function which is not registered, but that details.
Now you may want to have a helper:
class FunctionRegistrator {
public:
FunctionRegistrator( const std::string &name, FunctionRegistry::Function f )
{
FunctionRegistry::instance().registerFunction( name, f );
}
};
Now you can group your function any way you want, what you need to do in your cpp files:
namespace {
FunctionRegistrator regfunc1( "func1", std::bind( func1 ) );
...
}
for every function you have in that cpp file.
You may want to use boost::bind and boost::function if you cannot use c++11
.
Upvotes: 2