c - 在 C 中进行通用/类 OO 编程,同时避免符号冲突

标签 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;

虽然您可能会或可能不喜欢这种风格,但我已经开始喜欢它,并且到目前为止,它在这个小型(私有(private)..)项目中对我很有帮助。

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

但是,当我将不同的游戏状态链接在一起时,我会得到一些难看的东西,例如:

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

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

菜单.c

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

游戏.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 有点陌生,虽然我真的很喜欢用它进行编程,但我想知道在这种情况下我是否正确使用了它。

最佳答案

一些非小型游戏使用类似的范式。我脑海中浮现的第一个例子是 Neverball .
您可能想下载它的源代码(它是一个开源游戏)并看看他们是如何做的。

我个人认为你应该检查C++。直到几年前,我以前只使用 C,也像你一样使用 C;然后我就发疯了(主要是因为名字冲突),转向 C++ 让我发现了一个新世界。无论如何,我知道您可能出于多种原因想要避免它。


关于像您的 menu_model 这样的 objecst,其名称与其他 C 源文件中的其他 menu_model 冲突,您应该将它们声明为 static:

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

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


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

关于c - 在 C 中进行通用/类 OO 编程,同时避免符号冲突,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/4266414/

相关文章:

c - 为什么 fread() 的第一个参数被强制转换为其他类型

c - 这样用好不好?

c - 为什么这段代码是可利用的?

将空格转换为制表符

c++ - 应用程序中两个不同版本的库

c - 如何查看管道是否为空

c - 循环和函数 : How do I calculate how many years it takes to accumulate a given amount of money to retire?

c - 从 PROGMEM 中查找 C 中十六进制字符数组的大小

c - 如何更高效地计算组合数?

c - 结构中的位是否有保证