3

I only started learning C 2 days ago, so sorry if this is kind of a dumb question, but I wanted to try and store a function that I could call with default arguments.

At the moment, I have a struct with 2 values. One is storing a pointer, and one is storing a function pointer named print.

struct List{
    int **ptr;
    int (*print)(int**);
};

At the moment, I am creating the struct with the following code:

struct List list;
list.ptr = ptr;
list.print = (&printList);

where printList is a function that takes a pointer as a parameter, and prints a list of values.

Currently the code works when I call the function with the paramater

list.print(list.ptr);

however, I want to be able to call it like this:

list.print();

Is this possible? If so, could you give me some ideas on how to implement it?

Thanks!

LavaTheif
  • 118
  • 3
  • 7
  • Would something like `list_print(&list)` be acceptable? – KamilCuk Feb 11 '20 at 22:03
  • 1
    "I want to be able to call it like this: list.print();" and what behaviour do you want for that? – Yunnosch Feb 11 '20 at 22:06
  • 4
    Someone did that actually. It is called C++! :D – bca Feb 11 '20 at 22:10
  • @yunnosch I want to know if its possible to have the same behaviour as `list.print(list.ptr);`, ie when I call the function stored in the struct, it will automatically call it with the pointer stored in the same struct. – LavaTheif Feb 11 '20 at 22:10
  • @LavaTheif jokes aside I can think two ways to achieve that. One is lexical closure in functional languages, other is `this` pointer in OO languages. C doesn't have either so AFAIK it is not possible. – bca Feb 11 '20 at 22:12
  • 1
    Not sure if your intent is to emulate C++ with pure C, but what you're describing is similar to calling a method of a C++ class. Note that when calling a C++ method, the C++ compiler provides a pointer to the object for use by the method. That doesn't happen with a C compiler, and there's no way to emulate that behavior transparently. What you can do is pass the pointer to the struct as the first argument to any function declared in the struct. – user3386109 Feb 11 '20 at 22:14
  • This can be achieved with a factory function, but it would be very inefficient in both space and time. – jwdonahue Feb 11 '20 at 22:29
  • Binding an argument to a function is called *currying*. Searching on that will get you more information. C does not support it, although various kludges are possible. – Eric Postpischil Feb 11 '20 at 22:31

3 Answers3

4

As is shown in Function pointer as a member of a C struct : You can change the function so that it takes the struct as argument instead of an integer:

struct List{
    int **ptr;
    int (*print)(List*);
};


void printList(List* list);

struct List list;
list.ptr = ptr;
list.print = (&printList);

list.print(&list);

Its not quite what you wanted, but its the only way I can think of in C.

When you absolutely need it the other way, I suggest using C++ and create a class instead.

Raphael
  • 810
  • 6
  • 18
3

C is not an object language. Because of that, it does not pass the hidden this pointer that you can find in Java or C++, and the programmer has to pass it explicitely.

Said differently when in C++ you would have:

struct foo {
    int val;

    int double() {
        return 2 * val; // or return 2 * this->val; using the implicit this pointer
    }
}

the equivalent C code would look like:

struct foo {
    int val;
};

int double(struct foo *obj) {
    return 2 * obj->val;
}

So there is no way to obtain list.print(); in C. The idiomatic way would be to define a non member invoker:

int list_invoker(List *obj) {
    return obj->print(obj->ptr);
}

and use it as list_invoker(&list); to avoid typing twice the pointer variable.

Serge Ballesta
  • 143,923
  • 11
  • 122
  • 252
0

list.print() is the C++ or most other object oriented language idiom, but in C, it's always print(obj). If you need to have different print behaviors for some of the nodes in your list, then you would provide a print style enum or Id in your struct:

typedef struct _List{
    int **ptr;
    enum objectType {T1, T2, T3};
} List ;

bool Print(List *p)
{
    switch(p->objectType)
    ...
}

Note that you could use an integer Id and build a table of Id => PrintHandler mappings for extensibility.

jwdonahue
  • 6,199
  • 2
  • 21
  • 43