3

How do you create a function pointer with struct table such as

static struct {
  int pid;
  int queue[MAXPROCS];
} semtab[MAXSEMS];

I think I understand how to make OO equivalent in C using function pointer with this post, but how can I do with when my struct is an array. I'm still a little iffy with the syntax.

Would it be something like

static struct {
  int pid;
  int queue[MAXPROCS];

  void (*fncPtr_enqueue)(int) = enqueue;
                 // or is it void(*enqueue)(semtable[]*) ?
  int (*fcnPtr_dequeue)() = dequeue;
} semtab[MAXSEMS];

void enqueue(int i) { /* code */ }
int dequeue() { /* code */ }


// then to use it, it would be like this?
void foo() {
  semtab[5].enqueue(6);
}
Community
  • 1
  • 1
Sugihara
  • 1,091
  • 2
  • 20
  • 35
  • I'm unsure what you are really asking, could you clarify your question ? – Morten Jensen Feb 15 '15 at 22:54
  • You have to declare the functions before you can use them. You can't create initializers in the body of the structure definition. Using Standard C, you can't use a repeat count on initializers, which is a nuisance. GCC provides a non-standard extension to support repeated initializers. – Jonathan Leffler Feb 15 '15 at 22:56
  • 1
    You should use `(void)` for a function taking no arguments; in C an empty argument list does not form a prototype, it means that any number of parameters can match ( and cause undefined behaviour at runtime ) – M.M Feb 15 '15 at 23:02
  • There's no point in doing this in C, unless you plan on assigning different function pointers at some point. Right now you are just creating an extra layer of indirection for no reason. Also note that your enqueue and dequeue functions need to take an extra argument because there is no implicit `self` or `this` argument in C. – JS1 Feb 16 '15 at 00:07

3 Answers3

1

Use

static struct {
  int pid;
  int queue[MAXPROCS];

  void (*fncPtr_enqueue)(int); // This defines a member fncPtr_enqueue
  int (*fncPtr_dequeue)();     // Note that you had fcnPtr_ in your post.
                               // I have fncPtr_ here.
} semtab[MAXSEMS];

void enqueue(int i) { /* code */ }
int dequeue() { /* code */ }

Each object in semtab that needs to have valid function pointers needs to be updated.

semtab[0].fncPtr_enqueue = enqueue;
semtab[0].fncPtr_dequeue = dequeue;
Jonathan Leffler
  • 730,956
  • 141
  • 904
  • 1,278
R Sahu
  • 204,454
  • 14
  • 159
  • 270
  • This is kind of a noob question. Say I want to enqueue(5), queue[tail++] = input. When I call the function, semtab[5].enqueue(5), that queue will be semtab[5]'s queue right? – Sugihara Feb 15 '15 at 23:21
  • 1
    It'd probably be a good idea to give `enqueue()` some idea of exactly *which* queue you want it to use, though. Right now it has no idea. You'll need to explicitly pass it a pointer to `semtab[5]`s `queue`, or a pointer to `semtab[5]` itself. – Crowman Feb 15 '15 at 23:34
  • Yeah, I think I'll pass another parameter and do something like `semtab[s].queue[semtab[s].tail++] = input`. I am not sure why, but the compiler is complaining that it does not know what the variable `queue` and `tail` are. – Sugihara Feb 15 '15 at 23:41
  • @Jack: Well, C isn't C++, so you're not going to be able to directly refer to member variables like `queue` and `pid` from inside your `enqueue()` function. You're always going to have to access them through a pointer. There's an implicit `this` argument in a C++ member function call, but C doesn't have this concept, so you always have to explicitly provide it. – Crowman Feb 15 '15 at 23:43
  • In other words, you'll have to (1) give your struct a name, e.g. `struct queue_struct { ...`; (2) change the signature of `enqueue` to `void enqueue(struct queue_struct * this, int i);` (3) call it like `semtab[5].enqueue(semtab[5], 6);` and (4) in that function, do something like `this->queue[this->tail++] = i;`. – Crowman Feb 15 '15 at 23:50
1

You could use:

static struct
{
    int pid;
    int queue[MAXPROCS];
    void (*enqueue)(int);
    int (*dequeue)(void);
} semtab[MAXSEMS];

void enqueue(int i) { /* code */ }
int dequeue(void) { /* code */ }

void foo(void)
{
    semtab[5].enqueue(6);
}

Changes include:

  1. Systematic names for structure member pointers (instead of mixed fncPtr and fcnPtr prefixes).
  2. No attempt to initialize in the structure definition.
  3. Add void to function prototypes to indicate no arguments. In C (and in contrast to C++), an empty pair of brackets (parentheses) means "a function taking an undefined number of arguments, but not one which has a variable argument list with ... ellipsis".
  4. Because of (1), the original invocation is OK. (With the original code, you'd have needed semtab[5].fncPtr_enqueue(6); — or even (*semtab[5].fncPtr_enqueue)(6);)

You would still have to ensure that the function pointers in the table are all initialized.

With GCC and C99 or C11 compilation, you could initialize the array using:

static struct
{
    int pid;
    int queue[MAXPROCS];
    void (*enqueue)(int);
    int (*dequeue)(void);
} semtab[MAXSEMS] =
{
    [0 ... MAXSEMS-1] = { .enqueue = enqueue, .dequeue = dequeue }
};

The [0 ... MAXSEMS-1] part is a GCC extension. Observe that a space is required after the 0 to avoid problems with the 'maximal munch' rule.

Jonathan Leffler
  • 730,956
  • 141
  • 904
  • 1,278
1

As JS1 mentioned in the comments, it's actually pretty pointless to do this with the example you have, as you're not achieving anything with the indirection if you're not going to vary the value of those pointers.

That being said, here's an example using a stack (because the logic is easier than a queue, and this is a simple example). Note that you must pass a pointer to the stack to each of your member functions, because while C++ member functions have an implicit this argument, C functions never do. You also need to give your struct a name, otherwise you won't be able to refer to it in the abstract, which you need to do.

This program uses the same struct to implement two variations of a stack, one normal one, and one which unnecessarily shouts at you when you push or pop:

#include <stdio.h>
#include <stdlib.h>

enum {
    STACKSIZE = 1024
};

struct stack {
    int stack[STACKSIZE];
    size_t top;
    void (*push)(struct stack *, int);
    int (*pop)(struct stack *);
    void (*destroy)(struct stack *);
};

void stack_push(struct stack * this, int i)
{
    if ( this->top == STACKSIZE ) {
        fprintf(stderr, "Queue full!\n");
        exit(EXIT_FAILURE);
    }

    this->stack[this->top++] = i;
}

void stack_push_verbose(struct stack * this, int i)
{
    stack_push(this, i);
    printf("** PUSHING %d ONTO STACK! **\n", i);
}

int stack_pop(struct stack * this)
{
    if ( this->top == 0 ) {
        fprintf(stderr, "Stack empty!\n");
        exit(EXIT_FAILURE);
    }

    return this->stack[--this->top];
}

int stack_pop_verbose(struct stack * this)
{
    const int n = stack_pop(this);
    printf("** POPPING %d FROM STACK! **\n", n);
    return n;
}

void stack_destroy(struct stack * this)
{
    free(this);
}

struct stack * stack_create(void)
{
    struct stack * new_stack = malloc(sizeof * new_stack);
    if ( !new_stack ) {
        perror("Couldn't allocate memory");
        exit(EXIT_FAILURE);
    }

    new_stack->top = 0;
    new_stack->push = stack_push;
    new_stack->pop = stack_pop;
    new_stack->destroy = stack_destroy;

    return new_stack;
}

struct stack * stack_verbose_create(void)
{
    struct stack * new_stack = stack_create();
    new_stack->push = stack_push_verbose;
    new_stack->pop = stack_pop_verbose;

    return new_stack;
}

int main(void)
{
    struct stack * stack1 = stack_create();
    struct stack * stack2 = stack_verbose_create();

    stack1->push(stack1, 4);
    stack1->push(stack1, 3);
    stack1->push(stack1, 2);

    printf("Popped from stack1: %d\n", stack1->pop(stack1));

    stack2->push(stack2, 5);
    stack2->push(stack2, 6);

    printf("Popped from stack2: %d\n", stack2->pop(stack2));
    printf("Popped from stack1: %d\n", stack1->pop(stack1));
    printf("Popped from stack1: %d\n", stack1->pop(stack1));
    printf("Popped from stack2: %d\n", stack2->pop(stack2));

    stack1->destroy(stack1);
    stack2->destroy(stack2);

    return 0;
}

with output:

paul@horus:~/src/sandbox$ ./stack
Popped from stack1: 2
** PUSHING 5 ONTO STACK! **
** PUSHING 6 ONTO STACK! **
** POPPING 6 FROM STACK! **
Popped from stack2: 6
Popped from stack1: 3
Popped from stack1: 4
** POPPING 5 FROM STACK! **
Popped from stack2: 5
paul@horus:~/src/sandbox$ 

Note that we use the exact same struct stack for both types of stack - the differences between them are implemented by having the function pointers point to different functions in each case. The only visible difference to the user is that one is created with stack_create(), and the other is created with stack_create_verbose(). In all other respects, they're used identically, so you can see the polymorphism at work.

Crowman
  • 25,242
  • 5
  • 48
  • 56