[Slightly OT] Trying to simulate OOP with C

D

Deniz Bahar

Hello,

A couple days ago my friend (OOP guy) shows me what OOP was all about
in C++. This morning I figured I can do pretty much the same thing
with C (by putting function pointers in structures and using Macros).

I've gone pretty far but I'm running into problems because the function
pointers within the structures need an extra argument (a pointer to
that type of structure) so they know which data to operate on. Also, I
have to initialize all the functions pointers for every structure
object I define.

I'm trying to get rid of all this using Macros, and this is where I
need help.
I get "invalid type argument ->" as error message from my
compiler/preprocessor.

Here are the includes and macros:
---------------------------------
#include <stdio.h>
#include <stdlib.h>
#include <string.h>

#define InitFunctions(P) {(P)->setname = nameset;
(P)->setlover = loverset;
(P)->setIQ = IQset;}

#define (P).NAME(S) (P).setname( (S), &(P) );

#define (P).LOVER(S, I) (P).setlover( (S), (I), &(P) );

#define (P).IQ(I) (P).setIQ( (I) , &(P) );



Here is my structure(simulated class) template:
-----------------------------------------------

struct PersonTag
{
char name[40];
char loves[20][40];
int IQ;


char *(*setname)(const char *, struct PersonTag *);
char *(*setlover)(const char *, int index, struct PersonTag *);
int (*setIQ)(const int, struct PersonTag *);

};
typedef struct PersonTag Person;



Here are the function definitions:
----------------------------------

char *nameset(const char *name, Person *P)
{
/* name = NULL signals read operation */
if(name)
strcpy(P->name, name);
return P->name;
}


char *loverset(const char *newlove, int index, Person *P)
{
/* newlove = NULL signals read operation */
if(newlove)
strcpy(P->loves[index], newlove);
return P->loves[index];
}


int IQset(const int IQ, Person *P)
{
/* IQ = 0 signals read operation */
if(IQ)
P->IQ = IQ;
return P->IQ;
}



Here is my main function:
-------------------------

int main(void)
{
Person Dan;
InitFunctions(Dan);

Dan.NAME("Dan");
Dan.LOVER("Julia", 0);
Dan.IQ(100);

printf("Name = %s\nFirst Lover = %s\n,IQ = %d\n",
Dan.NAME(NULL), Dan.LOVER(NULL,0), Dan.IQ(0) );

return 0;
}




I am not too worried about buffer overflow or error checking at this
point (strcpy). My difficulties are in trying to "hide away" the
operations of: setting function pointers in the structure objects to
the proper functions AND passing extra structure pointer argument every
time a function is called.

I would think this is possible because the name of the structure is
always used when I call a function, and it's seems pointless that I
keep having to repeat the name.

Examples:

Bob.setlover("Teresa" , &Bob);
Bob.setIQ(120, &Bob);

As you can see, "Bob" comes twice for each call. Can anyone help me
with my macros or point me to another solution? Thanks
 
T

Toni Uusitalo

Deniz Bahar said:
Hello,

A couple days ago my friend (OOP guy) shows me what OOP was all about
in C++. This morning I figured I can do pretty much the same thing
with C (by putting function pointers in structures and using Macros).

I've gone pretty far but I'm running into problems because the function
pointers within the structures need an extra argument (a pointer to
that type of structure) so they know which data to operate on. Also, I
have to initialize all the functions pointers for every structure
object I define.

I'm trying to get rid of all this using Macros, and this is where I
need help.
I get "invalid type argument ->" as error message from my
compiler/preprocessor.

Sorry in advance that I don't answer to the question about your error
message but
I only answer to the OOP question:

If you need true objects: Use C++ or perhaps Objective-C.

I personally think that careful OOP style naming (see for example GLIB -
something like
http://developer.gnome.org/doc/API/2.0/glib/glib-Byte-Arrays.html)
will be enough. No need for messy vtables etc.)

google for example for "object oriented vtable C". One hit:
http://www.embedded.com/97/fe29712.htm

with respect,
Toni Uusitalo
 
M

Michael Mair

Deniz said:
Hello,

A couple days ago my friend (OOP guy) shows me what OOP was all about
in C++. This morning I figured I can do pretty much the same thing
with C (by putting function pointers in structures and using Macros).

OO in C is not OT round here.
I've gone pretty far but I'm running into problems because the function
pointers within the structures need an extra argument (a pointer to
that type of structure) so they know which data to operate on. Also, I
have to initialize all the functions pointers for every structure
object I define.

Hmmm, register new classes "somewhere" and give this somewhere only
a standard constructor which does only the setup of function pointers.
Then, CreateObject(ClassName) is all you have to work with.

In special cases, it might even make sense to have sample class objects
which get memcpy()ed to new class objects.

The problem with passing the address of the object calling a method
cannot be circumvented.

There was a discussion about OOP in C some time ago on c.l.c where
I was involved. Use groups.google.
There was also a book about object oriented programming in ANSI C
mentioned. I cannot say anything about its quality; google for ooc.pdf.
I'm trying to get rid of all this using Macros, and this is where I
need help.
I get "invalid type argument ->" as error message from my
compiler/preprocessor.

Here are the includes and macros:
---------------------------------
#include <stdio.h>
#include <stdlib.h>
#include <string.h>

#define InitFunctions(P) {(P)->setname = nameset;
(P)->setlover = loverset;
(P)->setIQ = IQset;}

Look at the do {} while 0 construct in the c.l.c FAQ
#define (P).NAME(S) (P).setname( (S), &(P) );

#define (P).LOVER(S, I) (P).setlover( (S), (I), &(P) );

#define (P).IQ(I) (P).setIQ( (I) , &(P) );

This is not C.
You cannot fake ::, so use standard C macros.

About the interface: What do all your calls have in common?
Exactly. &P gets passed. So make it the same position in
all calls, i.e. the first argument.


Cheers
Michael
Here is my structure(simulated class) template:
-----------------------------------------------

struct PersonTag
{
char name[40];
char loves[20][40];
int IQ;


char *(*setname)(const char *, struct PersonTag *);
char *(*setlover)(const char *, int index, struct PersonTag *);
int (*setIQ)(const int, struct PersonTag *);

};
typedef struct PersonTag Person;



Here are the function definitions:
----------------------------------

char *nameset(const char *name, Person *P)
{
/* name = NULL signals read operation */
if(name)
strcpy(P->name, name);
return P->name;
}


char *loverset(const char *newlove, int index, Person *P)
{
/* newlove = NULL signals read operation */
if(newlove)
strcpy(P->loves[index], newlove);
return P->loves[index];
}


int IQset(const int IQ, Person *P)
{
/* IQ = 0 signals read operation */
if(IQ)
P->IQ = IQ;
return P->IQ;
}



Here is my main function:
-------------------------

int main(void)
{
Person Dan;
InitFunctions(Dan);

Dan.NAME("Dan");
Dan.LOVER("Julia", 0);
Dan.IQ(100);

printf("Name = %s\nFirst Lover = %s\n,IQ = %d\n",
Dan.NAME(NULL), Dan.LOVER(NULL,0), Dan.IQ(0) );

return 0;
}




I am not too worried about buffer overflow or error checking at this
point (strcpy). My difficulties are in trying to "hide away" the
operations of: setting function pointers in the structure objects to
the proper functions AND passing extra structure pointer argument every
time a function is called.

I would think this is possible because the name of the structure is
always used when I call a function, and it's seems pointless that I
keep having to repeat the name.

Examples:

Bob.setlover("Teresa" , &Bob);
Bob.setIQ(120, &Bob);

As you can see, "Bob" comes twice for each call. Can anyone help me
with my macros or point me to another solution? Thanks
 
E

E. Robert Tisdale

Deniz said:
A couple days ago my friend (OOP guy) shows me what OOP was all about
in C++. This morning I figured I can do pretty much the same thing
with C (by putting function pointers in structures and using Macros).

I've gone pretty far but I'm running into problems because the function
pointers within the structures need an extra argument (a pointer to
that type of structure) so they know which data to operate on. Also, I
have to initialize all the functions pointers for every structure
object I define.

I'm trying to get rid of all this using Macros, and this is where I
need help.
I get "invalid type argument ->" as error message from my
compiler/preprocessor.

Here are the includes and macros:
---------------------------------
#include <stdio.h>
#include <stdlib.h>
#include <string.h>

#define InitFunctions(P) {(P)->setname = nameset;
(P)->setlover = loverset;
(P)->setIQ = IQset;}

This should be done by the [pseudo] constructor.
Ditch the macros. Use inline static functions instead.
#define (P).NAME(S) (P).setname( (S), &(P) );

#define (P).LOVER(S, I) (P).setlover( (S), (I), &(P) );

#define (P).IQ(I) (P).setIQ( (I) , &(P) );

These aren't valid macro definitions.
Here is my structure(simulated class) template:
-----------------------------------------------

struct PersonTag {
char name[40];
char loves[20][40];
int IQ;
char *(*setname)(const char *, struct PersonTag *);
char *(*setlover)(const char *, int index, struct PersonTag *);
int (*setIQ)(const int, struct PersonTag *);

};
typedef struct PersonTag Person;

Here are the function definitions:
----------------------------------

char *nameset(const char *name, Person *P) {
/* name = NULL signals read operation */
if(name)
strcpy(P->name, name);
return P->name;
}

char *loverset(const char *newlove, int index, Person *P) {
/* newlove = NULL signals read operation */
if(newlove)
strcpy(P->loves[index], newlove);
return P->loves[index];
}

int IQset(const int IQ, Person *P) {
/* IQ = 0 signals read operation */
if(IQ)
P->IQ = IQ;
return P->IQ;
}

Here is my main function:
-------------------------

int main(void) {
Person Dan;
InitFunctions(Dan);

Dan.NAME("Dan");
Dan.LOVER("Julia", 0);
Dan.IQ(100);

printf("Name = %s\nFirst Lover = %s\n,IQ = %d\n",
Dan.NAME(NULL), Dan.LOVER(NULL,0), Dan.IQ(0) );

return 0;
}

I am not too worried about
buffer overflow or error checking at this oint (strcpy).
pMy difficulties are in trying to "hide away" the operations of:
setting function pointers in the structure objects to the proper functions
AND passing extra structure pointer argument
every time a function is called.

I would think this is possible
because the name of the structure is always used when I call a function,
and it seems pointless that I keep having to repeat the name.

Examples:

Bob.setlover("Teresa" , &Bob);
Bob.setIQ(120, &Bob);

As you can see, "Bob" comes twice for each call.
Can anyone help me with my macros or point me to another solution?

The member function pointers don't buy you anything
unless you want to implement [run-time] polymorphism.
The way this is done is to create a Virtual function TABLE (VTABLE)
which contains pointers to the [actual] functions
and include a pointer to this table in each object.
Virtual functions reference the actual functions
through the pointers in the VTABLE.
> cat main.c
#include <stdio.h>
#include <stdlib.h>
#include <string.h>

#define LENGTH 40
#define LOVERS 20
typedef struct Person {
const
void* pvft; // virtual function table pointer
char name[LENGTH];
size_t lovers;
char lover[LOVERS][LENGTH];
int IQ;
} Person;

typedef struct Person_virtualFunctionTable_t {
const char* (*name)(const Person*);
const char* (*love)(const Person*, size_t);
int (*IQ)(const Person*);
Person* (*newLove)(Person*, const char*);
int (*fprint)(FILE*, const Person*);
void (*destroy)(const Person*);
} Person_virtualFunctionTable_t;

const char*
ActualPerson_name(const Person* const p) {
return p->name;
}

const char*
ActualPerson_lover(const Person* const p, size_t lover) {
return (lover < p->lovers)? p->lover[lover]: NULL;
}

int
ActualPerson_IQ(const Person* const p) {
return p->IQ;
}

Person*
ActualPerson_newLove(Person* const p, const char* const lover) {
if (NULL != lover)
if (p->lovers < LOVERS) {
strncpy(p->lover[p->lovers], lover, LENGTH);
p->lover[p->lovers][LENGTH-1] = '\0';
++(p->lovers);
}
return p;
}

int
ActualPerson_fprint(FILE* const fp, const Person* const p) {
int characters = 0;
characters += fprintf(fp, "Name: %s\n", p->name);
if (0 < p->lovers) {
characters += fprintf(fp, "lover\tname\n");
for (size_t lover = 0; lover < p->lovers; ++lover)
characters += fprintf(fp, "%3d\t%s\n",
lover, p->lover[lover]);
}
characters += fprintf(fp, "IQ: %d\n", p->IQ);
return characters;
}

void
ActualPerson_destroy(const Person* const p) {
}

const Person_virtualFunctionTable_t
Person_virtualFunctionTable = {
ActualPerson_name, ActualPerson_lover, ActualPerson_IQ,
ActualPerson_newLove, ActualPerson_fprint,
ActualPerson_destroy };

Person
Person_create(
const char* const name, const char* const lover, int IQ) {
Person P;
P.pvft = (const void*)(&Person_virtualFunctionTable);
strncpy(P.name, name, LENGTH);
P.name[LENGTH-1] = '\0';
P.lovers = 0;
ActualPerson_newLove(&P, lover);
P.IQ = IQ;
return P;
}

// virtual functions
inline static const char*
Person_name(const Person* const p) {
return ((Person_virtualFunctionTable_t*)(p->pvft))->name(p);
}

inline static const char*
Person_love(const Person* const p, size_t lover) {
return ((Person_virtualFunctionTable_t*)(p->pvft))->
love(p, lover);
}

inline static int
Person_IQ(const Person* const p) {
return ((Person_virtualFunctionTable_t*)(p->pvft))->IQ(p);
}

inline static Person*
Person_newLove(Person* const p, const char* lover) {
return ((Person_virtualFunctionTable_t*)(p->pvft))->
newLove(p, lover);
}

inline static int
Person_fprint(FILE* fp, const Person* const p) {
return ((Person_virtualFunctionTable_t*)(p->pvft))->
fprint(fp, p);
}

inline static void
Person_destroy(const Person* const p) {
((Person_virtualFunctionTable_t*)(p->pvft))->destroy(p);
}

int main(int argc, char* argv[]) {
const
Person Dan = Person_create("Dan", "Julia", 100);

Person_fprint(stdout, &Dan);
Person_destroy(&Dan);

return EXIT_SUCCESS;
}
> gcc -Wall -std=c99 -pedantic -o main main.c
> ./main
Name: Dan
lover name
0 Julia
IQ: 100

As you can plainly see,
my main program is much simpler
and works much better than yours.
The main difference between C and C++ is that
C++ does most of this stuff for you.
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

Forum statistics

Threads
473,994
Messages
2,570,223
Members
46,812
Latest member
GracielaWa

Latest Threads

Top