diff --git a/CMakeLists.txt b/CMakeLists.txt index 30c9f6b..4b96c5c 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -188,6 +188,7 @@ set(AKGL_TEST_SUITES draw error game + headers heap json_helpers physics diff --git a/include/akgl/controller.h b/include/akgl/controller.h index bcee29e..c8cbac8 100644 --- a/include/akgl/controller.h +++ b/include/akgl/controller.h @@ -30,6 +30,12 @@ #include #include #include "types.h" +// The binding handlers below take an akgl_Actor *, so this header cannot be +// included before akgl/actor.h without one. akgl_Actor is a typedef of a named +// struct, and repeating a typedef is not C99, so it is included rather than +// forward-declared. actor.h reaches only types.h and character.h, so this does +// not close a cycle. +#include /** @brief How many control maps exist -- effectively the local player limit. */ #define AKGL_MAX_CONTROL_MAPS 8 diff --git a/tests/controller.c b/tests/controller.c index af0fa64..ced878b 100644 --- a/tests/controller.c +++ b/tests/controller.c @@ -13,8 +13,8 @@ #include #include -// akgl/controller.h uses akgl_Actor without declaring it, so actor.h has to -// come first, the same way src/controller.c reaches it through akgl/game.h. +// For the akgl_Actor_cmhf_* handlers these tests bind. akgl/controller.h pulls +// actor.h in for itself now; tests/headers.c is what keeps it doing so. #include #include #include diff --git a/tests/headers.c b/tests/headers.c new file mode 100644 index 0000000..d4e2204 --- /dev/null +++ b/tests/headers.c @@ -0,0 +1,37 @@ +/** + * @file headers.c + * @brief Checks that a public header can be the first thing a caller includes. + * + * The assertion here is the compile, not anything main() does. `akgl/controller.h` + * declares its binding handlers as taking an `akgl_Actor *` and once did not + * declare that type, so every translation unit that reached for it before + * `akgl/actor.h` failed with "unknown type name 'akgl_Actor'". `src/controller.c` + * never noticed, because it includes `akgl/game.h` first. + * + * One header per file, deliberately. A second `#include` after the first proves + * nothing about the second: by then the first has already dragged its + * dependencies in. Covering another header means another file like this one. + */ + +#include + +#include + +int main(void) +{ + akgl_Control control; + + // Enough to require the akgl_Actor declaration to be complete rather than + // merely present: the handler signature names it, and a control map holds + // one by value. + memset(&control, 0x00, sizeof(akgl_Control)); + control.key = SDLK_LEFT; + control.handler_on = &akgl_Actor_cmhf_left_on; + if ( control.handler_on == NULL ) { + return 1; + } + if ( sizeof(GAME_ControlMaps) != (sizeof(akgl_ControlMap) * AKGL_MAX_CONTROL_MAPS) ) { + return 1; + } + return 0; +}