C中的通用/类OO编程,同时避免符号冲突

时间:2010-11-24 11:52:23

标签: c

我在C中做了一个小游戏。我尝试使用函数指针以面向对象的方式编程 我真的很想这次推进而不是过于夸张,我经常迷失在这里。使用普通的旧C对我的编程工作更快更好。

目前,我使用以下方式描述“游戏状态”:

/* macros */

#define SETUP_ROUTINE(component) component##_##setup_routine
#define DRAW_ROUTINE(component) component##_##draw_routine
#define EVENT_ROUTINE(component) component##_##event_routine
#define UPDATE_ROUTINE(component) component##_##update_routine
#define TEARDOWN_ROUTINE(component) component##_##teardown_routine

#define SETUP_ROUTINE_SIGNATURE void
#define DRAW_ROUTINE_SIGNATURE void
#define EVENT_ROUTINE_SIGNATURE SDL_Event evt, int * quit
#define UPDATE_ROUTINE_SIGNATURE double t, float dt
#define TEARDOWN_ROUTINE_SIGNATURE void

/* data */

typedef enum GameStateType {
    GAME_STATE_MENU,
    GAME_STATE_LEVELSELECT,
    ...
} GameStateType;

typedef struct GameState {
    GameStateType state;
    GameStateType nextState;
    GameStateType prevState;

    void (*setup_routine)(SETUP_ROUTINE_SIGNATURE);
    void (*draw_routine)(DRAW_ROUTINE_SIGNATURE);
    void (*event_routine)(EVENT_ROUTINE_SIGNATURE);
    void (*update_routine)(UPDATE_ROUTINE_SIGNATURE);
    void (*teardown_routine)(TEARDOWN_ROUTINE_SIGNATURE);

} GameState;

虽然你可能会或可能不会欣赏这种风格,但我已经逐渐喜欢它了,到目前为止这个小型(私人......)项目对我很有帮助。

例如,我有一个“过渡”游戏状态,只是从一个游戏状态转换到另一个游戏状态。

然而,当我将不同的游戏状态链接在一起时,我会得到丑陋的东西,如:

extern GameState GAME; /* The 'singleton' "game" */

extern void menu_setup_routine(SETUP_ROUTINE_SIGNATURE);
extern void menu_draw_routine(DRAW_ROUTINE_SIGNATURE);
extern void menu_event_routine(EVENT_ROUTINE_SIGNATURE);
extern void menu_update_routine(UPDATE_ROUTINE_SIGNATURE);
extern void menu_teardown_routine(TEARDOWN_ROUTINE_SIGNATURE);

extern void debug_setup_routine(SETUP_ROUTINE_SIGNATURE);
extern void debug_draw_routine(DRAW_ROUTINE_SIGNATURE);
extern void debug_event_routine(EVENT_ROUTINE_SIGNATURE);
extern void debug_update_routine(UPDATE_ROUTINE_SIGNATURE);
extern void debug_teardown_routine(TEARDOWN_ROUTINE_SIGNATURE);

此外,对于每个游戏状态,我都有以下内容:

menu.c

struct MenuModel menu_model; /* The singleton 'menu' model */

game.c

struct GameModel game_model; /* The singleton 'game' model */

..这是在整个程序执行期间保留在堆上的全局数据片段。当然,这些领域通常包括指向动态记忆的指针,当游戏状态发生变化时,这些指针会随着内容的变化而变化 起初我觉得这很疯狂,我开始喜欢它。但是,如果链接另一个也具有“menu_model”符号的.o,则可能会导致名称空间冲突。

第一个问题:这是疯了吗,有没有更好的办法做这样的事情?人们通常做些什么来避免这些可能的符号名称冲突?

第二个问题是我必须在一个包含以下类型函数的源文件/目标文件中使用“extern ..”重新发布不同的... _ setup_routine / .. draw_routine / ..函数:

void (*get_setup_routine(GameStateType state))(SETUP_ROUTINE_SIGNATURE) {
    switch(state) {
        case GAME_STATE_MENU:
            return SETUP_ROUTINE(menu);
            break;
        case GAME_STATE_LEVELSELECT:
            return SETUP_ROUTINE(level_select);
            break;
        default: /* ... */ break;
    } 
}

因为否则在编译时不知道符号“menu_setup_routine”。

无论如何,任何建议都是受欢迎的,我对C有点新,虽然我真的很喜欢编程,但我想知道我是否正确使用它。

2 个答案:

答案 0 :(得分:2)

一些非小游戏使用类似的范例。我想到的第一个例子是Neverball 你可能想下载它的源代码(它是一个OpenSource游戏)并看看它们是如何做的。

我个人认为你应该检查C ++。我曾经只使用过C,就像你在做的那样,直到几年前;然后我发疯了(主要是因为名字冲突),转而使用C ++让我发现了一个新的世界。无论如何,我理解你可能出于多种原因想要避免它。


关于与您的menu_model类似的对象,其名称与其他C源文件中的其他menu_model冲突,您应该将它们声明为static

static struct MenuModel menu_model; /* The singleton 'menu' model */

menu_model将在其声明的C源文件中可见(您将无法在其他C源文件中使用它,甚至不能在extern中使用它),并且name不会与其他C static变量冲突,并且在其他C源文件中声明的名称相同。


关于第二个问题,没什么可做的。必须声明您使用的函数和变量。

答案 1 :(得分:2)

我有点困惑,但我认为你不应该需要所有那些menu_setup_routine等等来进行外部联系。相反,为每个例程定义一个包含一个函数指针的struct game_vtable,然后让“menu”和“debug”中的每一个提供对该结构的实例的访问。要调用组件上的函数,可以执行以下操作:

// vtable is a global symbol
component##_##vtable.setup

// vtable is acquired from a function
component##_##getvtableptr()->setup

或者您可以将vtable指针作为参数传递,代替您的GameStateType,从而可以删除一些switch语句。

至于全局变量 - 你没有提供很多细节,但避免全局菜单的方法是在本地创建一个高级别,然后将其传递给任何需要它的人。如果您认为自己更喜欢全局,那么如果它在TU之外可见,则必须给它一个唯一的名称。