首页
学习
活动
专区
圈层
工具
发布
首页
学习
活动
专区
圈层
工具
MCP广场
社区首页 >问答首页 >在C中进行泛型/类OO编程,同时避免符号冲突

在C中进行泛型/类OO编程,同时避免符号冲突
EN

Stack Overflow用户
提问于 2010-11-24 19:52:23
回答 2查看 284关注 0票数 2

我正在用C语言做一个小游戏,我试着用函数指针以面向对象的方式编程。

这一次我真的很想继续前进,不要把事情搞得太笼统,我经常迷失在这一点上。使用普通的老式C语言帮助我更快更好地编程。

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

代码语言:javascript
运行
复制
/* 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;

虽然你可能欣赏或不欣赏这种风格,但我已经喜欢上了它,到目前为止,它在这个小的(私人的..)项目。

例如,我有一个简单地从一个游戏状态转换到另一个游戏状态的“转换”游戏状态。

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

代码语言:javascript
运行
复制
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

代码语言:javascript
运行
复制
struct MenuModel menu_model; /* The singleton 'menu' model */

game.c

代码语言:javascript
运行
复制
struct GameModel game_model; /* The singleton 'game' model */

..which是在整个程序执行过程中保留在堆上的全局数据片段。当然,这些字段通常包含指向动态内存的指针,这些动态内存以及哪些内容会随着游戏状态的变化而变化。

虽然一开始我觉得这很疯狂,但我开始喜欢它了。但是,当链接的另一个.o也具有这样的"menu_model“符号时,可能会导致名称空间冲突。

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

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

代码语言:javascript
运行
复制
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语言还有点陌生,虽然我真的很喜欢用它编程,但我想知道在这种情况下我是否正确地使用了它。

EN

回答 2

Stack Overflow用户

回答已采纳

发布于 2010-11-24 20:10:52

一些非小型游戏使用类似的范例。我脑海中浮现的第一个例子是Neverball

你可能想下载它的源代码(这是一个OpenSource游戏),看看他们是怎么做的。

就个人而言,我认为你应该检查一下C++。直到几年前,我一直只使用C,就像你现在做的那样;然后我发疯了(主要是因为名称冲突),切换到C++让我发现了一个新的世界。不管怎样,我知道你可能会因为一些原因而想要避免它。

对于像您的menu_model这样的对象,其名称与其他C源文件中的其他menu_model冲突,您只需将其声明为static

代码语言:javascript
运行
复制
static struct MenuModel menu_model; /* The singleton 'menu' model */

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

关于第二个问题,没有太多的事情要做。您使用的函数和变量必须声明。

票数 2
EN

Stack Overflow用户

发布于 2010-11-24 20:11:33

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

代码语言:javascript
运行
复制
// vtable is a global symbol
component##_##vtable.setup

代码语言:javascript
运行
复制
// vtable is acquired from a function
component##_##getvtableptr()->setup

或者,您可以将vtable指针作为参数传递,而不是GameStateType,这样就可以摆脱一些switch语句。

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

票数 2
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/4266414

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档