Skip to main content
deleted 1 character in body
Source Link
G. Sliepen
  • 69.5k
  • 3
  • 75
  • 180
voidint SceneFSM__Start(Scene *_scenes, int _numScenes, void (*_onEnd)(void)) {
    ...
    return SceneFSM__End(SceneFSM__Run(&SceneFSM, &app));
}
...
int main(int argc, const char *argv[]) {
    ...
    ANSI__Start();
    int result = SceneFSM__Start(scenes, 3, end);
    ANSI__End();

    printf("Program completed %s.\n", result == 0 ? "sucessfully" : "with errors");
    return result;
}
void SceneFSM__Start(Scene *_scenes, int _numScenes, void (*_onEnd)(void)) {
    ...
    return SceneFSM__End(SceneFSM__Run(&SceneFSM, &app));
}
...
int main(int argc, const char *argv[]) {
    ...
    ANSI__Start();
    int result = SceneFSM__Start(scenes, 3, end);
    ANSI__End();

    printf("Program completed %s.\n", result == 0 ? "sucessfully" : "with errors");
    return result;
}
int SceneFSM__Start(Scene *_scenes, int _numScenes, void (*_onEnd)(void)) {
    ...
    return SceneFSM__End(SceneFSM__Run(&SceneFSM, &app));
}
...
int main(int argc, const char *argv[]) {
    ...
    ANSI__Start();
    int result = SceneFSM__Start(scenes, 3, end);
    ANSI__End();

    printf("Program completed %s.\n", result == 0 ? "sucessfully" : "with errors");
    return result;
}
Source Link
G. Sliepen
  • 69.5k
  • 3
  • 75
  • 180

Stack efficiency

Is the SceneFSM__Run method stack efficient?

There is no recursion, so stack usage is bounded. The only way to make it more efficient is to have less on the stack in each function, but since you don't have much to begin with I would not worry about this.

How to switch scenes

In order to switch to another scene, the currently running method should return NEXT but before doing so must set the struct member SceneFSM->nextSceneName so that the machine actually knows where to go next.

Instead of having CONTINUE, NEXT and EXIT codes, you could consider to instead have the functions return a pointer to the (name of the) scene that should run next, and use NULL to indicate that it should exit instead. This avoids having to pass a pointer to the SceneFSM to the methods of each scene. If you also want to distinguish between normal exits and errors, then you could either define a struct that contains both a response code and the name of the scene to switch to, and return that from the member functions, or you could define a special error scene.

You can also consider to have the End method return something different from SceneResponse or the abovementioned pointer, since the only thing it should return is whether it ran succesfully or encountered an error, any other value doesn't make sense.

Another idea would be to not have Start, Update and End methods for each scene, but instead have only one method per scene, and then just create more scenes, for example:

typedef struct {
    const char *name;
    SceneResponse (*Run)(SceneFSM *_SceneFSM, App *_app);
} Scene;
...
Scene scenes[] = {
    {
        .name = "Boot__Start",
        .Run = Boot__Start,
    },
    {
        .name = "Boot__End",
        .Run == Boot__End,
    },
    ...
};

And then have Boot__Start set nextSceneName to Boot__End, and have Boot__End set nextSceneName to Title__Start, and so on. This simplifies the scene manager, but will actually make it more flexible.

Avoid manually calling atexit() and exit()

There sometimes might be reasons to install cleanup handlers with atexit(), but in this case it is completely avoidable. Instead of calling exit() inside SceneFSM__End(), why not have SceneFSM__Start() return the result value, and then main() can continue doing cleanup after the scene manager finished? Like so:

void SceneFSM__Start(Scene *_scenes, int _numScenes, void (*_onEnd)(void)) {
    ...
    return SceneFSM__End(SceneFSM__Run(&SceneFSM, &app));
}
...
int main(int argc, const char *argv[]) {
    ...
    ANSI__Start();
    int result = SceneFSM__Start(scenes, 3, end);
    ANSI__End();

    printf("Program completed %s.\n", result == 0 ? "sucessfully" : "with errors");
    return result;
}

Use const where appropriate

There are a few places where you should make variables const. First, once you have defined your scenes, you don't want to change their names. So make name const:

typedef struct {
    const char *name;
    ...
} Scene;

Similarly, in struct SceneFSM, you can make scenes and numScenes const as well.

The function SceneFSM__SceneIndexByName() doesn't modify the data pointed to by the _SceneFSM member variable, so than can be made const as well:

static int SceneFSM__SceneIndexByName(const SceneFSM *_SceneFSM, char *_name) {
    ...
}

Making things const prevents accidental writes, and allows the compiler to generate better optimized code.