Interface for a common data structure - c

Let's say I have Stack that I want to use to store various different types. If I define the interface as follows:
// stack.h
typedef struct Stack {
size
push // function pointers
pop
etc.
};
And let's say I want to support two different stack types, and so I create:
// stack.c
Stack person_stack;
person_stack->push = push_to_person_stack;
Stack animal_stack;
animal_stack->push = push_to_animal_stack;
How can I make it such that the push_to_<type>_stack is effectively private and the caller of that stack is only able to see the Stack->push function? Or maybe that's not possible in C and you need an OO language to do it, but what would be an example of having a unified interface for this?

You can use function pointers to emulate methods in other OOP languages but still you need to pass the instance to the method otherwise there is no way to know on which stack to push. Also using void* would make more problems than it solves.
struct Stack {
void (*push)(Stack* self, void* data); //< self here
}
Here is a way that I use to emulate template/generics in c by using macros (reference : https://github.com/wren-lang/wren/blob/main/src/vm/wren_utils.h#L16)
#define DECLARE_STACK(type) \
typedef struct { \
type* data; \
int size; \
int capacity; \
} type##Stack; \
void type##Stack_push(type##Stack* self, type value); \
type type##Stack_pop(type##Stack* self); \
void type##Stack_init(type##Stack* self); \
#define DEFINE_STACK(type) \
void type##Stack_push(type##Stack* self, type value) { \
if (self->capacity <= self->size + 1) { \
self->capacity = self->capacity * 2; \
self->data = realloc(self->data, sizeof(type) * self->capacity); \
} \
self->data[self->size] = value; \
self->size++; \
} \
\
type type##Stack_pop(type##Stack* self) { \
self->size--; \
return self->data[self->size]; \
} \
\
void type##Stack_init(type##Stack* self) { \
self->size = 0; \
self->capacity = 2; \
self->data = malloc(sizeof(type) * self->capacity); \
} \
typedef struct Person {
int id;
} Person;
DECLARE_STACK(Person); // in person.h
DEFINE_STACK(Person); // in person.c
int main() {
Person p1, p2, p3, p4;
p1.id = 1; p2.id = 2; p3.id = 3; p4.id = 4;
PersonStack stack;
PersonStack_init(&stack);
PersonStack_push(&stack, p1);
PersonStack_push(&stack, p2);
PersonStack_push(&stack, p3);
Person p;
p = PersonStack_pop(&stack); // p.id = 3
p = PersonStack_pop(&stack); // p.id = 2
PersonStack_push(&stack, p4);
p = PersonStack_pop(&stack); // p.id = 4
p = PersonStack_pop(&stack); // p.id = 1
return 0;
}
And If you want to debug, macros are harder to debug the flow with a debugger, so I've used a python script to generate the expansion of the macro to source and header files before compile (I build with scons which is python based so I automated the source files generation)

Related

Generate multiple similar functions with C preprocessor

I want to generate multiple similar functions replacing just one word across the function.
As an example, for each of the below:
OBJECT = customer
OBJECT = account
use the function template:
void add_OBJECT_to_array(void* item_ptr, int pos)
{
mtx_lock(&OBJECT_array_mtx);
OBJECT_array[pos] = *(OBJECT_t*)item_ptr;
mtx_unlock(&OBJECT_array_mtx);
return;
}
So that I can call
add_order_to_array(ord, 1);
add_customer_to_array(cust, 1);
Is this possible?
Totally possible. You just need to know about the preprocessor concatenation operator ##. The following code will generate two functions add_order_to_array and add_customer_to_array.
#define GENERATE_FUNC(OBJECT) \
void add_ ## OBJECT ## _to_array(void* item_ptr, int pos)\
{ \
mtx_lock(&OBJECT ## _array_mtx); \
OBJECT ## _array[pos] = *(OBJECT ## _t*)item_ptr; \
mtx_unlock(&OBJECT ## _array_mtx); \
return; \
}
GENERATE_FUNC(order)
GENERATE_FUNC(customer)
The preprocessor output will be (unfortunately it does not respect formatting):
void add_order_to_array(void* item_ptr, int pos) { mtx_lock(&order_array_mtx); order_array[pos] = *(order_t*)item_ptr; mtx_unlock(&order_array_mtx); return; }
void add_customer_to_array(void* item_ptr, int pos) { mtx_lock(&customer_array_mtx); customer_array[pos] = *(customer_t*)item_ptr; mtx_unlock(&customer_array_mtx); return; }
Yes it's possible:
#define DECLARE_ADD_FUNCTION(__obj) \
void add_##__obj##_to_array(void* item_ptr, int pos) \
{ \
mtx_lock(&__obj##_array_mtx); \
__obj##_array[pos] = *(__obj##_t*)item_ptr; \
mtx_unlock(&__obj##_array_mtx); \
return; \
}
DECLARE_ADD_FUNCTION(customer)
DECLARE_ADD_FUNCTION(account)
When you look at the output of the preprocessor you get:
gcc -E foo.c
void add_customer_to_array(void* item_ptr, int pos) { mtx_lock(&customer_array_mtx); customer_array[pos] = *(customer_t*)item_ptr; mtx_unlock(&customer_array_mtx); return; }
void add_account_to_array(void* item_ptr, int pos) { mtx_lock(&account_array_mtx); account_array[pos] = *(account_t*)item_ptr; mtx_unlock(&account_array_mtx); return; }
You can even ensure that the pointer type is the correct type by changing the function prototype to add_##__obj##_to_array(__obj##_t *, int pos)

Escape struct field in a macro definition

I have the following structure (simplified):
struct error_t{
const char *file;
const char *error_desc;
};
I wrote a macro to create the structure
#define ERROR_SET(error_desc) \
{ \
struct error_t tmp = {.error_desc = error_desc, .file = __FILE__}; \
struct error_t *ptr = malloc(sizeof(*ptr)); \
memcpy(ptr, &tmp, sizeof(tmp)); \
*error_ptr = ptr; \
}
The problem is that at the line
struct error_t tmp = {.error_desc = error_desc, .file = __FILE__}
both error_descs .error_desc = error_desc are replaced which is not what I wanted. The only solution I can see is to rename the macro function parameter from error_desc to _error_desc, but maybe there is a better way. Maybe we can sort of "escape" the error_desc to be substituted in the .error_desc?
Just do not use the same name for the parameter and the struct member
You can have a different MACRO that the preprocessor would replace as error_desc.
#define ERROR_DESC error_desc
Then you can define ERROR_SET like this:
#define ERROR_SET(error_desc) \
{ \
struct error_t tmp = {.ERROR_DESC = error_desc, .file = __FILE__}; \
struct error_t *ptr = malloc(sizeof(*ptr)); \
memcpy(ptr, &tmp, sizeof(tmp)); \
*error_ptr = ptr; \
}
This works because the substitution is done only once.
You can "deceive" the preprocessor with something like
#define CONCAT(a, b) a##b
#define ERROR_SET(error_desc) \
{ \
struct error_t tmp = { .CONCAT(error,_desc) = error_desc, .file = __FILE__ }; \
...\
}
but it is just not worth it. Just rename the parameter. And develop a convention for parameter naming that would help you to avoid such naming conflicts in the future.
On the second thought, the extra CONCAT macro is not even necessary. This will achieve the same objective
#define ERROR_SET(error_desc) \
{ \
struct error_t tmp = { .error##_desc = error_desc, .file = __FILE__ }; \
...\
}

Passing NULL as argument to a C macro

I am trying to define a macro -
#define macro1(arg1) \
do{ \
int _state = 0; \
if (arg1 && arg1->member_) \
_state = arg1->member_->state_; \
printf("%d", _state); \
} while(0)
A *a = new A():
macro1(a); // Works
macro1(NULL); // Error
The specific error I see is -
"error: base operand of ‘->’ is not a pointer"
Aren't we allowed to pass NULL as an argument to macros?
Macro expansion is just text replacement, so when you passed NULL, it will expand to NULL->member, clearly it is an error. One way is to use a temporary variable for that:
#define macro1(arg1) \
do{ \
A* p = (arg1);
int _state = 0; \
if (p && p->member_) \
_state = p->member_->state_; \
printf("%d", _state); \
} while(0)
A *a = new A():
macro1(a);
macro1(NULL);
This way both cases will work.
You have to understand what's a macro in order to understand your mistake. Except for the compiler, there's an animal called pre-compiler. It replaces all the macros' references by the actual code defined for this macro. So this code:
#define macro1(arg1) \
do{ \
int _state = 0; \
if (arg1 && arg1->member_) \
_state = arg1->member_->state_; \
printf("%d", _state); \
} while(0)
A *a = new A():
macro1(a); // Works
macro1(NULL); // Error
will be replaced with:
A *a = new A():
do{
int _state = 0;
if (a && a->member_)
_state = a->member_->state_;
printf("%d", _state);
} while(0)
do{
int _state = 0;
if (NULL && NULL->member_)
_state = NULL->member_->state_;
printf("%d", _state);
} while(0)
THIS code will be compiled. And now you can see for yourself what's the root cause of the compilation error.
Macros are just a text replacement.
for example, if you have
#define mac(x) x/x
that would work for must numbers but not for 0, because it will be replaced with 0/0 which is not defined.
in your case if you pass NULL it will be replaced with:
do{ \
int _state = 0; \
if (NULL && NULL->member_) \
_state = NULL ->member_->state_; \
printf("%d", _state); \
} while(0)
so what is the meaning of NULL->member_ in this case. No sense, hence it fails.
consider using a regular function, or two macros one for regular pointers and one for NULL pointers, and make your code as this:
if (ptr)
macro1(ptr);
else
macro2;
Small adjust let the pre-compiler know the type size + a forward declaration and it'll work:
#define macro1(arg1) \
do{ \
int _state = 0; \
if ((arg1) && ((A*)arg1)->member_) \
_state = ((A*)arg1)->member_->state_; \
printf("%d", _state); \
} while(0)
Complete code:
#include <stdio.h>
class A;
#define macro1(arg1) \
do{ \
int _state = 0; \
if ((arg1) && ((A*)arg1)->member_) \
_state = ((A*)arg1)->member_->state_; \
printf("%d", _state); \
} while(0)
struct member{
int state_;
};
class A {
public:
member* member_;
};
int main(int n, char** arg) {
A* a = new A();
a->member_ = new member();
a->member_->state_ = 1;
macro1(a);
macro1(NULL);
return 0;
}
The more fundamental problem is that NULL is not a pointer, it is a macro for 0.
Hence when you pass in NULL, it is equivalent to passing in 0, which of course is an error.
As other answers have mentioned, giving the argument an explicit cast will fix it
(A*)arg1
As a side note, you should be using nullptr in C++ for a null pointer.
EDIT: As #AjayBrahmakshatriya pointed out, NULL can be defined as (void*)0 (in C only) which will be a pointer, but the argument still holds true, NULL is not a pointer of your type.
EDIT2: Apparently in C++11 and later, NULL can be defined as nullptr

Autogenerating a function wrapper in C

I want to create a function wrapper, with a defined body, that calls the wrapped function.
How can I get from this:
int foo (int arg)
{
do_something(arg);
}
To something equivalent to this:
int foo (int arg)
{
always_do_this();
real_foo(arg);
}
int real_foo (int arg)
{
do_something(arg);
}
I must do the transformation automatically and it has to be signature independent and in compile time.
How can I do it?
You can do this with macros, here's a quick example:
#define CREATE_FUNC_WRAPPER(NAME, ARG) \
int NAME(ARG arg) { \
real_##NAME(arg); \
}
int real_foo(int a) {
printf("real_foo speaking: %d\n", a);
}
CREATE_FUNC_WRAPPER(foo, int)
int main() {
foo(1);
}
You need to modify it to fit your needs, your question is unclear.
I had to give up from installing wrapper on function declaration, the solution was to install the wrappers on function call using compound statements. Bellow the two macros I created to do that, one deals with calls returning void, other with any call that returns a value:
#define PROFILE_VOID(call) \
({ \
hal_time_t stop, start; \
hal_getTime(&start); \
call; \
hal_getTime(&stop); \
__PROFILE_PRINT(call, hal_secondsElapsed_d(&stop, &start)); \
})
#define PROFILE(call) \
({ \
typeof(call) __ret; \
hal_time_t stop, start; \
__profile_nested_cnt++; \
hal_getTime(&start); \
__ret = call; \
hal_getTime(&stop); \
__profile_nested_cnt--; \
__PROFILE_PRINT(call, hal_secondsElapsed_d(&stop, &start)); \
__ret; \
})
Example of use:
PROFILE_VOID(func_returning_void(arg, arg2)); //Void func
PROFILE(other_funcs()); // Any non void func
PROFILE(x = func(x, y, z)); //Any statement
x = PROFILE(func(x, y, z)); //Same as previous
if (PROFILE(func()) == 0) { } //Inside conditionals
if (PROFILE(func() == 0)) { } //Same as previous

C Struct-Function Pointer-Passing Self Reference As Argument

I would like to run the function pointed by my struct with auto-filling functionality.
This is the part that I'm working on:
#include <stdio.h>
#include <stdlib.h>
struct Fra { //Fraction
int n; //Numerator
int d; //Denominator
void (*p)(struct Fra*);
void (*sD)(int, struct Fra*);
void (*sN)(int, struct Fra*);
};
void print(struct Fra*);
void setDenom(int, struct Fra*);
void setNum(int, struct Fra*);
int main() {
struct Fra* fraA = 0;
fraA = (struct Fra*) malloc(sizeof(struct Fra));
fraA->sN = setNum;
fraA->sN(2, fraA);
fraA->sD = setDenom;
fraA->sD(3, fraA);
fraA->p = print;
fraA->p(fraA);
return 0;
}
And this is what I've been trying to achieve
From:
fraA->sN(2, fraA);
fraA->sD(3, fraA);
fraA->p(fraA);
To:
fraA->sN(2);
fraA->sD(3);
fraA->p();
After spending some time on trial-error, I've arrived to the conclusion that I need assistance on this. I've tried browsing, but it seems I don't have the right keyword, so I'm unable to verify whether this question is a double or not.
Thanks for any help.
You could declare some macros to savely always pass the correct reference, that's all you can do:
#define FRA_NEW(this, sN sD, sP) \
{ \
(this) = calloc(sizeof(*(this))) \
if (this) \
{ \
(this)->sN = (sN); \
(this)->sN = (sD); \
(this)->sN = (sP); \
} \
}
#define FR_DELETE(this) \
free(this)
#define FRA_PRINT(this) \
(this)->print(this)
#define FRA_SETNUM(this, num) \
(this)->setNum(this, num)
#define FRA_SETDENOM(this, denom) \
(this)->setDenom(this, denom)
Also I'd propose to have "this" always as first parameter to the "member"-functions.
int main(void)
{
struct Fra * fraA = NULL;
FRA_NEW(fraA, setNum, setDenom, print);
if (NULL == fraA)
{
perror("FRA_NEW() failed");
return 1;
}
FRA_SETNUM(fraA, 2);
FRA_SETDENOM(fraA, 3);
FRA_PRINT(fraA);
FRA_DELETE(fraA);
return 0;
}
The only really useful way I can think of would be to provide functions which do the job:
void call_print(struct Fra* fra)
{
fra->p(fra);
}
void call_setDenom(int val, struct Fra* fra)
{
fra->sD(val, fra);
}
void call_setNum(int val, struct Fra* fra);
{
fra->sN(val, fra);
}
and use these:
call_setNum(2, fraA);
call_setDenom(3, fraA);
call_print(fraA);

Resources