C Private Variables Get and Set methods - c

I am working in C, and have some variables that I don't want to be global, but I do want to have get and set methods for them that can be accessed "Globaly" outside of the file. I am used to doing this in Java, but C is very different in this manner. Basically I am looking for something that follows this pseudo Code, but I have not been able to find anywhere with examples that I might look at.
main.c
#include data.h
set(b);
datalog.c
#include data.h
get(b);
data.c
private int c;
set(b){
c = b;
}
get(c){
return c;
}

You make the variable static. When a global variable is made static, its scope is restricted to the current file.
An example is as follows:
Filename: main.c
#include <stdio.h>
#include "header.h"
extern int get();
extern void set(int);
int main()
{
set(10);
printf("value = %d \n", get());
set(20);
printf("value = %d \n", get());
set(30);
printf("value = %d \n", get());
set(40);
printf("value = %d \n", get());
return 0;
}
Filename: header.h
#include <stdio.h>
int get(void);
void set(int);
Filename: header.c
#include "header.h"
static int value = 0;
int get(void)
{
return value;
}
void set(int new_value)
{
value = new_value;
}
Output:
$ gcc -Wall -o main main.c header.h header.c
$ ./main
value = 10
value = 20
value = 30
value = 40
$

If you want private variables in c, there are a number of techniques that can approximate a private variable, but the C language actually doesn't have a "protection" concept that extends to private, public, protected (as C++ does).
C will show the name of any variable (it's a requirement in C) so you must approach it with the idea of information hiding the type of the variable (making dereferencing quite difficult).
One trick is to define the variable as an void* with the actual variable type being known in only one .c module.
/* somefile.h */
extern void* counter;
/* somefile.c */
#include "somefile.h"
int actualCounter = 0;
void* counter = &actualCounter;
/* otherfile.c */
#include "somefile.h"
// we can see "counter", but we cannot "use" it here; because we don't have access
// to the real "hidden" type of "int".
A better method is to extend this idea using the struct keyword, and make pseudo-methods, like so
/* person.h */
struct s_person;
typedef Person struct s_person;
Person* new_Person(char* name);
void delete_Person(Person* person);
void Person_setName(Person* person, char* name);
char* Person_getName(Person* person);
/* person.c */
struct s_person {
char* name;
};
Person* new_Person(char* name) {
Person* object = (Person*)malloc(sizeof(struct s_person));
// duplicate the string for more security, otherwise constructor
// could manipulate the "private" string after construction.
object->name = strdup(name);
return object;
}
void delete_Person(Person* person) {
// some implementations pass a Person** to set the reference to 0
// this implementation requires that the caller sets his own references to 0
free(person->name);
free(person);
}
void Person_setName(Person* person, char* name) {
// free the old
free(person->name);
// duplicate the new to provide "out of simulated class" modification by malicious
// name setter.
person->name = strdup(name);
}
char* Person_getName(Person* person) {
// must return a copy, otherwise one can manipulate name
// from reference provided by Person_getName(...);
return strdup(person->name);
}
/* otherfile.c */
#include "Person.h"
/* Now we can hold Person "simulated objects", but we cannot */
/* manipulate their "state" without using the C simulated object */
/* methods */
int main(int argc, char** argv) {
Person* bob = new_Person("bob");
printf("%s\n", Person_getName(bob));
delete_Person(bob);
// critical or we hold a pointer to freed memory.
bob = 0;
return 0;
}
Techniques like this have several variants, one is to have a "public struct" with a void* pointer to the "private struct". One is to include the "methods" as function pointers in the "public struct" (a step towards supporting polymorphism), one is to actually write a full and proper C++ type system which attempts to resolve things exactly as C++ would (class hierarchies, polymorphisim, late binding, information hiding, etc).
Basically, you can get some "object-oriented-ness" without too much work, but as you add more features of -ornamentation, you will add more glue code (until it is much simpler to actually use an object-oriented programming language).

You can type:
static int c;
This way, the ".o" won't export the "c" variable.

By your example, you can try using some struct with this information. A struct is like a class with only public member variables (i.e. no functions). So consider something as follows
#include <stdio.h>
typedef struct _somestruct
{
int c;
} theStruct;
int getC(theStruct* obj)
{
if(obj == NULL)
return -1;
return obj->c;
}
void setC(theStruct* obj, int val)
{
if(obj == NULL)
return;
obj->c = val;
}
int main()
{
theStruct myStruct;
setC(&myStruct, 5);
printf("%d\n", getC(&myStruct));
return 0;
}
As you can see, C works only with objects and functions. But to get a global variable across all files, try static int c = 0;
The example above is nearly as close as you can possibly get to a "java-style" convention.

static int c;
int get(void) {
return c;
}
int set(int n) {
c = n;
}

You can improve on #RageD's answer by using function pointers:
#ifndef MYCLASS_H
#define MYCLASS_H
/********************************* MyClass.h **********************************/
// Typedef function pointers for usage clarity
typedef int (*GetInt)();
typedef void (*SetInt)();
typedef struct MyClass {
int Value;
GetInt GetValue;
SetInt SetValue;
} MyClass_t;
// Make the default class accessible to other modules
extern MyClass_t new_MyClass;
#endif
/********************************* MyClass.c **********************************/
#include <stdio.h>
static int getValue(MyClass_t* obj){
if(obj == NULL)
return -1;
return obj->Value;
}
static void setValue(MyClass_t* obj, int value){
if(obj == NULL)
return;
obj->Value = value;
}
// Default "constructor" of MyClass
MyClass_t new_MyClass = {0, &getValue, &setValue};
/*********************************** main.c ***********************************/
//#include "MyClass.h"
int main(){
// Create a default instance of the class
MyClass_t myClass = new_MyClass;
// Call the private (static) Getter function --> Prints 0
printf("%d\n", myClass.GetValue(&myClass));
// Set the instance's value by the Setter function
myClass.SetValue(&myClass, 9);
// Prints 9
printf("%d\n", myClass.GetValue(&myClass));
return 0;
}

Related

Init a const var in a struct after the struct variable is created

I have a c struct that has a const variable.
typedef struct {
u32 status;
const u32 dir_search_idx;} FS_OBJ;
What I would like to do is init the const variable in a function once I have created the struct object. I guess I want to do something similar to what a constructor would do in c++. Is it possible to do something similar in c? Thanks
This should work perfectly fine if you are using C99 or newer and want to initialize the const variable when creating the struct:
FS_OBJ obj = { .status = /* something */, .dir_seach_idx = /* something */ };
You can't modify the const variable after creating the struct. Then you would have to remove the const keyword as mentioned by user3386109 in the comments.
I think const is not the right tool for what you are looking for. You can put data (structs) and behavior (functions) in a *.c file and provide public functions in the corresponding header file. This way you can mimic the equivalent c++ code that you want and hide the data and of course, you can define a constructor. A great book that might help is The GLib/GTK+ Development Platform. In chapter 3 you can find a good introduction to Semi-Object-Oriented Programming in C.
Here is a possible implementation, not necessarily the best one:
/src/main.c
#include <stdio.h>
#include "point.h"
int main()
{
Point *p1 = init(6, 7);
printf("%d\n", getX(p1));
printf("%d\n", getY(p1));
Point *p2 = init(12, 14);
printf("%d\n", getX(p2));
printf("%d\n", getY(p2));
setX(p2, 16);
printf("%d\n", getX(p2));
setY(p2, 16); /* error; we want y to initialize once and remain constant. Also accessing y with p2->y is an error too. */
printf("%d\n", getY(p2)); /* getY is ok */
freep(p1);
freep(p2);
}
/src/point.h
typedef struct _Point Point;
Point *init(int, int);
int getX(Point *);
void setX(Point *, int);
int getY(Point *);
void freep(Point *);
/src/point.c
#include <stdlib.h>
#include "point.h"
struct _Point{
int x;
int y;
};
Point *init(int x, int y)
{
Point *temp;
temp = malloc(sizeof(Point));
temp->x = x;
temp->y = y;
return temp;
}
int getX(Point *p)
{
return p->x;
}
void setX(Point *p, int x)
{
p->x = x;
}
int getY(Point *p)
{
return p->y;
}
void freep(Point *p)
{
free(p);
}
Furthermore, if we need a private method in our class, we do not provide a declaration of it in the header and also we use static to restrict its access within the class's file.

How to use structs, pointers and a function in C?

I have learned how to use functions and structs and pointers. I want to combined them all into one. But the code that I write doesn't seem to work. The compiler tells me the test is an undeclared identifier. Here is the code:
#include <stdio.h>
#include <stdlib.h>
struct character
{
int *power;
};
void test (use_power)
int main ()
{
test (use_power)
printf("%d\n",*power);
return 0;
}
void test ()
{
int use_power = 25;
struct character a;
a.power = &use_power;
}
Your code has many mistakes it can't even compile
Multiple missing semicolons.
Implicit declaration of test() here
test (use_power)
with a missing semicolon too.
power is not declared in main().
This line
void test use_power()
does not make sense and is invalid, and also has no semicolon.
The a instance in test() defined at the end is local to test() and as such will be deallocated when test() returns. The use_power int, has exactly the same problem and trying to extract it's address from the function is useless because you can't access it after the function has returned.
I have no idea what you were trying to do, but this might be?
#include <stdio.h>
#include <stdlib.h>
struct character {
int *power;
};
/* Decalre the function here, before calling it
* or perhaps move the definition here
*/
void test(struct character *pointer);
/* ^ please */
int
main(void) /* int main() is not really a valid signature */
{
struct character instance;
test(&instance);
if (instance.power == NULL)
return -1;
printf("%d\n", *instance.power);
free(instance.power);
return 0;
}
void
test(struct character *pointer)
{
pointer->power = malloc(sizeof(*pointer->power));
if (pointer->power != NULL)
*pointer->power = 25;
}
Your code seems to be wrong. Your definition for test contains no arguments as
void test ()
{
int use_power = 25;
struct character a;
a.power = &use_power;
}
but your prototype contains one argument
void test (use_power)
which is wrongly put. First there are no semicolons; at the end of your prototype declaration, secondly by looking at your code, use_power is a variable and not a datatype so it cannot be present solely in a function declaration.
You will get an argument mismatch error.
You have used the line in main()
printf("%d\n",*power);
which is absolutely wrong. you cannot access any member of a structure without a structure variable.
And again, you have not mentioned the; after your call to the incorrect test()before this line
As you have not put your question so properly, I must figure out what you wish to achieve. I bet you want to hold the address of a integer in the pointer member of a structure and then print its value.
Below is a code snippet which will work as you desire.
#include <stdio.h>
#include <stdlib.h>
struct character
{
int *power;
};
struct character a; //define a structure variable
void test ();
int main ()
{
test ();
printf("%d\n",*(a.power)); // print the member of structure variable a
return 0;
}
void test ()
{
int use_power = 25;
a.power = &use_power;
}
example
#include <stdio.h>
struct character {
int *power;
};
void test(struct character *var);
int main (void){
struct character use_power;
int power = 5;
use_power.power = &power;
test(&use_power);
printf("%d\n", power);
return 0;
}
void test(struct character *var){
int use_power = *var->power;
*var->power = use_power * use_power;
}

object oriented C - variables in struct + pro's and cons

I am a young student who learned C two years ago. I just discovered more complex things like object oriented C.
My first question is:
1) How do you access your variables? The goal would be to have specific values for those variables depending on the struct which has been instantiated.
So I'd like something like:
myStruct* myStrPtr;
myStruct2* myStrPtr;
myStrPtr = initializeStruct();
myStrPtr->printContent //prints for example 55
myStrPtr2 = initializeStruct();
myStrPtr2->printContent //prints for example 6548
example
typedef struct {
void (*sum)(int a, int b);
void (*printContent)(void);
int content;
}myStruct;
void printInhoud(void){
printf("content: %d\n", content);}
void sum(int a, int b){
/***********THIS DOESN T WORK OBVIOUSLY************/
this->content = a+b;
printf("calculated sum: %d", sum);
}
myStruct * initializeStruct(void)
{
myStruct* myStrPtr = malloc(sizeof(myStruct));
myStrPtr -> foo = foo ;
mynStrPtr->printContent = printContent;
return myStrPtr;
}
void freeMem(myStruct * myStructPtr)
{
free(myStructPtr);
}
int main (void)
{
int a= 1;
int b=33;
myStruct* myStrPtr;
myStrPtr = initializeStruct();
myStrPtr->printContent();
return 0;
}
2) my second question is: what are pro's and con's about programming in this way? I think that if I am able to add the variables in the structures and access them just like in OOP, I get a big advantage: modularity. I am active in embedded software and believe that having such an advantage of OOP on an embedded system can be very interesting. I have been criticized for trying to do this. The only reason I was given: "You have no garbage collector, so don't". Could someone give me some pro's and con's and explain why this would be "such an incredibly bad programming practice"
You can do OOP in C, but one of the major cons is that you have to pass the this pointer around. Let's say you have a pointer to a printInfo() function in your struct and you want to print the info of that particular struct. You have to do
my_struct->printInfo(my_struct);
or, if my_struct is not a pointer to your struct
my_struct.printInfo(&my_struct);
There's no way around it.
For your second question, I'm not sure doing OOP in C is really practical. I have done it out of curiosity and it's really fun. You can get inheritance, virtual base classes, polymorphism and all. If you're interested you can check it out here:
https://github.com/vladcc/object-oriented-c
Disclaimer: I'm not sure that the following is truely an answer, but it is way too long for a comment.
Is it possible to do OOP in C?
Yes it is. First C++ compilers were mere pre-processors that converted C++ source to C. Of course, you have neither contructors not destructors so you must explicitely call them, you must use composition pattern for inheritance, you must have vtables for virtual methods and must explicitely pass the this pointer.
Simple example with ctor, dtor, a dynamic array and a method:
struct __foo {
int *arr;
int n;
};
typedef struct __foo foo;
bool init_foo(foo *f, int n) {
f->arr = malloc(n * sizeof(int));
f->n = n;
return (f->arr != NULL);
}
void clean_foo(foo *f) { free(f->arr); }
bool set(int index, int value, foo *f) { // same for get...
if ((index >= f->n) || (index < 0)) return false;
f->arr[index] = value;
return true;
}
Usage:
foo f;
init_foo(8, &f);
set(5, 2, &f);
clean_foo(&f);
More complex example with inheritance and virtual method :
typedef struct {
int age;
const char *name;
const char* (*say)(void *);
} animal;
typedef struct {
animal parent;
} dog;
typedef struct {
animal parent;
} cat;
void init_animal(int age, const char *name, animal *a) {
a->age = age;
a->name = name;
}
char *pre_say(animal *this) {
char * msg = malloc(strlen(this->name) + 11);
strcpy(msg, this->name);
strcat(msg, " says ");
return msg;
const char * wow(void *an) {
animal *this = (animal *) an;
char * msg = pre_say(this);
strcat(msg, "Woof");
return msg;
}
const char* meeow(void *an) {
animal *this = (animal *) an;
char * msg = pre_say(this);
strcat(msg, "Meew");
return msg;
}
void init_dog(int age, const char * name, dog *d) {
init_animal(age, name, &(d->parent));
d->say = &woof;
}
void init_cat(int age, const char * name, cat *c) {
init_animal(age, name, &(c->parent));
d->say = &meeow;
}
Usage example:
dog d;
init_dog(2, "Rintintin", &d);
cat c;
init_cat(3, "Tom", &c);
const char *msg = (d.say)(&d); // msg <- Rintintin says Woof
free(msg);
msg = (c.say)(&c); // msg <- Tom says Meew
free(msg);
Should we do OOP in C?
Definitely NO. As you can say from previous example, as soon as you need polymorphism, pointers must be void * and you loose all possible type checking. And you also loose all the C++ goodies of automatic construtors and destructors, automatic this pointer, etc. You end with much more code, much harder to read and debug. The only acceptable use cases (IMHO) are:
interfacing C++ and C
minimal conversion of C++ code for a platform where you have only a C compiler.
low level codes (kernel or high performance libraries) in which you do not want to be bored with the overhead of C++, and still need OOP.
And just remember: if C++ was invented, it must have been to fill a gap in C language. So don't try to do what C++ was created for in C, unless you really know why you do that.
Has Garbage Collector something to do with OOP?
Here again no. C++ has no garbage collection and is indeed an OO language.
Not only encapsulation, many other OOP concepts can be implemented using C with little bit effort.
Here is an example.
//
// cobj.h
//
#ifndef __COBJ_H__
#define __COBJ_H__
struct _cobj_priv;
typedef struct _cobj {
struct _cobj_priv *priv;
void (*set_data)(struct _cobj *obj, int data);
int (*get_data)(struct _cobj *obj);
void (*print_data)(struct _cobj *obj);
} cobj_t;
cobj_t *new_struct(void);
void free_struct(cobj_t *obj);
#endif /* __COBJ_H__ */
//
// cobj.c
//
#include "cobj.h"
#include <stdio.h>
#include <stdlib.h>
//
// Internal section
//
struct _cobj_priv {
int data;
};
static void set_data (struct _cobj *obj, int data) {
struct _cobj_priv *this = (obj && obj->priv) ? obj->priv: NULL;
if (this) {
this->data = data;
}
}
static int get_data (struct _cobj *obj) {
struct _cobj_priv *this = (obj && obj->priv) ? obj->priv: NULL;
return (this)? this->data : 0;
}
static void print_data (struct _cobj *obj) {
struct _cobj_priv *this = (obj && obj->priv) ? obj->priv: NULL;
if (this)
printf("%d\n", this->data);
}
//
// APIs section
//
cobj_t *new_struct(void) {
cobj_t *obj = malloc(sizeof(cobj_t));
if (obj) {
obj->priv = malloc(sizeof(struct _cobj_priv));
if (obj->priv) {
obj->priv->data = 0;
}
obj->set_data = &set_data;
obj->get_data = &get_data;
obj->print_data = &print_data;
}
return obj;
}
void free_struct(cobj_t *obj) {
if (obj) {
if (obj->priv)
free(obj->priv);
free(obj);
obj = null;
}
}
//
// main.c
//
#include "cobj.h"
#include <stdio.h>
int main(int argc, char *argv[]) {
cobj_t *obj = new_struct();
if (obj) {
obj->print_data(obj);
obj->set_data(obj, 100);
obj->print_data(obj);
printf("> get data return %d\n", obj->get_data(obj));
}
return 0;
}
Result:
0
100
> get data return 100
In C, struct's methods are function pointers, they do not know about the existence of struct so that they can not access to the struct members. You need to pass the struct instance to methods as a parameter in order to access to its members.

Typecasting (or deference) void * to struct foo*

In api.h
typedef void* hidden_my_type;
void do_something(my_type x);
In core.c
struct _my_type
{
int a;
}
void do_something(hidden_my_type void_x)
{
struct *_my_type x = void_x; /*Don't understand is that correct way to do, as I'm getting segmentation fault error */
printf("Value: %d\n", x->a);
}
Other way I thought as,
struct *_my_type x = (struct _my_type *)malloc(sizeof(struct _my_type));
void_x = x
printf(Value: %d\n", x->a);
But still I'm getting seg-fault error.
ok here is the problem with void*....
e.g.
in core.c
void init_my_type(hidden_my_type a)
{
my_type *the_a = malloc(...);
a = the_a // <<<<<<<<<<<<<<<<<<<<<<<<<<<< is this correct?! a is void* and the_a // is original type
pthread_cond_init(&the_a->...);
.. (in short any other methods for init ..)
}
void my_type_destroy(my_hidden_type x)
{
my_type *the_x = x;
pthread_detroy(&the_x-> ...);
}
in main.c
test()
{
my_hidden_type x;
init_my_type(x);
....
my_type_detroy(x);
}
this it self should fail. as in main.c test function, x is void* ... init will allocate but in destroy I'm again passing void* .. which can be anything!
EDIT (Solved for me)
In api.h
typedef void* hidden_my_type;
void do_something(my_type x);
In core.c
struct _my_type
{
int a;
}
void init_hidden_type(hidden_my_type void_p_my_type)
{
struct _my_type *real_my_type = (struct _my_type *)malloc(sizeof(struct _my_type));
//--- Do init for your type ---
void_p_my_type = real_my_type;
}
void do_something(hidden_my_type void_x)
{
struct *_my_type x = void_x;
printf("Value: %d\n", x->a);
}
Version 0 — Critique of Question's Code
The posted code does not compile.
api.h
typedef void* hidden_my_type;
void do_something(my_type x);
This defines hidden_my_type but not the my_type that is passed to do_something(). Presumably, you intended:
typedef void *my_type;
void do_something(my_type x);
core.c
struct _my_type
{
int a;
}
As noted below too, there is a semi-colon missing after the structure definition.
void do_something(hidden_my_type void_x)
{
struct *_my_type x = void_x;
printf("Value: %d\n", x->a);
}
You have the hidden_my_type vs my_type problem again. You have the * of the pointer where it cannot go; it must go after the struct _my_type. You probably intended something like:
void do_something(my_type void_x)
{
struct _my_type *x = void_x;
printf("Value: %d\n", x->a);
}
This is now syntactically correct (I think; I haven't actually run it past a compiler). You have not shown how it is used; indeed, since the user code has no way to generate a pointer to a valid structure, there is no way for this code to be used safely.
Your test code (unshown — why don't you show your test code) might look something like this:
#include "api.h"
int main(void)
{
my_type x = 0;
do_something(x);
return 0;
}
Alternatively, it might not have the = 0 initializer in place. Either way, your code is unable to function sanely, and a core dump is almost inevitable. When you hide the structure from the user, you have to provide them with a mechanism to get hold of a valid (pointer to) the structure, and you've not done that.
Version 1
This is a better way to do it, because it is more nearly type-safe:
api.h version 1
typedef struct _my_type *my_type;
void do_something(my_type x);
core.c version 1
#include "api.h"
struct _my_type
{
int a;
};
Note the added semi-colon, and the include of the api.h file.
void do_something(my_type x)
{
// Now you don't have to do casting here!
//struct *_my_type x = void_x; /*Don't understand is that correct way to do, as I'm getting segmentation fault error */
printf("Value: %d\n", x->a);
}
Version 2
Actually, we can debate the wisdom of hiding the pointer; I would prefer not to do so:
api.h version 2
#ifndef API_H_INCLUDED
#define API_H_INCLUDED
typedef struct my_type my_type;
extern void do_something(my_type *x);
extern my_type *my_type_initializer(void);
extern void my_type_release(my_type *x);
#endif /* API_H_INCLUDED */
core.c version 2
#include "api.h"
#include <stdio.h>
#include <stdlib.h>
struct my_type
{
int a;
};
void do_something(my_type *x)
{
printf("Value: %d\n", x->a);
}
my_type *my_type_initializer(void)
{
my_type *x = malloc(sizeof(*x));
x->a = 57; // More plausibly, this would be 0
return x;
}
void my_type_release(my_type *x)
{
free(x);
}
main.c
#include "api.h"
int main(void)
{
my_type *x = my_type_initializer();
do_something(x);
my_type_release(x);
return 0;
}
That's nice and clean. Of course, the user cannot allocate a struct my_type (only a pointer to it), so you need a function to allocate the structure for them. Think of the Standard C Library, and the FILE type, and fopen() to allocate and fclose() to release and fprintf() etc to manipulate the type. The my_type_initializer() is functioning as an analogue to fopen(), my_type_release() as an analogue to fclose(), and do_something() as an analogue to fprintf().
Jonathan, you beat me to an answer, but this may be helpful as well. Here, api.c contains the (private) implementation, and api.h provides the interface to be consumed by other code such as main.c.
// main.c: uses only the public interface to the private code
#include "api.h"
int main(int argc, char *argv[]) {
void *foo;
foo = create_foo("five", 5);
print_foo(foo);
delete_foo(foo);
}
// EOF main.c
// api.h: the public interface
#ifndef _api_h_
#define _api_h_
void *create_foo(char *name, int number);
void print_foo(void *foo);
void delete_foo(void *foo);
#endif // _api_h_
// api.c: the private implementation
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
// The real structure is private to the implementation.
typedef struct {
char name[20];
int number;
} real_struct;
// Create a new structure, initialize, return as ptr-to-void.
void *create_foo(char *name, int number) {
real_struct *s = malloc(sizeof(real_struct));
strcpy(s->name, name);
s->number = number;
return (void *) s;
}
// Print the data.
void print_foo(void *foo) {
real_struct *s = (real_struct *) foo;
printf("name: %s, number: %d\n", s->name, s->number);
}
// Release the memory.
void delete_foo(void *foo) {
free(foo);
}
// EOF api.c
This code should compile and run:
$ gcc -o foo main.c api.c
$ ./foo
name: five, number: 5

Accessing members of the struct via void *

The solution consists of two parts, one is a static library that receives instances of struct from the user of the library. Library doesn't know what will be the type of structs, all it knows there will be two function pointers to it with a specific name.
Library Code
pre-compiled library has no way of knowing types of user structs, hence receiving via void*
void save(void *data) {
// library will save/cache user's object
data->registered(); // if register successful
}
void remove(void *data) {
// library will remove the object from memory
data->remove(); // if removed successful
}
User of the Library Code
struct Temp { // random order of fields
void (*custom1)();
void (*registered)();
void (*custom2)();
void (*remove)();
void (*custom3)();
}
void reg() {
printf("registered");
}
void rem() {
printf("removed");
}
void custom1() {}
void custom2() {}
void custom3() {}
var temp = malloc(struct Temp, sizeof(struct Temp));
temp->registered = reg;
temp->remove = rem;
temp->custom1 = custom1; // some custom functions
temp->custom2 = custom2;
temp->custom3 = custom3;
// calling library code
save(temp);
remove(temp);
Q. Is there a way for the Library to know how to iterate and go through member fields and see if there's a pointer to such function and call it available.
Is there a way for the Library to know how to iterate and go through member fields and see if there's a pointer to such function and call it available.
No there is not.
Your best bet is to create a structure in the library that has these members, and pass that structure instead of void*.
As #immibis said, there is no way for this to work (i.e. no way for the compiler to justify compiling such code) if the compiler does not know what the types of the data being passed to the function are.
Since you wanted to pass the objects along to the library without storing information about the type of each object in the library, you can fake polymorphism in C, by doing the following:
callback.h
#ifndef _CALLBACK_H_
#define _CALLBACK_H_
typedef struct {
void (*registered)();
void (*removed)();
} ICallback;
#endif _CALLBACK_H_
pre_comp.h
#ifndef _PRE_COMP_H_
#define _PRE_COMP_H_
#include "callback.h"
void save(ICallback* data);
void remove(ICallback* data);
#endif /* _PRE_COMP_H_ */
precomp.c
#include <stdlib.h> /* NULL */
#include "callback.h"
#include "pre_comp.h"
void save(ICallback *data) {
if (NULL != data && NULL != data->registered) {
data->registered(); // if register successful
}
}
void remove(ICallback *data) {
if (NULL != data && NULL != data->removed) {
data->removed(); // if removed successful
}
}
main.c
#include <stdio.h>
#include "pre_comp.h"
#include "callback.h"
struct Temp {
ICallback base; // has to be defined first for this to work
void (*custom1)();
void (*custom2)();
void (*custom3)();
};
// calling library code
void reg() {
puts("registered");
}
void rem() {
puts("removed");
}
int main() {
struct Temp data = {{reg, rem}};
save((ICallback*)&data);
remove((ICallback*)&data);
}
compiling
gcc pre_comp.c main.c
output
registered
removed
If the library has 0 information about the possible struct types, then you
cannot do it. The library has to get somehow the information or the offsets.
The only way I can think of is:
All register member have the same prototype
Pass the offset to the function.
I created an example of this
#include <stdio.h>
#include <stddef.h>
#include <stdint.h>
// function that does not know anything about any struct
void reg(void *data, size_t offset)
{
uintptr_t *p = (uintptr_t*) (((char*) data) + offset);
void (*reg)() = (void(*)()) *p;
reg();
}
struct A {
int c;
void (*reg)();
};
struct B {
int b;
int c;
void (*reg)();
};
void reg_a()
{
printf("reg of A\n");
}
void reg_b()
{
printf("reg of B\n");
}
int main(void)
{
struct A a;
struct B b;
a.reg = reg_a;
b.reg = reg_b;
reg(&a, offsetof(struct A, reg));
reg(&b, offsetof(struct B, reg));
return 0;
}
This prints:
$ ./c
reg of A
reg of B
I run it with valgrind and I did not get any errors nor warnings. I'm not sure if
this violates somehow strict aliasing rules or yields undefined behaviour
because of the uintptr_t* conversions, but at least it seems to work.
I think however, the more cleaner solution is to rewrite the register (btw. register
is a keyword in C, you cannot use that for a function name) function to
accept a function pointer and possible parameters, something like this:
#include <stdio.h>
#include <stdarg.h>
void reg(void (*func)(va_list), int dummy, ...)
{
if(func == NULL)
return;
va_list ap;
va_start(ap, dummy);
func(ap);
va_end(ap);
}
void reg1(int a, int b)
{
printf("reg1, a=%d, b=%d\n", a, b);
}
void vreg1(va_list ap)
{
int a = va_arg(ap, int);
int b = va_arg(ap, int);
reg1(a, b);
}
void reg2(const char *text)
{
printf("reg2, %s\n", text);
}
void vreg2(va_list ap)
{
const char *text = va_arg(ap, const char*);
reg2(text);
}
int main(void)
{
reg(vreg1, 0, 3, 4);
reg(vreg2, 0, "Hello world");
return 0;
}
This has the output:
reg1, a=3, b=4
reg2, Hello world
Note that reg has a dummy parameter. I do that because the man page of
stdarg says:
man stdarg
va_start():
[...]
Because the address of this argument may be used in the va_start() macro,
it should not be declared as a register variable, or as a
function or an array type.
You can take an approach similar to qsort and pass function pointers in addition to a void pointer to the structure.
Here is the function prototype for qsort, which is a function that can be used to sort arrays of any type:
void qsort(void *base, size_t nmemb, size_t size, int (*compar)(const void *, const void *));
It takes a function pointer that performs the comparison because without it qsort wouldn't know how to compare two objects.
This can be applied to your task with a function prototype like this:
int DoFoo(void *thing, void (*register)(void *), void (*remove)(void *))
This function takes a void pointer to your struct and then two functions that it can call when it needs to register or remove that struct. Having the functions be members of the struct is not required and I generally do not recommend it. I recommend reading up on qsort because it is does something similar to what you are trying to do.

Resources