Bwebb
Bwebb

Reputation: 685

C Macro trick to hide assignment?

I have a project with a handful of files: source1.c source2.c source1.h source2.h. Now source1.c declares some variables and source1.h externs them (with a conditional build macro). source2.c would then use the extern variables by assigning a value to them.

The issue is that source1.h has something like the following:

#ifdef CUST1
extern type var_name;
#else
// Need to extern something or use a clever macro here but i dont like these below
//#define var_name int x (requires top of scope block usage)
//#define var_name // (doesnt compile)
//#define var_name -1 (simply doesnt compile)
//#define var_name 1= (This seems like it would reduce to unused 1==1; but doesnt work)
#endif

My build works for CUST1 but it will not work for CUST2 because var_name is never declared/outta scope when it is referenced in source2.c.

I dont want to use var_name for CUST2 and it is unreachable code in source2.c. Here is my question, how can i use a macro to #define var_name so that the assignment "goes away" or does nothing?

I could "#define var_name int x". This would put int x on the stack in the source2.c functions and assign the values in source2.c, but if it is ever referenced anywhere besides the top of a scope block, my old (C89?) compiler will error.

For example if source2.c ever had the following it would fail to compile:

unsigned short local_var = 0;
local_var = 1; //or someother value
var_name = local_var * 2;

I could wrap the logic in source2.c with the same #ifdef CUST1 macros but that seems not so good either.

If var_name was only being compared against it would not be so bad because i could just use #define var_name -1 or something that would fail all compares/switches. The issues is -1 = temp_var; does not compile because -1 cant be an lvalue.

Similiarly i cant "#define var_name //" because comments are removed before the marcos are replaced according to this:Can you #define a comment in C?

Is there a clever macro trick that will hide/remove this assignment, that does not put a local_var on the stack? I feel like there is something possible with a ternary but i cant think of it.

EDIT minimal example code:

source1.c

int var_name = 0;

source1.h

#ifdef CUST1
extern int var_name;
#else
// clever macro
#endif

source2.c

#include "source1.h"
int main(){
  var_name = 1;
  return 0;
}

Upvotes: 3

Views: 561

Answers (3)

caf
caf

Reputation: 239171

If the code accessing var_name in source2.c is unreachable when CUST1 is not defined (as you seem to say), then you can define it like this:

#include <stdlib.h>

#define var_name (*(abort(), (type *)0))

This will work syntactically as an lvalue (or rvalue) of the correct type, but will abort the program if it's actually ever executed.

Upvotes: 1

0___________
0___________

Reputation: 67721

extern int varname;

#define varname __attribute__((unused)) int varname

int square(int num) {
    varname = 1;
    return num * num;
}

It will work at any optimization level except the -O0

https://godbolt.org/g/phssNn

Upvotes: 1

alinarezrangel
alinarezrangel

Reputation: 21

There is no direct way of hiding an assignment as you propose, but there are some alternatives:

  1. You could use another macro:

    #ifdef CUST1
    extern type var_name;
    /* Do the real assignment */
    #define SET_VAR_TO(val) do{ var_name = (val); } while(0)
    #else
    /* Just evaluate for the side-effects */
    #define SET_VAR_TO(val) do { val; } while(0)
    #endif
    

    And then inside source2.c replace all assignments to var_name with SET_VAR_TO(value) like:

    int foo(void) {
        /* Replace
        var_name = bar();
        * With: */
        SET_VAR_TO(bar());
    }
    
  2. You could test if CUST1 is defined also in source2.c:

    /* in source2.c */
    int foo(void) {
    #ifdef CUST1
    var_name = bar();
    #endif
    }
    

    In this case you could even wrap the assignment to a real function only defined in the source file:

    /* in source2.c */
    int set_var_to(type value) {
    #ifdef CUST1
    var_name = value;
    #endif
    }
    
    int foo(void) {
        /* Replace
        var_name = bar();
        * With: */
        set_var_to(bar());
    }
    

As you prefer to not duplicate code by wrapping each assignment inside an #ifdef CUST1 ... #endif you could use the function or the macro. Remember that the option #1 will also expose the macro SET_VAR_TO to any file that #includes source1.h, not just source2.c.

Upvotes: 1

Related Questions