Coming from OO languages to C, how can I avoid circular dependencies?

BassguitarBill

When I say "Entity" below, I'm not specifically referring to anything relating to the ECS pattern, I just mean a general game entity.

I'm trying my hand at game development in C, after having done previous game dev in TypeScript. I'm looking for a C idiomatic way to reuse a pattern with which I'm familiar: each tick, the game iterates through a list of entities, telling each one to update itself, then draw itself. Each entity knows how to update itself, but requires information about the game as a whole to make this update.

// Game.ts
import Entity from './Entity.js'
class Game {
  entities: List<Entity>;

  tick(dt: number) {
    entities.forEach(e => e.tick(dt));
    entities.forEach(e => e.draw());
  }
}

// Entity.ts
import Game from './Game.ts'
class Entity {
  game: Game;

  constructor(g: Game) {
    this.game = g;
  }

  tick(dt: number) {
    this.move(dt);
    this.collide(this.game);
  }
  draw() { /* snip */}
}

In C, I would like to have a big Game struct that has a list of all Entities inside it, and each Entity contains a function pointer for how to update itself.

// game.h
#include "entity.h"
typedef struct Game {
  Entity *entities;
} Game;

// entity.h
#include "game.h"
typedef struct Entity Entity;

typedef void (*tick) (Entity*);

struct Entity {
  Game *game;
  char* name;
  int x, y;
  tick* t;
};

This, however, requires a circular reference to Game in Entity and Entity in Game, which I've gathered that one is not supposed to do. The only way I've thought of to make this work is to put a tick_entity(Game *game, Entity *e) function in game.h, but my OO brain wants to separate my concerns some more to avoid making Game responsible for everything, especially once I have different kinds of Entities. Is there a more idiomatic way to do what I'm trying to do here?

Ted Lyngmo

Don't #include "entity.h" from game.h and vice versa. Just forward declare what you need a pointer to. Also add header guards if you haven't already.

Example:

// game.h
#ifndef GAME_H                 // header guard
#define GAME_H

//#include "entity.h"          // remove this
typedef struct Entity Entity;  // forward declare

typedef struct Game {
    Entity* entities;
} Game;

#endif
// entity.h
#ifndef ENTITY_H               // header guard
#define ENTITY_H

//#include "game.h"            // remove this
typedef struct Game Game;      // forward declare
typedef struct Entity Entity;

typedef void (*tick)(Entity*);

struct Entity {
    Game* game;
    char* name;
    int x, y;
    tick* t;
};

#endif

Collected from the Internet

Please contact [email protected] to delete if infringement.

edited at
0

Comments

0 comments
Login to comment

Related

How does the C preprocessor handle circular dependencies?

Can I avoid circular imports in Flask and SQLAlchemy

OO design and circular dependencies

How can I avoid a circular reference situation

How can i avoid circular references in type aliases

How can i display images in React coming from api

How to resolve circular struct dependencies in C

How can I write lines coming from two lists?

How can I figure out where this tracking stuff is coming from?

How can i use a angular variable coming from controller in html?

How can I avoid circular logic when synchronizing Java Threads?

How can I return two data coming from different pages?

How can I type an Object coming from a .forEach function?

How can I pass the value coming from a jQuery variable to PHP?

How can I update my languages from the terminal?

How can I avoid dependencies between methods?

How do circular dependencies occur in C++?

how can i check where the google font request is coming from?

How can i count the number coming in excel

swift how can I check for NULL values coming from Json

How can I detect line breaks in string coming from state?

Client-side typescript: how do I remove circular dependencies from the factory method pattern?

How can I avoid circular reference errors when serializing EF Core results?

How to avoid circular dependencies in a GUI application with fyne?

How can I avoid circular dependency in my Makefile compiling obj file with g++ and gcc?

How to avoid circular dependencies in Javascript / Typescript?

How can I avoid memory leaks coming from std::list and std::vector

How Can I Avoid Sql Circular Reference In This Instance?

How can I resolve circular dependencies with FastAPI and SQLModel?