我有一个包含许多命令行选项的代码库。目前,如果在命令行上传入命令,则每个命令行选项都与表中的函数指针一起存在于表中。
e.g。
static CommandFunction s_Commands[] =
{
{ "command1", Func1 },
{ "command2", Func2 },
{ "command3", Func3 },
etc...
};
我的问题是,这张桌子很大,而且这些功能都存在于其他地方。我希望命令的字符串能够存在于每个函数旁边。
例如:
COMMAND_ARG("command1")
void Func1()
{
dostuff
...
}
COMMAND_ARG("command2")
void Func2()
{
dostuff
...
}
COMMAND_ARG("command3")
void Func3()
{
dostuff
...
}
这可能吗?
答案 0 :(得分:6)
您可以使用由函数地址专门设计的模板:
#include <stdio.h>
// In a header file.
template<void(*Fn)()>
struct FnMeta
{
static char const* const meta;
};
// no definition of meta
// some.cc
void some() {}
template<> char const* const FnMeta<some>::meta = "some";
// another.cc
void another() {}
template<> char const* const FnMeta<another>::meta = "another";
// main.cc
int main() {
printf("%s\n", FnMeta<some>::meta);
printf("%s\n", FnMeta<another>::meta);
}
上面的想法是FnMeta<>::meta
未定义。但是,不同的翻译单元(.cc文件)可以提供FnMeta<>::meta
专业化的定义。这样,当使用FnMeta<X>::meta
时,链接器会在另一个转换单元中找到它的适当定义。
答案 1 :(得分:5)
这个特殊问题有不同的方法。您可以使用继承来创建基础Command
,然后实现一些execute
函数(您还可以实现help
,validate
....)。然后创建一个调度程序函数,将名称与命令的实际实现相关联(在各种查找表中,可能是map
)。
虽然这不能解决 locality 的问题,但这个问题可能是真的,也可能不是。也就是说,命令的实现可能到处都是,但是有一个地方可以确定CLI中可用的命令。
如果 locality 对您来说非常重要(代价是在源代码中没有一个位置,其中列出了所有使用的命令),您可以提供一个注册机制,全局可访问,然后提供一个辅助类型,在构造期间将函数注册到机制中。然后,您可以使用每个函数定义创建一个此类对象。
CommandRegistry& getCommandRegistry(); // Access the registry
struct CommandRegister {
CommandRegister(const char* name, Function f) {
getCommandRegistry().registerCmd(name,f);
}
// Optionally add deregistration
};
// ...
void Func2() {...}
static CommandRegister Func2Registration("function2",&Func2);
我个人更喜欢走另一条路...在代码中有一个位置列出了所有命令,因为它允许在一个位置找到执行它的代码的命令(文本)。也就是说,当你有一些命令而其他人需要维护其中一个命令时,它会更容易从命令行转到执行它的实际代码。
答案 2 :(得分:1)
我同意Maxim Yegorushkin的回答,最好尝试使用静态机制,但这里有几种运行时方法可以满足将行为和函数名称保持在一起的要求。
方法#1,命令对象:
class AbstractCommand{
public:
virtual ~AbstractCommand() {}
virtual void exec() = 0;
virtual const char *commandName() const = 0;
};
class Command1 : public AbstractCommand{
public:
virtual void exec() { /* do stuff */ }
virtual const char *commandName() const { return "command name 1"; }
};
class Command2 : public AbstractCommand{
public:
virtual void exec() { /* do stuff */ }
virtual const char *commandName() const { return "command name 2"; }
};
static AbstractCommand *s_commands[] {
new Command1(),
new Command2(),
...,
0
};
方法#2,带选择器的功能:
enum CommandExecOption { GET_NAME, EXEC };
typedef void* (*command_func_t)( CommandExecOption opt );
void *Command1Func( CommandExecOption opt )
{
switch(opt){
case GET_NAME: return "command 1"; break;
case EXEC:
/* do stuff */
break;
}
return 0;
}
void *Command2Func( CommandExecOption opt )
{
switch(opt){
case GET_NAME: return "command 2"; break;
case EXEC:
/* do stuff */
break;
}
return 0;
}
command_func_t s_commands[] = {
Command1Func,
Command2Func,
...,
0
};
答案 3 :(得分:1)
所以你想使用预处理器宏,呵呵?有接缝是坏的,但我经常使用它们。这个答案将基于命令注册表:
class Command
{
public:
Command(std::string const& _name):name(_name){ registry[_name]=this; }
virtual ~Command() { registry.erase(name); }
static void execute( std::string const& name ) {
RegistryType::iterator i = registry.find(name);
if(i!=registry.end()) i->second->_execute();
//some exeption code here
}
protected:
virtual void _execute() = 0;
private:
const std::string name;
typedef std::map< std::string, Command* > RegistryType;
static RegistryType registry;
};
静态注册表应该位于除标题之外的其他位置:
Command::RegistryType Command::registry;
让我们看看我们需要什么(稍微改变一下):
COMMAND_ARG( doSomething )
{
cout << "Something to do!" << std::endl;
}
因此我们需要创建一个继承自Command并已实现_execute
方法的类的对象。由于方法可以在类之外定义,因此该宏将包含所有需要的代码,并使用braced中的代码:
class CommanddoSomething : public Command {
public:
CommanddoSomething () : Command( "doSomething" ) {}
private:
virtual void _execute();
} commanddoSomething;
void CommanddoSomething :: _execute()
{
cout << "Something to do!" << std::endl;
}
所以这是宏观的理想之地:
#define COMMAND_ARG( NAME ) \
class Command ## NAME : public Command { \
public: Command ## NAME () : Command( #NAME ) {} \
private: virtual void _execute(); \
} command ## NAME; \
void Command ## NAME :: _execute()
我希望你喜欢它。