9

I want to learn more about using function pointers in C structs as a way to emulate objects-oriented programming, but in my search, I've just found questions like this where the answer is simply to use a function pointer without describing how that would work.

My best guess is something like this

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

struct my_struct
{
    int data;
    struct my_struct* (*set_data) (int);
};

struct my_struct* my_struct_set_data(struct my_struct* m, int new_data)
{
    m->data = new_data;
    return m;
}

struct my_struct* my_struct_create() {
    struct my_struct* result = malloc((sizeof(struct my_struct)));
    result->data = 0;
    result->set_data = my_struct_set_data;
    return result;
}

int main(int argc, const char* argv[])
{
    struct my_struct* thing = my_struct_create();
    thing->set_data(1);
    printf("%d\n", thing->data);
    free(thing);
    return 0;
}

But that give me compiler warnings warning: assignment from incompatible pointer type, so obviously I'm doing something wrong. Could someone please provide a small but complete example of how to use a function pointer in a C struct correctly?

My class taught in C does not even mention these. It makes me wonder whether these are actually used by C programmers. What are the advantages and disadvantages of using function pointers in C structs?

Eva
  • 4,397
  • 5
  • 43
  • 65
  • 3
    The compiler warning may be because the argument list in the actual function doesn't match that argument list in the struct var: the actual function has `(struct my_struct *, int)` and the structure definition has only `(int)` – pb2q Jul 14 '12 at 05:56
  • @pb2q Which one should I change? – Eva Jul 14 '12 at 06:04
  • @Eva You should change the function pointer in the struct definition to `struct my_struct* (*set_data) (struct my_struct*, int);` if you want to assign `my_struct_set_data()` to it. – eddieantonio Jul 14 '12 at 06:15

2 Answers2

14

The answer given by Andy Stow Away fixes my compiler warning, but doesn't answer my second question. The comments to that answer given by eddieantonio and Niklas R answer my second question, but don't fix my compiler warning. So I'm pooling them together into one answer.

C is not object-oriented and attempting to emulate object-oriented design in C usually results in bad style. Duplicating methods called on structs so that they can be called using a pointer to the struct as I have in my example is no exception. (And frankly, it violates DRY.) Function pointers in structs are more useful for polymorphism. For example, if I had a struct vector that represented a generic container for a linear sequence of elements, it might be useful to store a comparison_func member that was a function pointer to allow sorting and searching through the vector. Each instance of the vector could use a different comparison function. However, in the case of a function that operates on the struct itself, it is better style to have a single separate function that is not duplicated in the struct.

This makes the answer to what is correct more complicated. Is what is correct how to make my above example compile? Is it how to reformat my above example so that it has good style? Or is it what is an example of a struct that uses a function pointer the way C programmer would do it? In formulating my question, I did not anticipate the answer being that my question was wrong. For completeness, I will provide an example of each answer to the question.

Fixing the Compiler Warning

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

struct my_struct
{
    int data;
    struct my_struct* (*set_data) (struct my_struct*, int);
};

struct my_struct* my_struct_set_data(struct my_struct* m, int new_data)
{
    m->data = new_data;
    return m;
}

struct my_struct* my_struct_create()
{
    struct my_struct* result = malloc((sizeof(struct my_struct)));
    result->data = 0;
    result->set_data = my_struct_set_data;
    return result;
}

int main(int argc, const char* argv[])
{
    struct my_struct* thing = my_struct_create();
    thing->set_data(thing, 1);
    printf("%d\n", thing->data);
    free(thing);
    return 0;
}

Reformatting the Style

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

struct my_struct
{
    int data;
};

void my_struct_set_data(struct my_struct* m, int new_data)
{
    m->data = new_data;
}

struct my_struct* my_struct_create()
{
    struct my_struct* result = malloc((sizeof(struct my_struct)));
    result->data = 0;
    return result;
}

int main(int argc, const char* argv[])
{
    struct my_struct* thing = my_struct_create();
    my_struct_set_data(thing, 1);
    printf("%d\n", thing->data);
    free(thing);
    return 0;
}

Demonstrating a Use for Function Pointer in Structs

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

struct my_struct
{
    void* data;
    int (*compare_func)(const void*, const void*);
};

int my_struct_compare_to_data(struct my_struct* m, const void* comparable)
{
    return m->compare_func(m->data, comparable);
}

struct my_struct* my_struct_create(void* initial_data,
        int (*compare_func)(const void*, const void*))
{
    struct my_struct* result = malloc((sizeof(struct my_struct)));
    result->data = initial_data;
    result->compare_func = compare_func;
    return result;
}

int int_compare(const void* a_pointer, const void* b_pointer)
{
    return *(int*)a_pointer - *(int*) b_pointer;
}

int string_compare(const void* a_pointer, const void* b_pointer)
{
    return strcmp(*(char**)a_pointer, *(char**)b_pointer);
}

int main(int argc, const char* argv[])
{
    int int_data = 42;
    struct my_struct* int_comparator =
            my_struct_create(&int_data, int_compare);

    char* string_data = "Hello world";
    struct my_struct* string_comparator =
             my_struct_create(&string_data, string_compare);

    int int_comparable = 42;
    if (my_struct_compare_to_data(int_comparator, &int_comparable) == 0)
    {
        printf("The two ints are equal.\n");
    }

    char* string_comparable = "Goodbye world";
    if (my_struct_compare_to_data(string_comparator,
            &string_comparable) > 0)
    {
        printf("The first string comes after the second.\n");
    }

    free(int_comparator);
    free(string_comparator);

    return 0;
}
Eva
  • 4,397
  • 5
  • 43
  • 65
11

In your struct definition, change it to

struct my_struct
{
    int data;
    struct my_struct* (*set_data) (struct my_struct*,int);
};

and now use the above function pointer in main as

thing->set_data(thing,1);
Andy Stow Away
  • 649
  • 1
  • 8
  • 17
  • This compiles fine. What are the advantages of this method over just using the my_struct_set_data function? Disadvantages? – Eva Jul 14 '12 at 06:15
  • 2
    @Eva Could you elaborate on your question a bit more? If you mean, what are the advantages of having a struct in C containing function pointers that (presumably) operate on the struct itself, the most useful advantage that I could think of is polymorphism. For example, you'd define several `set_data()` functions, and any struct could choose a specific `set_data()` function that suits it. Then, when you're processing several of your structs, you don't need to choose which set data function the particular struct is going to use; it's already "built-in" into the struct. – eddieantonio Jul 14 '12 at 06:23
  • 3
    @Eva You just can't expect `thing->set_data(value)` behave the same way it would in C++. The function does *not* have any reference to `thing`. You can think about a method call in C++ being converted from `thing->do_stuff()` to `ThingsClass_do_stuff(thing)` or even `thing->_vtable->do_stuff(thing)` if the method was virtual. – Niklas R Jul 14 '12 at 06:24
  • @eddieantonio I guess my real question with the advantages and disadvantages is when to use one over the other? Is it customary to have both a my_struct_set_data method and a set_data method inside my struct or do C programmers usually just use the first and mainly use the second for polymorphism? – Eva Jul 14 '12 at 06:31
  • @Eva Hello, its about correctness. A compiler warning does not necessarily mean your code is alright. You code will not work when you run it. (Do give your previous program a run and see the output). In your case, ` my_struct_set_data` has the first parameter i.e `struct my_struct* m`. but your function pointer definition does not have the same signature. – Andy Stow Away Jul 14 '12 at 06:32
  • @AndyStowAway Sorry I was being unclear. What I meant when I said that "This compiles fine" I was referring to your change. Thanks for the upvote. – Eva Jul 14 '12 at 06:34
  • 1
    @Eva Personally, the thought of using both methods together kind of scares me. I would just write a `my_struct_set_data()` method and leave the struct without any function pointers, but that's just me. So if I'm representative of the typical C programmer, then yes, C programmers usually just use the first and mainly use the second for the express purpose of polymorphism! – eddieantonio Jul 14 '12 at 06:37