In an effort to learn pure C (coming from C++), I’ve decided to write a simple math library using structs and macros.
So far, I have this as a test macro:
#define MulVec2(dest,src) ((dest.x) = (dest.x) * (src.x); (dest.y) = (dest.y) * (src.y); return dest;)
typedef struct vec2f_s
{
float x, y;
}
vec2f_t;
In my calling code, I have this:
int main(void)
{
vec2f_t v, w;
v.x = 5.0f;
v.y = 2.0f;
w.x = 3.0f;
w.y = 3.0f;
v = MulVec2(v, w);
printf( "x => %f; y => %f \n", v.x, v.y );
return 0;
}
My questions are as follows:
1) Do I need to write a separate macro for passing pointers/addresses of objects to a macro? If so, how? For instance, note that the MulVec2(dest,src) macro assumes the objects passed in aren’t dynamically allocated, but I’d like support for that as well.
2) When I compile the code, I get this error:
../main.c: In function 'main':
../main.c:15:9: error: expected ')' before ';' token
../main.c:15:7: error: incompatible types when assigning to type 'vec2f_t' from type 'float'
What can I do to fix this?
Edit
I should have clarified that I don’t plan to use just macros for this, but the reason for writing a macro is so I don’t have to write a separate function for double and float variations of the vectors. I’d like it to be as reusable as possible by following the DRY (don’t repeat yourself) principle .
As others pointed out, macros are not functions, and this would be a use case for a function. A macro simply textually expands Mul2Vec(…) to what you wrote in the macro, which fails to make any sense to the compiler, since one can’t
returnout of an expression.If you want to achieve the effect of C++ templates in C, reconsider your strategy, perhaps to simply choose one of
floatordoubleand stick to it. But if you absolutely must do generics without code duplication, you can define function-defining macros like this:Compilation units that define actual functions would look like this:
This setup gives you a poor man’s approximation of C++ templates, without type inference and any of the fancy metaprogramming features. Again, don’t go that route unless you absolutely have to, and even then, minimize the code that does it. This is not idiomatic C code, and will not be well-received by competent C programmers—while C is more open to preprocessor hacking than C++, this is well over the line.
Regarding the code you posted: to get a feeling of what macros are, get a good book on C that explains the topic, such as Kernighan and Ritchie’s “The C Programming language”. When exploring macros, please keep in mind the following:
Unless you really know what you are doing, never
returnout of a macro. If you do it anyway, make the word RETURN part of the macro name. The same goes for other flow-control statements, such asbreak,continue, andgoto. People maintaining that code after you will appreciate it.By convention, macros are always spelled with all-caps, so it’s MUL_VEC2, not MulVec2. This alerts the reader that he’s dealing with a macro, not a function.
The point of parentheses in macro definition is for the macro expansion to work even if passed a compound expression. Therefore it’s
destthat must be parenthesized, notdest.x. I.e. instead of(dest.x), one would write(dest).x.Use the
-Eswitch to the compiler to show you the preprocessor output, so you can see what the compiler sees and figure out what’s going on.