Include actor.h from controller.h so the header compiles on its own
controller.h declares three handler pointers taking an akgl_Actor * and included nothing that declares that type, so any translation unit reaching 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, and tests/controller.c carried a comment explaining the workaround. Included rather than forward-declared: akgl_Actor is a typedef of a named struct and repeating a typedef is C11, not C99. It closes no cycle -- actor.h reaches only types.h and character.h. tests/headers.c is a whole suite for one #include on purpose. A second #include after the first proves nothing about the second, because by then the first has dragged its dependencies in; covering another header means another file shaped like this one. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -188,6 +188,7 @@ set(AKGL_TEST_SUITES
|
|||||||
draw
|
draw
|
||||||
error
|
error
|
||||||
game
|
game
|
||||||
|
headers
|
||||||
heap
|
heap
|
||||||
json_helpers
|
json_helpers
|
||||||
physics
|
physics
|
||||||
|
|||||||
@@ -30,6 +30,12 @@
|
|||||||
#include <SDL3/SDL.h>
|
#include <SDL3/SDL.h>
|
||||||
#include <akerror.h>
|
#include <akerror.h>
|
||||||
#include "types.h"
|
#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 <akgl/actor.h>
|
||||||
|
|
||||||
/** @brief How many control maps exist -- effectively the local player limit. */
|
/** @brief How many control maps exist -- effectively the local player limit. */
|
||||||
#define AKGL_MAX_CONTROL_MAPS 8
|
#define AKGL_MAX_CONTROL_MAPS 8
|
||||||
|
|||||||
@@ -13,8 +13,8 @@
|
|||||||
#include <akerror.h>
|
#include <akerror.h>
|
||||||
|
|
||||||
#include <akgl/error.h>
|
#include <akgl/error.h>
|
||||||
// akgl/controller.h uses akgl_Actor without declaring it, so actor.h has to
|
// For the akgl_Actor_cmhf_* handlers these tests bind. akgl/controller.h pulls
|
||||||
// come first, the same way src/controller.c reaches it through akgl/game.h.
|
// actor.h in for itself now; tests/headers.c is what keeps it doing so.
|
||||||
#include <akgl/actor.h>
|
#include <akgl/actor.h>
|
||||||
#include <akgl/character.h>
|
#include <akgl/character.h>
|
||||||
#include <akgl/game.h>
|
#include <akgl/game.h>
|
||||||
|
|||||||
37
tests/headers.c
Normal file
37
tests/headers.c
Normal file
@@ -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 <akgl/controller.h>
|
||||||
|
|
||||||
|
#include <string.h>
|
||||||
|
|
||||||
|
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;
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user