Deniz Bahar wrote:
>
>
> 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.