Initialize Static Array of Structs in C

72,231

Solution 1

Your approach is exactly right.

  1. This will work, and is a good way to avoid huge switch statements.
  2. You can't define functions inline in C, they each must have a unique name.
  3. extern is what you want, not static. Change your body to be this:

    struct CARD cardDefinitions[] = { 
        {0, 1, do_card0}, 
        {1, 3, do_card1} 
    }; 
    

    then in an appropriate header file:

    extern struct CARD cardDefinitions[];
    

Solution 2

Your approach is right and will work. Your function pointer syntax is right, except that you don't use parameter names - just types:

int (*do_actions)(struct GAME_STATE *, int, int);

Solution 3

  1. That should work fine. It seems like you'd have a lot of functions if you're doing one per card, but maybe this particular game requires that level of control

  2. You can't define them inline, but you can just do a forward declaration. You need to do &func_name in the struct initialization though

  3. No; extern means a variable is declared in another file, so it doesn't make sense to have an extern variable that you're declaring at that location. Also, static means the variable is only accessible from the current file, which is the opposite of what you want. Making it read-only would require a getter function, but if you just want to make it accessible from another file declare it normally here (struct cardDefinitions[] = {...}) and in the other file use an extern (extern struct cardDefinitions[];)

Share:
72,231
russell_h
Author by

russell_h

Updated on August 06, 2020

Comments

  • russell_h
    russell_h almost 4 years

    I'm implementing a card game in C. There are lots of types of cards and each has a bunch of information, including some actions that will need to be individually scripted associated with it.

    Given a struct like this (and I'm not certain I have the syntax right for the function pointer)

    struct CARD {
        int value;
        int cost;
        // This is a pointer to a function that carries out actions unique
        // to this card
        int (*do_actions) (struct GAME_STATE *state, int choice1, int choice2);
    };
    

    I would like to initialize a static array of these, one for each card. I'm guessing this would look something like this

    int do_card0(struct GAME_STATE *state, int choice1, int choice2)
    {
        // Operate on state here
    }
    
    int do_card1(struct GAME_STATE *state, int choice1, int choice2)
    {
        // Operate on state here
    }
    
    extern static struct cardDefinitions[] = {
        {0, 1, do_card0},
        {1, 3, do_card1}
    };
    
    1. Will this work, and am I going about this the right way at all? I'm trying to avoid huge numbers of switch statements.

    2. Do I need to define the 'do_cardN' functions ahead of time, or is there some way to define them inline in the initialization of the struct (something like a lambda function in python)?

    3. I'll need read-only access to cardDefinitions from a different file - is 'extern static' correct for that?

    I know this is a lot of questions rolled into one but I'm really a bit vague about how to go about this.

    Thanks.

    Edit:

    To be clear, my goal is to be able to do something like

    int cost = cardDefinitions[cardNumber].cost;
    

    or

    int result = cardDefinitions[cardNumber].do_action(state, choice1, choice2);
    

    Instead of using huge switch statements all over the place.

  • caf
    caf about 14 years
    You don't need to do &func_name. Either func_name or &func_name is fine (a bare function name is converted to a pointer to the function, analagous to the way a bare array name is converted to a pointer to the array's first element).
  • russell_h
    russell_h about 14 years
    1. Yeah, the game ("Dominion") is pretty ridiculous like that 2. Thanks for clarifying the extern bit, that makes a lot more sense. My approach was just throw keywords at it until it worked...
  • Michael Mrozek
    Michael Mrozek about 14 years
    Oh wow, I actually started an implementation of that game a while ago, but decided it probably wouldn't be nearly as much fun as IRL. You probably don't need separate functions for every card, you can just define properties for how much money a card gives you, how many cards it makes you draw, how many buys and actions you get, and how many VPs it's worth, and that will cover most of the cases
  • Michael Mrozek
    Michael Mrozek about 14 years
    @caf Interesting. There's some case where the & is required, because I've run into it before and got into the habit of always including it, but you're right, it's apparently not required all the time
  • Philip
    Philip about 14 years
    You can actually have "inline" functions in C. The word you're looking for is "anonymous functions". Furthermore, I'd add {0, 0, NULL} as the array's last element so you don't need to store its size separately.
  • Michael Mrozek
    Michael Mrozek about 14 years
    @Philip Well, those "inlines" are entirely unrelated to what he's talking about, but yes, you're right. And I prefer the "sizeof(cardDefinitions)/sizeof(cardDefinitions[0])" trick for cases like this
  • Philip
    Philip about 14 years
    @Michael: This trick doesn't work if you declare "extern struct CARD cardDefinitions[];".
  • caf
    caf about 14 years
    The only case where the & is required is if you're using it as the operand of the sizeof operator (the function designator does not decay to a function pointer in that case, and using sizeof on a function designator isn't allowed).
  • russell_h
    russell_h about 14 years
    @Michael this is actually a class assignment (implement a specified subset of all three Dominion games), so in all probability you know more about it than I do. Like you say, every card has certain common parameters (draw x cards, etc). But nearly every "Kingdom Card" seems to describe some action like "Trash this card. Gain a card costing up to 5" - Looking over the list of cards I can't find a good way to generalize these, at least not given the API I have to implement.