1

In an implementation of the Game of Life, I need to handle user events, perform some regular (as in periodic) processing and draw to a 2D canvas. The details are not particularly important. Suffice it to say that I need to keep track of a large(-ish) number of variables. These are things like: a structure representing the state of the system (live cells), pointers to structures provided by the graphics library, current zoom level, coordinates of the origin and I am sure a few others.

In the main function, there is a game loop like this:

// Setup stuff
while (!finished) {
    while (get_event(&e) != 0) {
        if (e.type == KEYBOARD_EVENT) {
            switch (e.key.keysym) {
            case q:
            case x:
            // More branching and nesting follows

The maximum level of nesting at the moment is 5. It quickly becomes unmanageable and difficult to read, especially on a small screen. The solution then is to split this up into multiple functions. Something like:

while (!finished {
    while (get_event(&e) !=0) {
        handle_event(state, origin_x, origin_y, &canvas, e...) //More parameters

This is the crux of the question. The subroutine must necessarily have access to the state (represented by the origin, the canvas, the live cells etc.) in order to function. Passing them all explicitly is error prone (which order does the subroutine expect them in) and can also be difficult to read. Aside from that, having functions with potentially 10+ arguments strikes me as a symptom of other design flaws. However the alternatives that I can think of, don't seem any better.

To summarise:

  1. Accept deep nesting in the game loop.
  2. Define functions with very many arguments.
  3. Collate (somewhat) related arguments into structs - This really only hides the problem, especially since the arguments are only loosely related.
  4. Define variables that represent the application state with file scope (static int origin_x; for example). If it weren't for the fact that it has been drummed into me that global variable are usually a terrible idea, this would be my preferred option. But if I want to display two views of the same instance of the Game of Life in the future, then the file scope no longer looks so appealing.

The question also applies in slightly more general terms I suppose: How do you pass state around a complicated program safely and in a readable way?

EDIT: My motivations here are not speed or efficiency or performance or anything like this. If the code takes 20% longer to run as a result of the choice made here that's just fine. I'm primarily interested in what is less likely to confuse me and cause the least headache in 6 months time.

diestl
  • 2,020
  • 4
  • 23
  • 37
  • 2
    Structs is the way to go. – Marco Apr 04 '17 at 15:10
  • 2
    "Safely" is relative; globals are safe so long as you can ensure no one else accesses them. Note: I am not endorsing the use of globals, but to better answer the question it would be good to know what your goals are (speed, low coupling, etc). – FluxIX Apr 04 '17 at 15:12
  • 4
    Try to create abstractions, and group *related* data in structures. And (for example) instead of having a single function handling your whole `switch` event handling, keep the `switch` in the main event loop and use functions for the different events, passing only the variables it needs. Or instead of breaking out the *only* the `switch` into a function, take out the whole *loop*? – Some programmer dude Apr 04 '17 at 15:13
  • @FluxIX See my edit. My goal is not speed or performance, instead I don't want to confuse myself or anyone else in the future. – diestl Apr 04 '17 at 15:22
  • 1
    "*I don't want to confuse myself or anyone else in the future.*" then you definitely don't want to go for globals (at least as long they aren't constant)! – alk Apr 04 '17 at 15:26
  • @alk Yes. Like I said, it was tempting to go for globals here, but it just feels _wrong_. – diestl Apr 04 '17 at 15:49
  • 2
    File scope (`static`) variables aren't necessarily strictly global any more than private member variables (fields, properties, whatever they are called) in a static class are. Don't let dogma - like globals are evil - put you off the most effective code; sometimes the wrong thing (more than one return, goto, global variables, etc) is the right thing for that circumstance. – Evil Dog Pie Apr 04 '17 at 16:05
  • Ok so the consensus seems to be: Collate related data into structs, tolerate many arguments to some degree and don't be afraid of globals (or file scope variables) when they are appropriate. Does anyone want to post an answer to that effect? – diestl Apr 07 '17 at 09:40
  • Also: It was helpful to group flags into a single `char` (or other integer type). So rather than passing a handful of pointers to boolean values into a function, I can just pass one. – diestl Apr 07 '17 at 09:41

1 Answers1

0

I would consider the canvas as one variable, containing a large 2D array... consider static allocation

bool canvas[ROWS][COLS];

or dynamic

bool *canvas = malloc(N*M*sizeof(int));

In both cases you can refer to the cell at position i,j as canvas[i][j] though for dynamic allocation, do not forget to free(canvas) at the end. You can then use a nested loop to update your state.

Search for allocating/handling a 2d array in C and examples or tutorials... Possibly check something like this or similar? Possibly this? https://www.geeksforgeeks.org/nested-loops-in-c-with-examples/

Also consider this Fastest way to zero out a 2d array in C?

ntg
  • 12,950
  • 7
  • 74
  • 95