简体   繁体   中英

c struct and function pointers

I am trying to create some c modules that has functions to internal functions without using c++, that I can then update and add features to while keeping things relatively clean and modular coding wise.

Let's say for example I have a simple math module that i'd like to continue to add functions to.

math.h

struct math;
struct ops;

struct math
{
    int id;
    struct ops* m_ops;
};

struct ops
{
    int (* add)(int* a, int* b);
    int (* sub)(int* a, int* b);    
};

with this setup if I continue to add functions to the struct ops; this will keep the size of my original math struct the same and can also maintain binary compatibility and things like that. I don't plan on having multiple implementation of these functions.

so with this simple setup, how can I init these functions? Currently I am doing something like this which seems a bit tedious, is there a nicer way to get it done. Maybe a language like c++ hides all this but I am looking for best practices without using c++ as I would like to know what's really going on.

int add(int *a, int*b)
{
    return ((*a) + (*b));
}

int sub(int *a, int*b)
{
    return ((*a) - (*b));
}

main.c

    struct test* t_test = (struct test*)malloc(sizeof(struct test));
    math->id = 3;
    math->m_ops = (struct m_ops*)malloc(sizeof(struct m_ops));
    math->m_ops->add = &add;
    math->m_ops->sub = ⊂

    int a = 10;
    int b = 20;
    printf("adding: %d\n",math->m_ops->add(&a, &b));

would the malloc of both structs be put in some init function in math.h or something like that? I am not sure, I've been looking for examples of code that does this but either I don't know where to look because I just can't find any.

The answer to your literal question is: provide a “constructor” function to initialize the struct (s), ie, instead of allocating struct math themselves, the user of your module would do something like:

 struct math *math = newMath();

The function would do the necessary setup before returning the allocated struct to the user.

However, I don't think your approach makes any sense. Your functions do not take either the containing struct math or struct ops as argument, so they should be just regular functions (ie, just put int sub(int *a, int*b); in the header directly and the user of your module can call sub(&i, &j) ). And if you wish to make “private” functions, make them static and do not introduce them in the header.

Or if you wish to simulate a C++-style class with struct s, make the first argument of each function a pointer to the related struct math (of course the function should then make use of the state of the struct for this to make sense). You still don't need function pointers for this, though.

Having function pointers in the struct would only be useful different struct math instances in the same program need to have a different implementation for, say, sub , and those instances would need to be interchangeable. That is, if math1->ops->sub and math2->ops->sub would point to a different function, and somewhere in the code you would need to call an unknown someMath->ops->sub without knowing which type it is, then it might be justified to have the pointers. In all other scenarios you should ditch struct ops .

You're not really trying to make a class, but a module. Your id isn't actually in use anywhere. This sort of thing is done from time to time in C, but it's not a very common pattern. It also inhibits a lot of possible optimizations but here it is:

static int add_impl(int *a, int *b) {
    return *a + *b;
}

static int sub_impl(int *a, int *b) {
    return *a + *b;
}

static struct {
    int (*add)(int*, int*); // pointer to the add function
    int (*sub)(int*, int*);
} math_ops = {
    add_impl, // .add = add_impl
    sub_impl
};

and then called with

int x = 2;
int y = 1;
math_ops.add(&x, &y);

If you want to expose the minimum amount in a header ( recommended ), you can separate it as follows:

mymath.h

struct math_ops_tag {
    int (*add)(int*, int*);
    int (*sub)(int*, int*);
};

extern struct math_ops_tag math_ops;

mymath.c

#include "mymath.h"

static int add_impl(int *a, int *b) {
    return *a + *b;
}

static int sub_impl(int *a, int *b) {
    return *a + *b;
}

struct math_ops_tag math_ops = {
    add_impl,
    sub_impl
};

If you can use C99 then I'd recommend instead defining math_ops as

struct math_ops_tag math_ops = {
    .add = add_impl,
    .sub = sub_impl
};

to avoid mispositioning anything

Don't use struct s for grouping functions to make things modular. Use symbol prefixes (C's poor man's namespaces) to group functions.

(Unless your goal is runtime polymorphism like C++ s virtual methods. Then your on the right track-- m_ops is your vtable , and like C++ 's vtables , you should initialize it statically, ie: without malloc )

It seems like you're sort of trying to emulate C++ while "knowing what's really going on"..

What's really going on in C++'s classes is that although C++ speaks of "member" functions, there's no functions pointers and function tables involved at all, unless your class has virtual functions.

At the object file level C++ methods are just free-standing functions made out of methods by adding an implicit instance pointer argument, prefixing the method name with the class's name and mangling the result.


Also, don't pass ints by pointers. Pass them by values. int s tend to take less or as much space as pointers. Passing ints by pointers makes things less efficient because you pass more data and have worse (or no) locality.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM