I'm trying to pass an identifier and a type to a macro that should declare the variable
#define DECLARE_PARAM(TYPE, PARAM) \
TYPE PARAM; \
gboolean __exist_##PARAM = FALSE
...
DECLARE_PARAM(char[100], title);
but I'm getting compilation errors :
expected identifier or '(' before '[' token
DECLARE_PARAM(char[100], title);
^
EDIT
It works with simple types such char or int, is there a way I can pass an array type?
CONTEXT
I'm actually trying to make a variadic function that takes a string of parameters name separated by spaces "title color other" and then their values so it would be called like this :
create_window("color title", "blue", "My window");
so the function would give default values to the none specified argument.
I wrote this as a test, but now I need to put this boilerplate code into a macro like this
for example :
DEFAULT_PARAM(title, char[100], "My window");
here is the test I wrote
#define DECLARE_PARAM(TYPE, PARAM) \
TYPE PARAM; \
gboolean __exist_##PARAM = FALSE
GtkWidget *fenetre_creer(char *keys, ...)
{
va_list args;
DECLARE_PARAM(char[100], titre);
DECLARE_PARAM(char[100], icon);
DECLARE_PARAM(int, padding);
GList *argList = read_args(keys);
int nombreArguments = g_list_length(argList);
va_start(args, nombreArguments);
GList *iterator = argList;
while(iterator)
{
char *argument = iterator->data;
if(strcmp(argument, "titre"))
{
strcpy(titre, va_arg(args, char*));
_titre_param = TRUE;
}
else if(strcmp(argument, "icon"))
{
strcpy(icon, va_arg(args, char*));
_icon_param = TRUE;
}
else if (strcmp(argument, "padding"))
{
padding = va_arg(args, int);
_padding_param = TRUE;
}
iterator = iterator->next;
}
var_end();
if(!__exist_titre)
strcpy(titre, "Titre de la fenetre");
if(!__exist_padding)
padding = 10;
if(!__exist_icon)
strcpy(icon, "");
printf("titre : %s\n", titre);
printf("padding : %d\n", padding);
printf("icon : %s\n", icon);
}
As others have noted, char[100] foo is legal Java but not legal C or C++, at least in the dialects I've used.
I'm not guaranteeing it, but you could try:
typedef char Char100[100];
DECLARE_PARAM(Char100, titre);
I suspect that would work. HOWEVER, using macros to add syntax to the language is almost always a bad idea. You really aren't saving yourself that much work, and you're creating a not-quite-C program which will be harder for other C programmers to work with. Don't do it for trivial things like this, and if you must do it document it to death.
Especially since you can say the same thing more clearly as
typedef char Char100[100];
Char100 titre;
RESPONDING TO EDIT FOR "CONTEXT":
Default parameters is a very different question. For that, define wrapper functions which take a subset of the arguments and fill in the missing values before calling the general version. Or define the function to recognize a reserved value (null, or -1, or something else that won't otherwise occur) as a request to use the default value for that parameter. Much cleaner than trying to warp the syntax of the language.
Related
Suppose i have code like this in my program:
if (!strcmp(current, "sin")) {
pushFloat(sin(x), &operands);
} else if (!strcmp(current, "cos")) {
pushFloat(cos(x), &operands);
} else if (!strcmp(current, "tan")) {
pushFloat(tan(x), &operands);
} else if (!strcmp(current, "ctg")) {
pushFloat(1. / tan(x), &operands);
} else if (!strcmp(current, "ln")) {
pushFloat(log(x), &operands);
} else if (!strcmp(current, "sqrt")) {
pushFloat(sqrt(x), &operands);
}
There are function names such as "sin" or "cos" saved in the current char array
Instead of using this long if block, or replacing it with an even longer switch block, i wanted to write a simple macro like this: #define PUSHFUNC(stack, func, value)(pushFloat(func(value), &stack)) and call it like this PUSHFUNC(operands, current, x)
Doing it this way creates an error "current is not a function or function pointer". I initially thought macros are just text replacement, so if i force a string that is equal to an actual function into a macro, it would expand to the function itself, but looks like i was wrong. Is there a way to achieve what i want using a macro, or should i just write a map block?
I initially thought macros are just text replacement,
That's your problem: macros are just text replacement. So if you have:
#define PUSHFUNC(stack, func, value) (pushFloat(func(value), &stack))
And you write:
PUSHFUNC(operands, current, x)
You get:
(pushFloat(current(value), &operands))
And indeed, you have no function named current. Macros are expanded before your code compiles; the preprocessor has no knowledge of the content of your variables.
If you really want to avoid a long chain of if statements, you could implement some sort of table lookup:
#include <stdio.h>
#include <string.h>
#include <stddef.h>
#include <math.h>
typedef double (*floatop)(double x);
typedef struct {
char *name;
floatop operation;
} entry;
double ctg(double);
entry opertable[] = {
{"sin", sin},
{"cos", cos},
{"tan", tan},
{"ctg", ctg},
{"sqrt", sqrt},
{NULL, NULL},
};
double ctg(double x) {
return 1. / tan(x);
}
floatop findop(char *name) {
int i;
for (i=0; opertable[i].name; i++) {
if (strcmp(opertable[i].name, name) == 0) {
return opertable[i].operation;
}
}
}
int main() {
float x = 4;
printf("sin(%f) = %f\n", x, findop("sin")(x));
printf("sqrt(%f) = %f\n", x, findop("sqrt")(x));
printf("tan(%f) = %f\n", x, findop("tan")(x));
printf("ctg(%f) = %f\n", x, findop("ctg")(x));
}
...but this requires that all of your functions take the same arguments, so for things like ctg you would need to add a helper function. You also need to decide if the increased complexity of the table lookup makes sense: it really depends on how many different operation names you expect to implement.
The output of the above code is:
sin(4.000000) = -0.756802
sqrt(4.000000) = 2.000000
tan(4.000000) = 1.157821
ctg(4.000000) = 0.863691
Is there a way to achieve what i want using a macro, or should i just write a map block?
I would recommend using an enum containing symbols for all the functions you might want to call, and using that in a switch-case block, instead of comparing a bunch of strings. Here's a very brief sample that only uses some of the functions you refer to...
enum which_func { SIN, COS, TAN, };
enum which_func which = SIN;
switch (which) {
case SIN:
pushFloat(sin(x), &operands);
break;
case COS:
pushFloat(cos(x), &operands);
break;
case TAN:
pushFloat(tan(x), &operands);
break;
default:
assert(false); // shouldn't be reachable if enum value is well-defined
}
This version will be easier to maintain in the long run, more efficient to execute and possibly more robust to logic errors (there are some compiler warnings that you can enable which will warn you if you're not handling all enum values, which can help you catch missed cases in your logic).
To add to what other answers said, what you can do is to make a macro that expands to the "basic block" of your if chain, avoiding some repetitions thanks to the stringizing operator:
#define HANDLE_FN_EXPR(fn, expr) \
else if(!strcmp(current, #fn)) \
pushFloat((expr), &operands)
#define HANDLE_FN(fn) \
HANDLE_FN_EXPR(fn, fn(x))
Then you can do
if(0);
HANDLE_FN(sin);
HANDLE_FN(cos);
HANDLE_FN(tan);
HANDLE_FN_EXPR(ctg, 1./tan(x));
HANDLE_FN(ln);
HANDLE_FN(sqrt);
Macros do in fact do text replacement. Given your macro definition, this:
PUSHFUNC(operands, current, x)
expands to this:
(pushFloat(current(x), &operands))
So as you can see, the text that is being replaced is the name of the variable, not the text that it contains.
And even if this did work as you expected, it wouldn't be able to properly handle the 1. / tan(x) case.
This means there isn't really a better way to do what you want.
Why not create some objects for each function type? I know, this is C not C++, but the idea will still work. First, create the function object type:-
typedef struct _Function
{
char *name;
float (*function) (float argument);
} Function;arg
And now create an array of function objects:-
Function functions [] =
{
{ "sin", sin },
{ "cos", cos }
// and so on
};
where the functions are defined:-
float sin(float x)
{
return 0; // put correct code here
}
float cos(float x)
{
return 0; // put correct code here
}
Finally, parse the input:-
for (int i = 0; i < sizeof functions / sizeof functions[0]; ++i)
{
if (strcmp(functions[i].name, current) == 0)
{
pushFloat(functions[i].function(arg)); // add operands!
break;
}
}
I find using enums for stuff like this very hard to maintain! Adding new functions means going through the code to find cases where the enum is used and updating it prone to errors (like missing a place!).
All because it's not C++, doesn't mean you can't use objects! It's just there's no language support for it so you have to do a bit more work (and, yeah, there are features missing!)
I have a problem with my C code for a stm32 target.
I get this error :
warning: pointer targets in passing argument 3 of 'Proc_Start' differ in signedness
I can't really figure out why, I searched the web for similar topics but none of the solutions proposed in the topics helped me.
I give you the code of where it breaks problem and the definition of the macro that registers the error
where the compilation generates the error
void AppGestRelay_Init(u8 u8lvoie)
{
//Init Dac value for alim
u16 u16lDacValue = (((41435.4-Param.vcoil[u8lvoie])/16376.2)/2.48)*1024;
DrDac_SetValueChip(u8lvoie+1, u16lDacValue);
//Init discharge mode
mProcStartParam(AppGestRelay_DischargeMode, &u8lvoie);
//test
TrackAlt[TRACK1] = ALTER_POS;
TrackRunning[u8lvoie] = TRACK_NOT;
}
definition of the macro
#define mProcStart(fonct) Proc_Start(fonct, NULL, (const s8*)#fonct)
#define mProcStartParam(fonct,param) Proc_Start(fonct, (TProcParam)(param), #fonct)
the function called with the macro
P_PROC(AppGestRelay_DischargeMode)
{
static u8 u8lvoie;
P_BEGIN;
u8lvoie = *(u8*)P_PARAM;
if(TRUE == Param.zener[u8lvoie])
{
PcfDataW.pin7[u8lvoie] = PIN_OFF;
printf("on\r");
P_DELAY(mTICK_MS(10));
PcfDataW.pin7[u8lvoie] = PIN_ON;
printf("off\r");
}
else
{
PcfDataW.pin6[u8lvoie] = PIN_OFF;
printf("on\r");
P_DELAY(mTICK_MS(10));
PcfDataW.pin6[u8lvoie] = PIN_ON;
printf("off\r");
}
P_EXIT();
P_CLEANUP;
P_END;
}
Thank you very much for your future help
EDIT :
I already tried but adding a 3rd argument doesn't give a warning but an error saying that the macro only takes 2 parameters
macro "mProcStartParam" passed 3 arguments, but takes just 2
The code works by slightly modifying the AppGestRelay_Init() function but there is still the warning, I would like to know where it comes from
Thanks :)
void AppGestRelay_Init(u8 u8lvoie)
{
static u8 u8lTrack;
//Init Dac value for alim
u16 u16lDacValue = (((41435.4-Param.vcoil[u8lvoie])/16376.2)/2.48)*1024;
DrDac_SetValueChip(u8lvoie+1, u16lDacValue);
//Init discharge mode
u8lTrack = u8lvoie;
mProcStartParam(AppGestRelay_DischargeMode, &u8lTrack);
//wait discharge mode is set
while(Proc_IsActif(AppGestRelay_DischargeMode))
{
P_SCHEDULE();
}
TrackRunning[u8lvoie] = TRACK_NOT;
}
SOLVE :
the warning disappears by adding the (const s8*) in front of #fonct in the definition of mProcStartParam as it is the case in the definition of mProcStart
#define mProcStartParam(fonct,param) Proc_Start(fonct, (TProcParam)(param), (const s8*) #fonct)
Thanks
Presumably the function Proc_Start requires a (signed char*) for its third argument.
#fonct evaluates to a string, which has type (char*).
You have already written (const s8*)#fonct in one macro, why not try that in the other?
In this Modern C video there's a trick that allows to postpone execution of a code until the block/scope exits. It's used as follows:
int main()
{
int foo=0, bar;
const char *etc = "Some code before defer";
defer(profile_begin(), profile_end())
{
/* Some code, which will be automatically
* preceded by call to profile_begin() and
* followed by run of profile_end().*/
foo++;
bar = 1;
}
etc = "Some code after defer";
foo = bar + 1;
}
Implementation from the video:
#define macro_var_line(name) concat(name, __LINE__)
#define defer(start,end) for( \
int macro_var_line(done) = (start,0); \
!macro_var_line(done); \
(macro_var_line(done) += 1), end)
It's pretty simply implemented. What might be confusing is the macro_var_line(name) macro. Its purpose is to simply ensure that a temporary variable will have a unique, "obfuscated" name by adding current line number to it (of where defer is called).
However the problem is that one cannot pass code to start snippet that declares new variables, because it is pasted in the for() comma operator that uses int type (the int macro_var_line(done) = …). So it's not possible to, eg.:
defer(FILE *f = fopen("log.txt","a+"), fclose(f))
{
fprintf(f,"Some message, f=%p",f);
}
I would want to have such macro, capable of declaring new vars in start snippet. Is it achievable with standard C99, C11 or maybe some GCC extensions?
UPDATE: I've found a solution utilizing GCC nested functions. Basically, the { bblock } that's following the defer() macro becomes nested function body. And it's possible to forward declare the nested function and invoke it from before the block, i.e.:
#define defer(start,end) \
auto void var_line(routine) (void); \
start; \
/* Invoke above predeclared void routine_123(void) function */ \
var_line(routine)(); \
end; \
/* Define the nested function */ \
void var_line(routine) (void)
UPDATE2: Here's an elegant version which:
runs first leading statements as start and the last one as the end code,
runs the very first statement in its own for()/declarative space,
runs the block properly via an if(cond == 0) check/block start up.
#define defer(...) \
for (int var_line(cond) = 0; var_line(cond) == 0; ) \
for (FIRST_ARG(__VA_ARGS__); var_line(cond) == 0; ) \
for (SKIP_LAST_ARG(SKIP_FIRST_ARG(__VA_ARGS__)); \
var_line(cond) == 0; \
var_line(cond) += 1 ) \
for (int var_line(cond_int) = 0; \
var_line(cond_int) <= 1; \
var_line(cond_int) += 1 ) \
if (var_line(cond_int) == 1) \
{ \
LAST_ARG(__VA_ARGS__); \
} else if (var_line(cond_int) == 0)
As I expressed in comments, my recommendation is to avoid using such a thing in the first place. Whatever your video might have said or implied, the prevailing opinion among modern C programmers is that macro usage should be minimized. Variable-like macros should generally represent context-independent constant values, and function-like macros are usually better implemented as actual functions. That's not to say that all macro use must be avoided, but most modern C professionals look poorly on complex macros, and your defer() is complex enough to qualify.
Additionally, you do yourself no favors by trying to import the style and idioms of other languages into C. The common idioms of each language become established because they work well for that language, not, generally, because they have inherent intrinsic value. I advise you to learn C and the idioms that C programmers use, as opposed to how to write C code that looks like Go.
With that said, let's consider your defer() macro. You write,
However the problem is that one cannot pass code to start snippet that declares new variables
, but in fact the restriction is stronger than that. Because the macro uses the start argument in a comma expression (start,0), it needs to be an expression itself. Declarations or complete statements of any kind are not allowed. That's only indirectly related to that expression appearing in the first clause of a for statement's control block. (The same applies to the end argument, too.)
It may also be important to note that the macro expands to code that fails evaluate the end expression if execution of the associated statement terminates by branching out of the block via a return or goto statement, or by executing a function that does not return, such as exit() or longjmp(). Additionally, unlike with Go's defer, the end expression is evaluated in full after the provided statement -- no part of it is evaluated before, which might surprise a Go programmer. These are characteristics of the options presented below, too.
If you want to pass only the start and end as macro arguments, and you want to allow declarations to appear in start, then you could do this:
// Option 1
#define defer(start,end) start; for( \
int macro_var_line(done) = 0; \
!done; \
(macro_var_line(done) += 1), (end))
That moves start out of the for statement in the macro's replacement text, to a position where arbitrary C code may appear. Do note, however, that any variable declarations will then be scoped to the innermost containing block.
If you want to limit the scope of your declarations then there is also this alternative and variations on it, which I find much more straightforward than the original:
// Option 2
#define defer(start, end, body) { start; body end; }
You would use that like so:
defer(FILE *f = fopen("log.txt","a+"), fclose(f), // argument list continues ...
fprintf(f,"Some message, f=%p",f);
);
That is somewhat tuned to your particular example, in that it assumes that the body is given as a sequence of zero or more complete statements (which can include blocks, flow-control statements, etc). As you can see, it also requires the body to be passed as a macro argument instead of appearing after the macro invocation, but I consider that an advantage, because it facilitates recognizing the point where the deferred code kicks in.
You can simulate defer by using the __attribute__((cleanup(...))) feature of GCC and Clang. Also see this SO question about freeing a variable.
For instance:
// the following are some utility functions and macros
#define defer(fn) __attribute__((cleanup(fn)))
void cleanup_free(void* p) {
free(*((void**) p));
}
#define defer_free defer(cleanup_free)
void cleanup_file(FILE** fp) {
if (*fp == NULL) { return; }
fclose(*fp);
}
#define defer_file defer(cleanup_file)
// here's our code:
void foo(void) {
// here's some memory allocation
defer_free int* arr = malloc(sizeof(int) * 10);
if (arr == NULL) { return; }
// some file opening
defer_file FILE* fp1 = fopen("file1.txt", "rb");
if (fp1 == NULL) { return; }
// other file opening
defer_file FILE* fp2 = fopen("file2.txt", "rb");
if (fp2 == NULL) { return; }
// rest of the code
}
There is actually an effort in the standard's committee to standardize a defer feature. The paper proposal also comes with a reference implementation. The idea is to propose such a feature that may be implemented with the least compiler magic possible.
If all goes to plan, that feature could even be rebase on lambdas, if we get these into C23 in time.
You could use a trick from "Smart Template Container for C". See link.
#define c_autovar(declvar, ...) for (declvar, *_c_ii = NULL; !_c_ii; ++_c_ii, __VA_ARGS__)
Basically you declare a variable and hijack it's type to form a NULL pointer. This pointer is used as a guard to ensure that the loop is executed only once.
Incrementing NULL pointer is likely Undefined Behavior because the standard only allows to form a pointer pointing just after an object and NULL points to no object. However, it's likely run everywhere.
I guess you could get rid of UB by adding a global variable:
int defer_guard;
And setting the guard pointer to a pointer to defer_guard in the increment statement.
extern int defer_guard;
#define defer_var(declvar, cleanup) \
for (declvar, *_c_ii = NULL; \
!_c_ii; \
_c_ii = (void*)&defer_guard, cleanup)
It will work fine when invoked as:
defer_var(FILE *f = fopen("log.txt","a+"), fclose(f))
{
fprintf(f,"Some message, f=%p",f);
}
EDIT
Actually it is possible to derive a macro that will accept both expression and declaration as start. One must use two for loops instead of one.
#define DEFER(start, end) \
for (int _done = 0; !_done;) \
for (start; !(_done++); end)
int main() {
DEFER(FILE *f = fopen("log.txt","a+"), fclose(f)) {
fprintf(f,"Some message, f=%p", (void*)f);
}
FILE *f;
DEFER(f = fopen("log.txt","a+"), fclose(f)) {
fprintf(f,"Some message, f=%p", (void*)f);
}
return 0;
}
//#define newScope(string, scopeType) ({ \
intrprtr.scope = realloc(intrprtr.scope, intrprtr.scope_layer*sizeof(struct Scope)); \
strncpy(intrprtr.scope[intrprtr.scope_layer-1].string, (string), 255); \
intrprtr.scope[intrprtr.scope_layer-1].scopeType = (scopeType); \
})
//newScope(string, objScope); // <-- wanted to use macro but it doesn't work. I don't get it
intrprtr.scope = realloc(intrprtr.scope, intrprtr.scope_layer*sizeof(struct Scope));
strncpy(intrprtr.scope[intrprtr.scope_layer-1].string, string, 255);
intrprtr.scope[intrprtr.scope_layer-1].scopeType = objScope; // <-- this worked
I wanted the code to be more readable, so I used a macro to wrap us to code. I got the following error if I used the macro.
src/parser.c:114:30: error: no member named 'objScope' in 'struct Scope'
newScope(string, objScope); // <-- wanted to use macro but it doesn't work. I don't get it
~~~~~~~~~~~~~~~~~^~~~~~~~~
src/parser.c:109:48: note: expanded from macro 'newScope'
intrprtr.scope[intrprtr.scope_layer-1].scopeType = (scopeType); \
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ ^
I did not get this error if I write the code explicitly.
Here is the declaration of intrprtr (in parser.h that I included in parser.c)
typedef struct{
FILE* file;
char cur_file_name[255];
int line_num;
struct Scope {
char string[255];
enum ScopeType { msgScope, objScope, codeBlock, func } scopeType;
}*scope;
int scope_layer;
}Intrprtr;
extern Intrprtr intrprtr;
According to my understanding, when I passed objScope to second argument of newScope macro, scopeType should expand to objScope. i.e.
intrprtr.scope[intrprtr.scope_layer-1].scopeType = objScope;
That no member error doesn't make any sense to me especially when intrprtr.scope[intrprtr.scope_layer-1].scopeType = objScope; actually worked. Can someone explain this for me? Thanks
In the replacement tokens intrprtr.scope[intrprtr.scope_layer-1].scopeType = (scopeType);, the scopeType token is replaced by the corresponding macro argument, objScope, both times it appears, but you want the first one to be a literal member name scopeType, not to be replaced.
Change the macro parameter name to something that is not otherwise used in the replacement tokens.
Taking up this code:
typedef enum CHECKSUM { DENY = 0, ALLOW = 1 } checksum;
#define terminal(x, str) static checksum* terminal_##x; { if(!strcmp(#str, "static")) { static checksum local = ALLOW; terminal_##x = &local; } else { checksum local = DENY; terminal_##x = &local; } }
What I want that code to do is define a macro function with two parameters x for name and str for a specific type. The macro function declares a static checksum* with the name terminal_ concatenated with the name x. Then it opens a new scope and stringify the specific type str and use a strcmp to check if it equals static. If so.. then it declares a variable type static checksum, initialized with ALLOW and makes the declared pointer to point to it, if it is not equal then it declares a variable type checksum, initialized with DENY and set the pointer to point to it.
Then we can call the macro like that:
int main(void)
{
int i = 0;
while(*terminal_name == ALLOW) { terminal(name, static) if(i > 200) { *terminal_name = DENY; } i++; }
return 0;
// Note that this is only an example usage. The real usage of this is far more long and complicated.
}
The code is well compiled on C89 and it causes no errors nor warnings. On a first view.. it works.
But as you can see by yourself.. it looks really suspicious.
Is that the correct way I am doing it?
Please ask if you are in trouble understanding something.
It's hard to say if the macro is reasonable or a bad idea without knowing more about your program.
Stylistically, you can use backslashes to split the macro up into multiple lines. That'll make it a lot more readable and less "suspicious".
#define terminal(x, str) \
static checksum* terminal_##x; \
{ \
if (!strcmp(#str, "static")) { \
static checksum local = ALLOW; \
terminal_##x = &local; \
} \
else { \
checksum local = DENY; \
terminal_##x = &local; \
} \
}
Using strcmp to decide whether to use static or not really rubs me the wrong way. That's a runtime check influencing a compile-time decision. I would suggest making two separate macros, say LOCAL_TERMINAL and STATIC_TERMINAL, rather than keying off of a macro argument.
#define LOCAL_TERMINAL (x) checksum terminal_##x = DENY
#define STATIC_TERMINAL(x) static checksum terminal_##x = ALLOW