Related
I want a C function that I can pass a structure to and it will return the number of fields/members within the structure.
At the moment I've found the easiest way is to just make a table and add to the table anytime I add a struct to any of the programs.
I guess I'm experiencing an XY problem (per the suggestions) So anyway, the reason why I would have use for this type function is because I'm wanting to send structures over a socket and to avoid the whole serialization process the way in which i'm trying to do this is by adding delimiters to the structure's field members. Like this:
0x001 --> hdrdata --> 0x003 --> hdrdata --> 0x017 --> 0x002 --> bodydata --> 0x003 --> bodydata --> 0x003 --> bodydata --> 0x004
As you can see there may be 1 bodydata or there may be 10 bodydata etc. The only other thing I can quickly think to do is add a member to my structs that would inform my header how many 'bodydata' transmissions will be coming through so the client knows how many 0x003's to expect.
Or (and i'm not asking for an answer to this but feel free if you are so inclined) can anyone recommend a better protocol design for sending structures over socket in unix? Most of this project is for my own learning so i'm not interested in using an already complete library/package solution otherwise i'd just be writing this in another language in the first place.
This is very very ugly, but the only thing I can think of is an X-Macro:
#include <stdio.h>
#define MEMBERS \
X(char a) \
X(float b) \
X(double c)
struct T {
#define X(x) x;
MEMBERS
#undef X
};
int main(void)
{
size_t n = 0
#define X(x) +1
MEMBERS
#undef X
;
printf("%zu members\n", n);
return 0;
}
This expands to:
#include <stdio.h>
struct T {
char a;
float b;
double c;
};
int main(void)
{
size_t n = 0+1+1+1;
printf("%zu members\n", n);
return 0;
}
As pointed out by #HAL9000 in comments, you can generate both (the struct and the counter) at the same time in this way:
#include <stdio.h>
#define GENERATE_STRUCT(s) s;
#define GENERATE_SCOUNT(s) +1
#define set_struct(name) \
struct name { \
name##_members(GENERATE_STRUCT) \
}; \
\
size_t name##_count(void) \
{ \
return 0 \
name##_members(GENERATE_SCOUNT) \
; \
}
#define foo_members(X) \
X(char a) \
X(float b) \
X(double c)
set_struct(foo)
int main(void)
{
printf("%zu members\n", foo_count());
return 0;
}
In this case is expanded to:
#include <stdio.h>
struct foo {
char a;
float b;
double c;
};
size_t foo_count(void)
{
return 0+1+1+1;
}
int main(void)
{
printf("%zu members\n", foo_count());
return 0;
}
Finally, another implementation provided by #Lundin using an enum instead of a variable:
#include <stdio.h>
#define MEMBERS \
/* type name */ \
X(char, a) \
X(float, b) \
X(double, c) \
struct T {
#define X(type, name) type name;
MEMBERS
#undef X
};
enum
{
#define X(type, name) dummy_##name,
MEMBERS
#undef X
MEMBER_COUNT
};
int main(void)
{
printf("%d members\n", MEMBER_COUNT);
return 0;
}
Expands to:
#include <stdio.h>
struct T {
char a;
float b;
double c;
};
enum {
dummy_a,
dummy_b,
dummy_c,
MEMBER_COUNT
};
int main(void)
{
printf("%d members\n", MEMBER_COUNT);
return 0;
}
I found this macro #define TIMES(x) for(int i1=0;i1<x;i1++)very pratical to shorten the code text. But I do not know how to write such a macro when I have nested loops and even I do not know if it is possible. The idea is the following. Is it possible to write this code
for(int i1=0;i1<5;i1++)
for(int i2=0;i2<3;i2++)
for (int i3=0;i3<7;i3++)
/* many nested `for` loops */
{
/* some code, for example to print an array printf("%d \n",a[i1][i2][i3]) */
}
as
TIMES(5) TIMES(3) TIMES(7) ....
{
/* some code, for example to print an array printf("%d \n",a[i1][i2][i3]) */
}
with a sort of "recursive" macro that detects all TIMES and replaces them by a forloop with i1, i2, i3, ... i'n' loop counters ?
This is very bad practice, don't do this. Other C programmers are perfectly aware of for loops, but they are completely oblivious to your private, secret macro language. In addition, function-like macros like these have poor type safety and should only be used as the last resort.
The correct solution is not to use a macro, but a function. If you wish to utilize proper generic programming, you could write it as follows:
typedef void callback_t (int data);
void traverse (size_t n, int data[n], callback_t* callback)
{
for(size_t i=0; i<n; i++)
{
callback(data[i]);
}
}
Where callback is a function pointer provided by the caller, which contains the actual functionality. Similar to the loop body in your macro.
Full example:
#include <stdio.h>
typedef void callback_t (int data);
void traverse (size_t n, int data[n], callback_t* callback)
{
for(size_t i=0; i<n; i++)
{
callback(data[i]);
}
}
void print (int i)
{
printf("%d ", i);
}
int main (void)
{
int array [5] = {1, 2, 3, 4, 5};
traverse(5, array, print);
}
EDIT:
In the above example, the data type was int. But since it is generic programming, you can do some tweaks and swap it for any other data type, such as an array or a struct. The catch then is that you must pass the parameter to the callback through a pointer, instead of passing it by value. Example:
#include <stdio.h>
/* Generally it is bad practice to hide arrays behind typedefs like this.
Here it just done for illustration of generic programming in C. */
typedef int data_t[3];
typedef void callback_t (data_t* data);
void traverse (size_t n, data_t data[n], callback_t* callback)
{
for(size_t i=0; i<n; i++)
{
callback(&data[i]);
}
}
void print_array (int(*array)[3])
{
int* ptr = *array;
printf("{%d %d %d}\n", ptr[0], ptr[1], ptr[2]);
}
int main (void)
{
int array [2][3] = { {1, 2, 3}, {4, 5, 6} };
traverse(2, array, print_array);
}
This closely follows Lundin's solution, but is refactored into something more generalized.
To generically step through elements, you can leave your arguments as void *, similar to qsort and bsearch.
typedef void cb_type (void *base, size_t sz);
void
traverse (void *base, size_t n, size_t sz, cb_type *cb) {
char *p = base;
for (size_t i = 0; i < n; ++i) {
cb(p + i*sz, sz);
}
}
The callback is passed the sizeof of the element. The callback function is supposed to be aware of the underlying type of the object, so it can properly deduce which dimension is being traversed. For example, if traversing a int[4][5][6]:
int array[4][5][6];
traverse(array, 4, sizeof(*array), print_456);
And the print function could look like this:
void
print_456 (void *base, size_t sz) {
if (sz == 5 * 6 * sizeof(int)) {
traverse(base, 5, 6*sizeof(int), print_456);
puts("");
} else if (sz == 6 * sizeof(int)) {
traverse(base, 6, sizeof(int), print_456);
puts("");
} else
printf("%d ", *(int *)base);
}
Try It Online
I finally succeed in writing this macro. I found most of the informations to do that in this very good article (http://jhnet.co.uk/articles/cpp_magic). The following posts (Can we have recursive macros?, Is there a way to use C++ preprocessor stringification on variadic macro arguments?, C++ preprocessor __VA_ARGS__ number of arguments, Variadic macro trick, ...) help me also a lot.
This answer is intended to answer question. It does not address the question of macro and good programming pratices. It is another subject.
This is the code
#define SECOND(a, b, ...) b
#define IS_PROBE(...) SECOND(__VA_ARGS__, 0)
#define PROBE() ~, 1
#define CAT(a, ...) PRIMITIVE_CAT(a, __VA_ARGS__)
#define PRIMITIVE_CAT(a, ...) a ## __VA_ARGS__
#define NOT(x) IS_PROBE(CAT(_NOT_, x))
#define _NOT_0 PROBE()
#define BOOL(x) NOT(NOT(x))
#define IF_ELSE(condition) _IF_ELSE(BOOL(condition))
#define _IF_ELSE(condition) CAT(_IF_, condition)
#define _IF_1(...) __VA_ARGS__ _IF_1_ELSE
#define _IF_0(...) _IF_0_ELSE
#define _IF_1_ELSE(...)
#define _IF_0_ELSE(...) __VA_ARGS__
#define EMPTY()
#define EVAL(...) EVAL1024(__VA_ARGS__)
#define EVAL1024(...) EVAL512(EVAL512(__VA_ARGS__))
#define EVAL512(...) EVAL256(EVAL256(__VA_ARGS__))
#define EVAL256(...) EVAL128(EVAL128(__VA_ARGS__))
#define EVAL128(...) EVAL64(EVAL64(__VA_ARGS__))
#define EVAL64(...) EVAL32(EVAL32(__VA_ARGS__))
#define EVAL32(...) EVAL16(EVAL16(__VA_ARGS__))
#define EVAL16(...) EVAL8(EVAL8(__VA_ARGS__))
#define EVAL8(...) EVAL4(EVAL4(__VA_ARGS__))
#define EVAL4(...) EVAL2(EVAL2(__VA_ARGS__))
#define EVAL2(...) EVAL1(EVAL1(__VA_ARGS__))
#define EVAL1(...) __VA_ARGS__
#define DEFER1(m) m EMPTY()
#define DEFER2(m) m EMPTY EMPTY()()
#define FIRST(a, ...) a
#define HAS_ARGS(...) BOOL(FIRST(_END_OF_ARGUMENTS_ __VA_ARGS__)())
#define _END_OF_ARGUMENTS_() 0
#define MAP(m, first, ...) \
m(first,__VA_ARGS__) \
IF_ELSE(HAS_ARGS(__VA_ARGS__))( \
DEFER2(_MAP)()(m, __VA_ARGS__) \
)( \
/* Do nothing, just terminate */ \
)
#define _MAP() MAP
#define PP_NARG(...) \
PP_NARG_(,##__VA_ARGS__,PP_RSEQ_N())
#define PP_NARG_(...) \
PP_ARG_N(__VA_ARGS__)
#define PP_ARG_N( \
z,_1, _2, _3, _4, _5, _6, _7, _8, _9,_10, \
_11,_12,_13,_14,_15,_16,_17,_18,_19,_20, \
_21,_22,_23,_24,_25,_26,_27,_28,_29,_30, \
_31,_32,_33,_34,_35,_36,_37,_38,_39,_40, \
_41,_42,_43,_44,_45,_46,_47,_48,_49,_50, \
_51,_52,_53,_54,_55,_56,_57,_58,_59,_60, \
_61,_62,_63,N,...) N
#define PP_RSEQ_N() \
63,62,61,60, \
59,58,57,56,55,54,53,52,51,50, \
49,48,47,46,45,44,43,42,41,40, \
39,38,37,36,35,34,33,32,31,30, \
29,28,27,26,25,24,23,22,21,20, \
19,18,17,16,15,14,13,12,11,10, \
9,8,7,6,5,4,3,2,1,0
#define TIMES(...) EVAL(MAP(TIME2FOR,__VA_ARGS__))
#define TIME2FOR(x,...) \
for(int CAT(i,PP_NARG(__VA_ARGS__))=0; \
CAT(i,PP_NARG(__VA_ARGS__))<x; \
CAT (i,PP_NARG(__VA_ARGS__))++)
main() {
int a[3][2][4];
TIMES(3,2,4) a[i2][i1][i0]=i2*100+i1*10+i0;
TIMES (3,2,4) printf("a[%d][%d][%d] : %d\n",i2,i1,i0,a[i2][i1][i0]);
TIMES (3,2,4) {/* whatever you want : loop indexes are ...,i2,i1,i0 */}
}
It turned out to be more difficult than I thought it would be.
In C is there a way to set a function argument using function pointers such that all calls to that function pointer just use the set parameter? As an example, is it possible to do something like the code below?
void do_something(int *a, int *b, int *c){
*a = *c - 5;
*b = *c - 10;
}
typedef void (*funcptr)(int *a, int *b, int *c);
int main(){
int num = 5;
funcptr f = do_something(c=&num); // c = 5 for all calls to f(a, b)
int *a; int *b;
f(a, b); //a = 0, b = -5
num = 10;
f = do_something(c=&num); // c = 10 for all calls to f(a, b)
f(a, b); //a = 5, b = 0
}
This seems possible with external C libraries like Blocks and FFCALL, but it appears I'd need to set a, b, and c instead of setting a single argument.
no, this is not a language feature of C or of the C calling convention.
You'll need to construct function that calls your function pointer with that argument yourself; external libraries that provide such functionality are available, as you've noticed.
Another way, though I'm personally not overly fond of that is using <stdarg> and varargs, letting your function do one thing or another, depending on the number of arguments passed.
In C frameworks, you'll often find a lot of functions that work in some kind of context. That is often even used in implementation of object-oriented programming. I don't know the larger picture of what you want to implement, but often questions like these are rised in a context where someone wants to do something that resembles function overloading in C++; if that's the case, you might really just make functions that take a "state/context" argument, and further arguments, which might or might not be relevant, depending on the state.
Rather than using function pointers, you would need a wrapper function that uses a global for state.
void do_something(int *a, int *b, int c){
*a = c - 5;
*b = c - 10;
}
static int c_val = NULL;
void do_something_wrap(int *a, int *b)
{
do_something(a, b, c);
}
int main(){
int num = 5;
c_val = num;
int a; int b;
do_something_wrap(&a, &b); //a = 0, b = -5
num = 10;
c_val = num;
do_something_wrap(&a, &b); //a = 5, b = 0
}
C has no such features like default arguments or function overloading.
Moreover in this declaration
funcptr f = do_something;
the initializer shall be a function pointer.
Standard C has no such facility. Compilers offer extensions that allow this though. Nested functions in GCC, Blocks in clang or via a library like CALLBACK in clisp or assembling your call via libffi.
[..] is there a way [..] ?
There always is a way. The question is if it's worth the effort and possible downsides.
The following is based on another answer I gave about deferred (that is, called e.g. at the end of scope) function execution.
Start off with a unified type for a "partially applied function":
struct partially_applied {
void * data; // parameters
void (*function)(void *); // function unpacking parameters and calling actual function
void (*store)(void *, char const *, void *); // storing parameters
};
typedef struct partially_applied * FUN;
To make a function partially apply-able we need
a struct holding the (eventually) already applied parameters (I added an init function and an allocation function)
a function to unpack these and call the actual function
a function to store ("partially apply") parameters
Here we go:
#define MAKE_PARTIAL(fn, N, ...) \
struct partially_applied_ ## fn ## _data { \
DATA_DEF(N, __VA_ARGS__) \
}; \
\
static void init_partially_applied_ ## fn ## _data (void * p) { \
struct partially_applied_ ## fn ## _data * data = p; \
DATA_INIT(N, __VA_ARGS__); \
} \
\
static void * allocate_partially_applied_ ## fn ## _data (void) { \
void * data = malloc(sizeof(struct partially_applied_ ## fn ## _data)); \
if (data == NULL) { \
fprintf(stderr, "Allocation failure for " #fn " data\n"); \
exit(1); \
} \
init_partially_applied_ ## fn ## _data(data); \
return data; \
} \
\
static void partially_applied_ ## fn (void * p) { \
struct partially_applied_ ## fn ## _data * data = p; \
if (DATA_CHECK(N, __VA_ARGS__)) { \
fn(DATA_ACCESS(N, __VA_ARGS__)); \
} else { \
fprintf(stderr, "Not all parameters for " #fn " are vaild\n"); \
} \
} \
\
static void partially_applied_ ## fn ## _store ( \
void * p, char const * id, void * src) { \
struct partially_applied_ ## fn ## _data * data = p; \
DATA_STORE_CODE(N, __VA_ARGS__) \
fprintf(stderr, "Cannot store %s in " #fn "!\n", id); \
}
The above contains some macros. These depend on the number of macro arguments (which could be counted by the preprocessor, but I want to keep it simpler). To expand the correct macro (depending on the number of arguments) we need a little helper:
#define SPLICE_2(l,r) l##r
#define SPLICE_1(l,r) SPLICE_2(l,r)
#define SPLICE(l,r) SPLICE_1(l,r)
Now on to the macros. DATA_DEF defines the structure contents:
#define DATA_DEF_0(...)
#define DATA_DEF_1(type, name) type name; bool name ## _valid;
#define DATA_DEF_2(type, name, ...) type name; bool name ## _valid; DATA_DEF_1(__VA_ARGS__)
#define DATA_DEF_3(type, name, ...) type name; bool name ## _valid; DATA_DEF_2(__VA_ARGS__)
#define DATA_DEF_4(type, name, ...) type name; bool name ## _valid; DATA_DEF_3(__VA_ARGS__)
// add more to support more parameters
#define DATA_DEF(N, ...) SPLICE(DATA_DEF_,N)(__VA_ARGS__)
DATA_INIT expands to code to initialize such a structure:
#define DATA_INIT_0(...)
#define DATA_INIT_1(t, name) data->name ## _valid = false;
#define DATA_INIT_2(t, name, ...) data->name ## _valid = false; DATA_INIT_1(__VA_ARGS__)
#define DATA_INIT_3(t, name, ...) data->name ## _valid = false; DATA_INIT_2(__VA_ARGS__)
#define DATA_INIT_4(t, name, ...) data->name ## _valid = false; DATA_INIT_3(__VA_ARGS__)
// add more to support more parameters
#define DATA_INIT(N, ...) SPLICE(DATA_INIT_,N)(__VA_ARGS__)
DATA_CHECK expands to a condition testing if all arguments have been applied:
#define DATA_CHECK_0(...) true
#define DATA_CHECK_1(t, name) data->name ## _valid
#define DATA_CHECK_2(t, name, ...) data->name ## _valid && DATA_CHECK_1(__VA_ARGS__)
#define DATA_CHECK_3(t, name, ...) data->name ## _valid && DATA_CHECK_2(__VA_ARGS__)
#define DATA_CHECK_4(t, name, ...) data->name ## _valid && DATA_CHECK_3(__VA_ARGS__)
// add more to support more parameters
#define DATA_CHECK(N, ...) SPLICE(DATA_CHECK_,N)(__VA_ARGS__)
DATA_ACCESS expands into code for passing the parameters to the actual function (actually it's only the comma separated arguments list):
#define DATA_ACCESS_0(...)
#define DATA_ACCESS_1(t, name) data->name
#define DATA_ACCESS_2(t, name, ...) data->name, DATA_ACCESS_1(__VA_ARGS__)
#define DATA_ACCESS_3(t, name, ...) data->name, DATA_ACCESS_2(__VA_ARGS__)
#define DATA_ACCESS_4(t, name, ...) data->name, DATA_ACCESS_3(__VA_ARGS__)
// add more to support more parameters
#define DATA_ACCESS(N, ...) SPLICE(DATA_ACCESS_,N)(__VA_ARGS__)
And finally DATA_STORE_CODE expands into code to store the parameters:
#define DATA_STORE_CODE_OP(type, name) \
if (strcmp(id, #name) == 0) { data->name = *((type *) src); data->name ## _valid = true; return; }
#define DATA_STORE_CODE_0(...)
#define DATA_STORE_CODE_1(type, name) DATA_STORE_CODE_OP(type, name)
#define DATA_STORE_CODE_2(type, name, ...) DATA_STORE_CODE_OP(type, name) DATA_STORE_CODE_1(__VA_ARGS__)
#define DATA_STORE_CODE_3(type, name, ...) DATA_STORE_CODE_OP(type, name) DATA_STORE_CODE_2(__VA_ARGS__)
#define DATA_STORE_CODE_4(type, name, ...) DATA_STORE_CODE_OP(type, name) DATA_STORE_CODE_3(__VA_ARGS__)
// more
#define DATA_STORE_CODE(N, ...) SPLICE(DATA_STORE_CODE_,N)(__VA_ARGS__)
Adding little helpers to allocate and free partially applied function structures (data is expected to be allocated by malloc here) ...
FUN make_fun(void (*function)(void *), void (*store)(void *, char const *, void *), void * data) {
FUN f = malloc(sizeof(*f));
if (f == NULL) {
fprintf(stderr, "Allocation of FUN failed\n");
exit(1);
}
f->function = function;
f->store = store;
f->data = data;
return f;
}
void free_fun(FUN f) {
free(f->data);
free(f);
}
... we can go on to define a macro that actually makes an instance of a partially applied function:
#define PARTIAL(fn) make_fun(&(partially_applied_ ## fn), \
&(partially_applied_ ## fn ## _store), \
allocate_partially_applied_ ## fn ## _data())
Of course we want to be able to apply some arguments:
#define APPLY(PFN, N, ...) \
do { \
struct partially_applied * pfn = (PFN); \
DATA_STORE(N, __VA_ARGS__) \
} while(0)
The macro DATA_STORE expands into code to call the store functions multiple times, so that we can apply multiple arguments at once:
#define DATA_STORE_OP(name, value) pfn->store(pfn->data, #name, &(value));
#define DATA_STORE_0(...)
#define DATA_STORE_1(name, value) DATA_STORE_OP(name, value)
#define DATA_STORE_2(name, value, ...) DATA_STORE_OP(name, value) DATA_STORE_1(__VA_ARGS__)
#define DATA_STORE_3(name, value, ...) DATA_STORE_OP(name, value) DATA_STORE_2(__VA_ARGS__)
#define DATA_STORE_4(name, value, ...) DATA_STORE_OP(name, value) DATA_STORE_3(__VA_ARGS__)
#define DATA_STORE(N, ...) SPLICE(DATA_STORE_,N)(__VA_ARGS__)
Last but not least we want to be able to call such a function (this could also be a function, but well):
#define CALL(fn) (fn)->function((fn)->data)
Finally, an example:
void foo(char * str, int i) {
printf("FOO| str = %s, i = %d\n", str, i);
}
void bar(float f, int i, size_t s) {
printf("BAR| f = %f, i = %d, s = %zu\n", f, i, s);
}
MAKE_PARTIAL(foo, 2, char *, string, int, integer)
MAKE_PARTIAL(bar, 3, float, floating, int, INT, size_t, SOME_SIZE)
int main() {
FUN f = PARTIAL(foo);
char * c = "Crazy";
APPLY(f, 1, string, c);
printf("doing other stuff\n");
FUN g = PARTIAL(bar);
size_t size = 99;
APPLY(g, 1, SOME_SIZE, size);
int answer = 42;
APPLY(f, 1, integer, answer);
answer = 21;
float pi = 3.14;
APPLY(g, 2, INT, answer, floating, pi);
CALL(f);
printf("done\n");
CALL(g);
printf("now completely done\n");
return 0;
}
Some downsides:
macros. macros everywhere.
Losing some type safety (in APPLY)
Need for lvalues (APPLY(f, 1, integer, 42) does not work)
I'm building a simple garbage collector in C, with a void pointer linked list that collects the malloced pointers and frees them all at the end.
#include "linked_list.h"
#define MAKE_GC(NAME) \
MAKE_LIST(NAME); \
static void _throw_away_##NAME() { \
ITERATOR(NAME) = &NAME; \
do { \
free(ITERATOR(NAME)->elem); \
} while ((ITERATOR(NAME) = ITERATOR(NAME)->next) != NULL); \
DESTROY_LIST(NAME); \
}
#define GC_ALLOC(TYPE, TARGET, LEN, GC_NAME) \
do { \
TARGET = (TYPE *)malloc(LEN * sizeof(TYPE)); \
PUSH(TYPE *, TARGET, GC_NAME); \
} while (0)
#define GC_FREE(NAME) _throw_away_##NAME()
The above is the garbage collector, and below is linked_list.h
struct linked_list {
void *elem;
struct linked_list *next;
};
#define ITERATOR(LIST_NAME) _iter_##LIST_NAME
#define MAKE_LIST(NAME) \
struct linked_list NAME = { NULL, NULL }; \
struct linked_list *ITERATOR(NAME) = &NAME
#define PUSH(TYPE, X, LIST) \
do { \
ITERATOR(LIST) = ITERATOR(LIST)->next = (struct linked_list *)malloc(sizeof(struct linked_list)); \
*(TYPE *)ITERATOR(LIST)->elem = X; \
ITERATOR(LIST)->next = NULL; \
} while (0)
#define DESTROY_LIST(LIST) \
do { \
struct linked_list *l; \
ITERATOR(LIST) = &LIST; \
do { \
l = ITERATOR(LIST)->next; \
free(ITERATOR(LIST)); \
} while ((ITERATOR(LIST) = l) != NULL); \
} while (0)
When I test this code with the following,
#include <stdio.h>
#include "garbage_collector.h"
MAKE_GC(char_gc);
int main() {
char *str;
int i;
GC_ALLOC(char, str, 11, char_gc);
for (i = 0; i < 10; i++) {
putchar(str[i] = i + '0');
}
str[i] = '\0';
putchar('\n');
puts(str);
GC_FREE(char_gc);
return 0;
}
It runs as expected, although the debuggers (gdb and Visual Studio debugger) keep on throwing segfaults in GC_ALLOC. It's a really short piece of code, and I'm quite annoyed that I still have no clue where it goes wrong.
I want to be sure where my program is broken and fix it before implementing elsewhere. Thanks in advance for any help.
In your PUSH macro, I don't think this line is correct:
*(TYPE *)ITERATOR(LIST)->elem = X; \
I think you want something like this:
ITERATOR(LIST)->elem = (void *)X; \
In your code, I get a seg fault here:
GC_ALLOC(char, str, 11, char_gc);
and it's due to the fact that GC_ALLOC does a PUSH, passing str as X. Once str has been properly allocated by the previous line, you want to assign the pointer to elem, but your code attempts to dereference elem, and then assign the pointer to that dereferenced item. dereferencing elem is illegal at that point, and seg faults, because it has not been allocated (and should not be at that point).
After that, you have a problem at the final GC_FREE in your code, because it eventually tries to free an unallocated pointer. When GC_FREE attempts to DESTROY_LIST(gc_Char), it starts with the first item in the list. But this item was not allocated via malloc (take a look at MAKE_LIST), so it cannot be free'd using free. So we have to skip past this first item in the list when we want to DESTROY_LIST. The following code has these items fixed and seems to work correctly for your test case:
#include <stdio.h>
#include <stdlib.h>
struct linked_list {
void *elem;
struct linked_list *next;
};
#define ITERATOR(LIST_NAME) _iter_##LIST_NAME
#define MAKE_LIST(NAME) \
struct linked_list NAME = { NULL, NULL }; \
struct linked_list *ITERATOR(NAME) = &NAME
#define PUSH(TYPE, X, LIST) \
do { \
ITERATOR(LIST) = ITERATOR(LIST)->next = (struct linked_list *)malloc(sizeof(struct linked_list)); \
ITERATOR(LIST)->elem = (void *)X; \
ITERATOR(LIST)->next = NULL; \
} while (0)
#define DESTROY_LIST(LIST) \
do { \
struct linked_list *l; \
ITERATOR(LIST) = &LIST; \
l = ITERATOR(LIST)->next; \
while ((ITERATOR(LIST) = l) != NULL) { \
l = ITERATOR(LIST)->next; \
free(ITERATOR(LIST)); \
} \
} while (0)
#define MAKE_GC(TYPE, NAME) \
MAKE_LIST(NAME); \
static void _throw_away_##NAME() { \
ITERATOR(NAME) = &NAME; \
do { \
free(ITERATOR(NAME)->elem); \
} while ((ITERATOR(NAME) = ITERATOR(NAME)->next) != NULL); \
DESTROY_LIST(NAME); \
}
#define GC_ALLOC(TYPE, TARGET, LEN, GC_NAME) \
do { \
TARGET = (TYPE *)malloc(LEN * sizeof(TYPE)); \
PUSH(TYPE *, TARGET, GC_NAME); \
} while (0)
#define GC_FREE(NAME) _throw_away_##NAME()
MAKE_GC(char, char_gc);
int main() {
char *str;
int i;
GC_ALLOC(char, str, 11, char_gc);
for (i = 0; i < 10; i++) {
putchar(str[i] = i + '0');
}
str[i] = '\0';
putchar('\n');
puts(str);
GC_FREE(char_gc);
return 0;
}
Found this very helpful Q/A on SO: Is there any way to loop through a struct with elements of different types in C?
but since I am quite new to the whole X-Macro stuff, I was wondering, if and how would it be possible to adapt this example for a struct with arrays - like this:
typedef struct
{
uint8 Addr1[SIZEOF_ADDR];
uint8 Addr2[SIZEOF_ADDR];
uint8 Addr3[SIZEOF_ADDR];
} TEST;
This is what would be to adapt:
//--- first describe the structure, the fields, their types and how to print them
#define X_FIELDS \
X(int, field1, "%d") \
X(int, field2, "%d") \
X(char, field3, "%c") \
X(char *, field4, "%s")
//--- define the structure, the X macro will be expanded once per field
typedef struct {
#define X(type, name, format) type name;
X_FIELDS
#undef X
} mystruct;
My starting point was like this, but I am quite sure, format would have to be something else, or would have to be replaced:
#define X_FIELDS \
X(uint8, Addr1, "%02X") \
X(uint8, Addr2, "%02X") \
X(uint8, Addr3, "%02X")
An address would be something like {0x10,0x12,0x0A} - all of the same size.
Edit:
This is an example how I am using this struct currently, without x-macros:
TEST test = {{0x16,0xA4,0x3},
{0x16,0xA4,0x2},
{0x16,0xA4,0x1}};
printf("%02X",test.addr1[i]);
You've got a reasonable start...
#include <stdio.h>
typedef unsigned char uint8;
enum { SIZEOF_ADDR = 3 };
#define X_FIELDS \
X(uint8, Addr1, "0x%02X") \
X(uint8, Addr2, "0x%02X") \
X(uint8, Addr3, "0x%02X")
//--- define the structure, the X macro will be expanded once per field
typedef struct {
#define X(type, name, format) type name[SIZEOF_ADDR];
X_FIELDS
#undef X
} TEST;
extern void iterate1(TEST *test);
extern void iterate2(TEST *test);
//--- Print the values
void iterate1(TEST *test)
{
const char *pad;
//--- "iterate" over all the fields of the structure
#define X(type, name, format) \
printf("%s is ", #name); \
pad = "{"; \
for (size_t i = 0; i < sizeof(test->name); i++) \
{ \
printf("%s" format, pad, test->name[i]); \
pad = ","; \
} \
printf("}\n");
X_FIELDS
#undef X
}
// Alternatively, define a function `print_addr()`
static void print_addr(const char *format, const uint8 *addr, size_t addrsize)
{
char pad = '{';
for (size_t i = 0; i < addrsize; i++)
{
putchar(pad);
printf(format, addr[i]);
pad = ',';
}
putchar('}');
}
//--- Print the values using print_addr()
void iterate2(TEST *test)
{
//--- "iterate" over all the fields of the structure
#define X(type, name, format) \
printf("%s is ", #name); \
print_addr(format, test->name, sizeof(test->name)); \
printf("\n");
X_FIELDS
#undef X
}
(This code, treated as a single file, is known to compile cleanly under GCC 4.7.1 on Mac OS X 10.7.5.)