Home > OS >  Is there a way to point to a struct member from a function pointer?
Is there a way to point to a struct member from a function pointer?

Time:11-06

I'm doing a double linked list from scratch in C and was programming the iter(able) function. However my struct has a bunch of fields and I don't necessarily want to mess with all when I call the function. I want to choose what member to alter in the function call.

typedef struct s_command 
{
    int         argc;               
    char        *argv[MAXARGS];     
    t_token     *args;               
    char        **envp;             
    t_builtin   builtin;            
    void        *input;             
    void        *output;
    struct s_command        *next;
    struct s_command        *prev;
}           t_command;

My obvious choice was having an int argument that gets caught by an if else (can't use switch) to pick what field I want. As such:

void    dll_iter(t_command *lst, int property, void (*f)(void *))
{
    if (!lst || !property || !f)
        return ;
    while (lst)
    {
        if(property == 1)
            f(lst->argc);
        else if(property == 2)
            f(lst->argv);
        else if(property == 3)
            f(lst->args);
        ...
        lst = lst->next;
    }
}

But I can't stop but wonder if C has any way to simplify this. Make it cleaner. What I would really like was someting like:

void    dll_iter(t_command *lst, void (*f)(void *))

where f would call directly the member it wants.

Is there any way to achieve this?

CodePudding user response:

I don't know how this will go over with a school, because this relies on border-line language-lawyering. But if you want a generic iterating function, just abstract away the only thing that matters to it, the links.

struct link {
    struct link *next;
    struct link *prev;
};

struct command {
    struct link link;
    // other members...
};

And now you can write

dll_iter(struct link*, void (*f)(struct link*));

Because a pointer to structure shares an address with it's first member, f can convert internally to the concrete node type it cares about. Meanwhile, the iteration function only deals with (and knows of) the members it needs to implement iteration.

Just note the calling the function is a little different now

void access_fn(struct link* link_p) {
    struct command *cmd = (struct command*)link_p;
    // do stuff
}

// ...

dll_iter(&cmd->link, access_fn);

Heck, now the function can even access more than one member at a time. How's that for flexibility?

CodePudding user response:

It can be done. Time for stacking function pointers. We're getting really close to higher order functions now.

void dll_iter(t_command *lst, void *(*decoder)(t_command *entry), void (*f)(void *argpointer))
{
    if (!lst || !decoder || !f)
        return ;
    while (lst)
    {
        f(decoder(lst));
        lst = lst->next;
    }
}

void *decoder_argc(t_command *entry) { return &entry->argc; }
void *decoder_argv(t_command *entry) { return &entry->argv; }
//...

Note that f always receives a pointer to the struct member.

Invocation looks like:

   dll_iter(list, decoder_argc, f); // Process argc for all entries in list
   dll_iter(list, decoder_argv, f); // ditto for argv
  • Related