Sign in to follow this  
TheComet

Function Pointers in Structs

Recommended Posts

TheComet    3896

I'm developing a thing in C, and currently I have followed a style where I provide a struct declaration containing data, and separate functions for acting upon said data. Something like this:
 
MyData.h

struct MyData
{
   int a;
   float b;
};

void initialise( struct MyData* instance );
int get_a( struct MyData* instance );
float get_b( struct MyData* instance );

MyData.c

#include <MyData.h>

void initialise( struct MyData* instance )
{
    instance->a = 2;
    instance->b = 7.3f;
}

int get_a( struct MyData* instance )
{
    return instance->a;
}

float get_b( struct MyData* instance )
{
    return instance->b;
}

main.c

#include <MyData.h>

int main( void )

{

    MyData test;
    initialise( &test );
    int x = get_a( &test );
    float y = get_b( &test );

    return 0;

}

I'm wondering whether or not to do some pointer trickery to make it look more OO, like the following:

 

MyData.h

struct MyData
{
    int a;
    float b;

    int (*get_a) (struct MyData*);
    float (*get_b) (struct MyData*);
};

void initialise( struct MyData* instance );

MyData.c


#include <MyData.h>

static int get_a( struct MyData* instance )
{
    return instance->a;
}

static float get_b( struct MyData* instance )
{
    return instance->b;
}

void initialise( struct MyData* instance )
{
    instance->get_a = get_a;
    instance->get_b = get_b;

    instance->a = 2;
    instance->b = 7.3f;
}

main.c


#include <MyData.h>

int main( void )

{

    MyData test;
    initialise( &test );
    int x = test.get_a( &test );
    float y = test.get_b( &test );

    return 0;

}

The benefit I see is being make get_a and get_b static, so they can't be called directly without having instantiated a type from MyData.

What other pro's and con's are there from doing this, and am I doing the initialisation correctly for assigning the function pointers?

Share this post


Link to post
Share on other sites
alh420    5995

The first approach is how normal member functions work in C++

 

The second is similar to how virtual functions work.

 

The pros and cons are roughly the same...you get some overhead in calling function pointers, but it can open the door to polymorphic "classes".

 

I can agree it looks a bit nicer, but that might be because I'm a C++ guy, and the question arises, why not just use C++? smile.png

 

 

 


The benefit I see is being make get_a and get_b static, so they can't be called directly without having instantiated a type from MyData.

 

Not sure this is a big benefit though...

You'll just have a different runtime error while calling the functions if the struct isn't correctly initialized, though I guess it might fail faster..

And wouldn't you need a pointer to the struct anyway to be able to call the function? Hopefully it would be initialized at that point if your architecture is good.

Edited by Olof Hedman

Share this post


Link to post
Share on other sites
wintertime    4108

As I see it, if you decide to use C you should do it only if you want to avoid being tempted as easily to create overcomplicated OOP-isms. If you want OOP you can spare yourself from emulating it and just switch the compiler to C++ and be able to use all those goodies like function overloading, namespaces, destructors, std library, exceptions and so on.

The example looks like its doing no useful work (I guess its cause of being a quick example only); theres no need for useless getters with function pointers for such a simple struct.

In C I think a functional style without structs would make more sense:

float calculate_whatever(int a, float b);

Or with the struct:

typedef struct {
  int a;
  float b;
} MyData;

int main() {
  MyData data={.a=1, .b=3.141f};

  float f=mydata_calculate(&data);
  // ...
}

Though if you plan on allocating many of those structs think about using SoA style and providing functions for looping over whole separate arrays.

Edited by wintertime

Share this post


Link to post
Share on other sites
TheComet    3896

@all - Thanks a lot, the information provided was very insightful to me, and I see that applying the "OO" style to my program is going to be overkill, adding unnecessary complexity.

 


Just makes me wish I was using C++ though - any reason you aren't?

Three reasons: Mainly because the program in question is simple, needs to be very close to the underlying hardware, and there actually isn't a compiler (that I know of) that is capable of compiling C++ code to the target architecture.

 

Even if there were a possibility to use C++, I'd neglect to do so. I feel more comfortable programming micro controllers in C because it keeps things clean and simple. Especially when others work with your code.

Share this post


Link to post
Share on other sites
BeerNutts    4400

So, I assume you're providing accessor functions (get_a(), get_b()) to ensure the data is only accessed by those functions, but the way you've implemented it, anyone can just access it via myData->a, etc.

 

If you want to hide the data, you can externally define the structure as an empty array in a header file you publish:

// myDataPublic.h
typedef struct
{
  unsigned char Data[8];
} MyData;
 
int MyDataGetA(MyData* data);
float MyDataGetB(MyData *data);

 

So whoever is using it doesn't know the contents of the struct.

 

Considering you're working on a small micro, processing/memory may be an issue, and trying to get fancy with accessors is a waste IMO.  Just allow them to access the members directly.

Share this post


Link to post
Share on other sites
Aardvajk    13207
I think the best way to implement data hiding in C is to have a unit provide handles and manage the memory for the actual data privately in the unit.

So you have like StringHandle getString(const char*) and FreeString(StringHandle) etc.

This is the only way I know to guarantee in C that data and implementation remain private and means you can centralize control of allocation.

But if you need thus kind of control you need a very good reason to be using C in the first place.

Share this post


Link to post
Share on other sites

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now

Sign in to follow this