2

Suppose I have the following struct

struct Being{
    int canFly;
    int (*printFlying)();
}

And I want to do something like the following:

int main(){
    struct Being * flyingThing = new_flyingThing();
    struct Being * thingThatCantFly = new_Thing();

    flyingThing->printFlying(); /* "I'm flying!" */
    thingThatCantFly->printFlying(); /* "I can't fly!" */
}

It looks like I would need the printFlyingfunction to be able to access the canFly variable in the struct, but I don't know how to do this, or if this is possible.

Is there any way to get my code to do what I want? Thanks!!


I explained in the comments why this question is not a duplicate of the linked one.

csTroubled
  • 367
  • 1
  • 3
  • 9
  • 2
    C isn't Java. It seems that you're attempting to use OOP in C, which it isn't designed for. – Rushy Panchal Jul 12 '16 at 00:38
  • 1
    You would need to give `printFlying` a parameter which is pointer to the object in question – M.M Jul 12 '16 at 00:39
  • @John3136 I've already read the answers in that question, and they don't answer my question: Most of them use things like `function(struct ...)` to do this, while I want the function to take no parameters, (`struct... ->function()`). – csTroubled Jul 12 '16 at 00:40
  • 1
    `new_flyingThing()` and `new_Thing()` need to put different functions in the member, and the functions should print the appropriate message. – Barmar Jul 12 '16 at 00:40
  • 2
    @csTroubled That's not possible. In other languages, an implicit `this` argument is passed to the function. In C, you have to specify it explicitly. Use C++ if you want OO support. – a3f Jul 12 '16 at 00:42
  • @csTroubled: That is not possible. In real OO, instance methods of objects also take the class instance as an argument, but usually not explicitly. This can be seen as a hidden argument passed to the method. C can't hide this, so you will have to pass at least the struct. – Rudy Velthuis Jul 12 '16 at 00:42
  • @Barmar That looks like a pretty clever way to do this! Is there any way to make the function pointer unmodifiable after that? – csTroubled Jul 12 '16 at 00:43
  • `Is there any way to make the function pointer unmodifiable after that?` Not in `C`. Is there any overriding reason why your code *has* to be `C` as opposed to, say, `C++`? – dxiv Jul 12 '16 at 00:47
  • 1
    Yeah, it seems like you're trying to do something that's normal for OO languages, but you don't want to use an OO language. – Barmar Jul 12 '16 at 00:48
  • @csTroubled IIRC, `struct Being { int (*const printFlying)(); };` will cause `printFlying` only be settable when initialising the struct. – Siguza Jul 12 '16 at 00:48
  • @Siguza That means you would only be able to create automatic and static structs that are initialized in the declaration, you couldn't create them dynamically with `malloc`. – Barmar Jul 12 '16 at 00:49
  • @Barmar `struct Being *b = malloc(sizeof(struct Being)); *b = (struct Being){ &printFlyingOrNot };`, no? – Siguza Jul 12 '16 at 00:52
  • In C++, `obj->func()` allows `func` to access `obj` via the `this` pointer. C has no equivalent. But, it is possible to do `obj->func(obj)` to get [nearly] the same effect. What is the big objection to that? It's a long standing idiom for C [of mine, at least] (ie. pseudo member functions are passed the object/struct pointer as the first argument). It could be done with `#define OBJFUNC(_obj,_func) (_obj)->_func(_obj)` and `OBJFUNC(obj,func)` if you wanted to minimize syntax. – Craig Estey Jul 12 '16 at 01:07
  • Is it possible to have function in a structure in C? – tryKuldeepTanwar Jul 12 '16 at 05:20
  • @Kuldeep1007tanwar Yes, just give it a function pointer. – YoTengoUnLCD Jul 12 '16 at 16:38

3 Answers3

2

C isn't an object-oriented language, so there's no automatic way for a function member to know which structure was used to call it.

Since each structure has its own function pointer, you can get the result you want by having them refer to different functions, which print the appropriate message, instead of getting it from the can_fly member.

int print_flying_thing() {
    printf("I'm flying!\n");
}
struct Being *new_flyingThing() {
    struct Being *thing = malloc(sizeof(struct Being));
    thing->can_fly = 1;
    thing->printFlying = print_flying_thing;
    return thing;
}

int print_nonflying_thing() {
    printf("I can't fly!\n");
}
struct Being *new_Thing() {
    struct Being *thing = malloc(sizeof(struct Being));
    thing->can_fly = 0;
    thing->printFlying = print_nonflying_thing;
    return thing;
}
Barmar
  • 741,623
  • 53
  • 500
  • 612
  • Sure, but the functions still can't access members of the structure. – Keith Thompson Jul 12 '16 at 00:47
  • @KeithThompson That's true, but in all fairness I was going to use that only to check if the value of `canFly` was true or false. – csTroubled Jul 12 '16 at 00:48
  • If you make it standard that the first argument of any "oo" c struct takes a pointer to itself (`int (*printFlying)(Being *);` then you follow the process of always passing in the struct you care about: `thing->printFlying(thing)` Then you can somewhat emulate member access. – Fantastic Mr Fox Jul 12 '16 at 01:12
  • @Ben He wrote in the comments that he doesn't want the function to take a parameter. – Barmar Jul 12 '16 at 01:13
1

The function pointed to by the printFlying pointer cannot access members of the containing struct Being structure unless it's given a parameter that refers to that structure. (Or it could access the structure in some other way, perhaps via a global variable, but a parameter is the cleanest way.)

Consider, for example, two objects of type struct Being, both having their printFlying pointer pointing to the same function. There's no way that function can tell which of the two objects it's associated with -- because it isn't associated with either of them.

C++ member functions can do that because they're given an implicit pointer parameter, referred to inside the function via the this keyword. C has no such feature.

You could probably create a macro that does something similar, but I doubt it would be worth the effort.

Have you considered just using C++?

Keith Thompson
  • 254,901
  • 44
  • 429
  • 631
1

For this particular example what you seem to really want is the ability to have a function pointer that points to two different functions, one to implement flying and one that implements not flying.

So the approach I would consider would be something like the following. I have not tried to actually compile this code but the sense of this is correct.

Also in your comments you mentioned about preventing changing the function pointer. You can do something like the following where a const is added to the struct definition for the function pointer and then using a clone of the struct with a cast.

Header file flying.h contents.

typedef struct _TAG_Being {
    int (* const printFlying)();
} Being;

Being * new_flyingThing ();
Being * new_Thing();

Implementation or flying.c file.

#include "flying.h"

static int printFlyingThing()
{
    printf (" I am a flying thing\n");
    return 0;
}

static int printThing()
{
    printf (" I am a thing\n");
    return 1;
}

// use the same layout and members as Being above.
typedef struct {
    int (* printFlying)();
} BeingX;

Being * new_flyingThing ()
{
    BeingX *p = malloc(sizeof(BeingX));
    p->printFlying = printFlyingThing;  // use the flying thing function

    {
        Being *q = (Being *)p;
        return q;
    }
}

Being * new_Thing()
{
    BeingX *p = malloc(sizeof(BeingX));
    p->printFlying = printThing;       // use the not flying thing function.

    {
        Being *q = (Being *)p;
        return q;
    }
}

Main or using .c file

#include "flying.h"

int main(){
    Being * flyingThing = new_flyingThing();
    Being * thingThatCantFly = new_Thing();

    flyingThing->printFlying(); /* "I'm flying!" */
    thingThatCantFly->printFlying(); /* "I can't fly!" */
}

If you have a need to access the actual Being object or variable in the printFlying() function then you will need to pass it as an argument.

For instance:

typedef struct _TAG_Being {
    int  xx;
    int (*printFlying)(struct _TAG_Being *p);
} Being;

Then you would need something like:

Being * flyingThing = new_flyingThing();

flyingThing->printFlying(flyingThing); /* "I'm flying!" */
Richard Chambers
  • 16,643
  • 4
  • 81
  • 106