2

我有一个包含许多命令行选项的代码库。目前,每个命令行选项与一个函数指针一起存在于一个表中,如果该命令是在命令行上传递的,则该函数指针将运行。

例如

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
    ...
}

这可能吗?

4

4 回答 4

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使用时,链接器会在另一个翻译单元中找到它的适当定义。

于 2013-08-13T12:48:04.420 回答
5

针对这个特定问题有不同的方法。您可以使用继承,通过它创建一个基础Command,然后实现一些execute功能(您也可以实现helpvalidate....)。然后创建一个调度程序函数,将名称与命令的实际实现相关联(在各种查找表中,可能是 a map)。

虽然这不能解决您的locality问题,但该问题可能是真实的,也可能不是真实的。也就是说,命令的实现可能无处不在,但只有一个地方可以确定 CLI 中可用的命令。

如果局部性对您来说如此重要(代价是源代码中没有一个位置列出所有正在使用的命令),您可以提供一个全局可访问的注册机制,然后提供一个辅助类型,在构造会将函数注册到机制中。然后,您可以为每个函数定义创建一个这样的对象。

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);

我个人更喜欢另一种方式......在代码中列出所有命令的单个位置,因为它允许在单个位置找到执行它的代码的命令(文本)。也就是说,当您有一些命令并且其他人需要维护其中一个时,它可以更容易地从命令行转到执行它的实际代码。

于 2013-08-13T13:19:51.893 回答
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
};
于 2013-08-13T14:37:39.980 回答
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方法。由于方法可以在类之外定义,这个宏将包含所有需要的代码,并使用括号中的代码:

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()

我希望你喜欢它。

于 2013-08-13T15:58:14.727 回答