Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to use a function pointer in a C struct?

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?

like image 623
Eva Avatar asked Jul 14 '12 05:07

Eva


2 Answers

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;
}
like image 190
Eva Avatar answered Sep 30 '22 01:09

Eva


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);
like image 24
Andy Stow Away Avatar answered Sep 30 '22 00:09

Andy Stow Away