#define One definition based upon another

Go To Last Post
15 posts / 0 new
Author
Message
#1
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

For example, if I have the following defines:

#define TACH1_INT   INT4
#define TACH1_VECT  INT4_vect

and in my code I use them as:

board_init() {
    . . .
    EIMSK |= (1<<TACH1_INT);
    . . .
}

ISR(TACH1_VECT) {
    . . .
}

Is there a way to define TACH1_VECT based upon the define of TACH1_INT (e.g. using concatenation) so that just modifying TACH1_INT to INT6 would update the TACH1_VECT without having to actually edit the TACH1_VECT definition?

 

David (aka frog_jr)

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Use ## to "glue" the 4 or 6 onto the end of the other text. 

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

My understanding of the # # is limited, but I had thought:

#define TACH1_INT		INT4
#define TACH1_VECT_(x)	INT ## x ## _vect			
#define TACH1_VECT		TACH1_VECT_(TACH1_INT)

might get me there; however AS gives the error

Warning	1	'INTINT4_vect' appears to be a misspelled signal handler [enabled by default]

I thought the preprocessor would expand TACH1_INT to "4" (since  "#define INT4   4"), so I am obviously missing something.

David (aka frog_jr)

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Oh, and

#define TACH1_INT		INT4
#define TACH1_vect_(x)	x ## _vect			
#define TACH1_vect		TACH1_vect_(TACH1_INT)

gives me

'TACH1_INT_vect' appears to be a misspelled ...

 

David (aka frog_jr)

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

I am really confused! Just for grins I tried:

#define TACH1_INT		INT4
#define TACH1_vect__(x)	        INT ## x ## _vect
#define TACH1_vect_(x)	        TACH1_vect__(x)			
#define TACH1_vect		TACH1_vect_(TACH1_INT)

And it works!

I really need a good tutorial on concatenation expansion...

David (aka frog_jr)

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

frog_jr wrote:
I really need a good tutorial on concatenation expansion...

I think the term you need to google is "Token Pasting" ...

Top Tips:

  1. How to properly post source code - see: https://www.avrfreaks.net/comment... - also how to properly include images/pictures
  2. "Garbage" characters on a serial terminal are (almost?) invariably due to wrong baud rate - see: https://learn.sparkfun.com/tutorials/serial-communication
  3. Wrong baud rate is usually due to not running at the speed you thought; check by blinking a LED to see if you get the speed you expected
  4. Difference between a crystal, and a crystal oscillatorhttps://www.avrfreaks.net/comment...
  5. When your question is resolved, mark the solution: https://www.avrfreaks.net/comment...
  6. Beginner's "Getting Started" tips: https://www.avrfreaks.net/comment...
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

My idea was simpler:

#define num  4

Then concatenate that with INT and INTnum_vect

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Thanks awneil, I found this which I had not realized previously:

"Macro expansion is not performed on the argument prior to replacement."

Is what I have done the correct/best way to do this? (It seems rather obtuse.)

David (aka frog_jr)

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

@clawson

Sorry to be so dense; however, I do not understand how I am to use

#define num 4

differently than the already defined

#define INT4 4

 

David (aka frog_jr)

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

frog_jr wrote:

@clawson

Sorry to be so dense; however, I do not understand how I am to use

#define num 4

differently than the already defined

#define INT4 4

 

There is no benefit if all you want is just the INT4 define.

 

But I thought you point was that you wanted to be able to create both INTx and INTx_VECT from one definition of x ?

Top Tips:

  1. How to properly post source code - see: https://www.avrfreaks.net/comment... - also how to properly include images/pictures
  2. "Garbage" characters on a serial terminal are (almost?) invariably due to wrong baud rate - see: https://learn.sparkfun.com/tutorials/serial-communication
  3. Wrong baud rate is usually due to not running at the speed you thought; check by blinking a LED to see if you get the speed you expected
  4. Difference between a crystal, and a crystal oscillatorhttps://www.avrfreaks.net/comment...
  5. When your question is resolved, mark the solution: https://www.avrfreaks.net/comment...
  6. Beginner's "Getting Started" tips: https://www.avrfreaks.net/comment...
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

frog_jr wrote:
It seems rather obtuse.

Yes, that is the trouble with trying to play "clever tricks" with the preprocessor!

 

frown

 

I know it sounds like a nice idea at first glance - but I remain unconvinced that it's really worth it in the end ...

 

ASF does a lot of this stuff, btw.

Top Tips:

  1. How to properly post source code - see: https://www.avrfreaks.net/comment... - also how to properly include images/pictures
  2. "Garbage" characters on a serial terminal are (almost?) invariably due to wrong baud rate - see: https://learn.sparkfun.com/tutorials/serial-communication
  3. Wrong baud rate is usually due to not running at the speed you thought; check by blinking a LED to see if you get the speed you expected
  4. Difference between a crystal, and a crystal oscillatorhttps://www.avrfreaks.net/comment...
  5. When your question is resolved, mark the solution: https://www.avrfreaks.net/comment...
  6. Beginner's "Getting Started" tips: https://www.avrfreaks.net/comment...
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

awneil wrote:
but I remain unconvinced that it's really worth it in the end ...

I agree. I originally was trying to make the code "easier" to maintain for the next bloke that gets this project; however, adding 3 lines of macros to replace 1 is not my idea of "easier".

David (aka frog_jr)

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

So long as you keep the naming convention consistent, it's probably just as easy to do a global search & replace ... ?

Top Tips:

  1. How to properly post source code - see: https://www.avrfreaks.net/comment... - also how to properly include images/pictures
  2. "Garbage" characters on a serial terminal are (almost?) invariably due to wrong baud rate - see: https://learn.sparkfun.com/tutorials/serial-communication
  3. Wrong baud rate is usually due to not running at the speed you thought; check by blinking a LED to see if you get the speed you expected
  4. Difference between a crystal, and a crystal oscillatorhttps://www.avrfreaks.net/comment...
  5. When your question is resolved, mark the solution: https://www.avrfreaks.net/comment...
  6. Beginner's "Getting Started" tips: https://www.avrfreaks.net/comment...
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

awneil wrote:
So long as you keep the naming convention consistent, it's probably just as easy to do a global search & replace ... ?
A better solution is to keep all the related #define-s next to each other.

Even if the single point of truth is klunky,

one should be able to see the entire point at once.

 

The trick to making token pasting games work the way one often wants is to add a level.

#define paste2(a,b) a ## b
#define paste3(a,b,c) a ## b ## c

#define INT(x) paste2(INT,x)
#define INT_vect(x) paste3(INT, x, _vect)

#define y 4

float INT(y) ;
double INT_vect(y) ;

 

Edit: For a one-off, token-pasting games are likely not to be worth the effort.

That said, when they fail, they generally fail loudly.

One need not worry about subtle token-pasting errors.

International Theophysical Year seems to have been forgotten..
Anyone remember the song Jukebox Band?

Last Edited: Tue. May 3, 2016 - 05:38 PM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

frog_jr wrote:
Thanks awneil, I found this which I had not realized previously:

"Macro expansion is not performed on the argument prior to replacement."

Is what I have done the correct/best way to do this? (It seems rather obtuse.)

The same issue exists for stringification, and is well documented here:

https://gcc.gnu.org/onlinedocs/cpp/Stringification.html

If you want to stringify the result of expansion of a macro argument, you have to use two levels of macros.

     #define xstr(s) str(s)
     #define str(s) #s
     #define foo 4
     str (foo)
          ==> "foo"
     xstr (foo)
          ==> xstr (4)
          ==> str (4)
          ==> "4"

sis stringified when it is used in str, so it is not macro-expanded first. But s is an ordinary argument to xstr, so it is completely macro-expanded before xstr itself is expanded (see Argument Prescan). Therefore, by the time str gets to its argument, it has already been macro-expanded.

 

The issue is also examined here:

https://gcc.gnu.org/onlinedocs/cpp/Argument-Prescan.html

3.10.6 Argument Prescan

 

Macro arguments are completely macro-expanded before they are substituted into a macro body, unless they are stringified or pasted with other tokens. After substitution, the entire macro body, including the substituted arguments, is scanned again for macros to be expanded. The result is that the arguments are scanned twice to expand macro calls in them.

 

Most of the time, this has no effect. If the argument contained any macro calls, they are expanded during the first scan. The result therefore contains no macro calls, so the second scan does not change it. If the argument were substituted as given, with no prescan, the single remaining scan would find the same macro calls and produce the same results.

 

You might expect the double scan to change the results when a self-referential macro is used in an argument of another macro (see Self-Referential Macros): the self-referential macro would be expanded once in the first scan, and a second time in the second scan. However, this is not what happens. The self-references that do not expand in the first scan are marked so that they will not expand in the second scan either.

 

You might wonder, “Why mention the prescan, if it makes no difference? And why not skip it and make the preprocessor faster?” The answer is that the prescan does make a difference in three special cases:

  • Nested calls to a macro.

    We say that nested calls to a macro occur when a macro's argument contains a call to that very macro. For example, if f is a macro that expects one argument, f (f (1)) is a nested pair of calls to f. The desired expansion is made by expanding f (1) and substituting that into the definition of f. The prescan causes the expected result to happen. Without the prescan, f (1) itself would be substituted as an argument, and the inner use of f would appear during the main scan as an indirect self-reference and would not be expanded.

  • Macros that call other macros that stringify or concatenate.

    If an argument is stringified or concatenated, the prescan does not occur. If you want to expand a macro, then stringify or concatenate its expansion, you can do that by causing one macro to call another macro that does the stringification or concatenation. For instance, if you have

              #define AFTERX(x) X_ ## x
              #define XAFTERX(x) AFTERX(x)
              #define TABLESIZE 1024
              #define BUFSIZE TABLESIZE
    

    then AFTERX(BUFSIZE) expands to X_BUFSIZE, and XAFTERX(BUFSIZE) expands to X_1024. (Not to X_TABLESIZE. Prescan always does a complete expansion.)

  • Macros used in arguments, whose expansions contain unshielded commas.

    This can cause a macro expanded on the second scan to be called with the wrong number of arguments. Here is an example:

              #define foo  a,b
              #define bar(x) lose(x)
              #define lose(x) (1 + (x))
    

    We would like bar(foo) to turn into (1 + (foo)), which would then turn into (1 + (a,b)). Instead, bar(foo) expands into lose(a,b), and you get an error because lose requires a single argument. In this case, the problem is easily solved by the same parentheses that ought to be used to prevent misnesting of arithmetic operations:

              #define foo (a,b)
    
    or
    
              #define bar(x) lose((x))
    

    The extra pair of parentheses prevents the comma in foo's definition from being interpreted as an argument separator.

 

Here are some macros I keep handy:

/// \brief Concatenate the parameters \a a and \a b to form one token.
///
/// If \a a or \a b are macros they are expanded first before concatenation.
///
/// \b Example:
///
/// \par
/// \code
/// #define FLAGS_REG 0
/// #define ERROR_FLAG 3
/// ...
///   CONCATENATE(GPIOR, FLAGS_REG) |= (1<<ERROR_FLAG);
/// \endcode
///
/// \par
/// This will expand to:
///
/// \par
/// \code
///   GPIOR0 = (1<<3);
/// \endcode
///
/// \par
/// Which will of course be further expanded as per the definition of GPIOR0.
///
/// \par External references:
///   - https://gcc.gnu.org/onlinedocs/cpp/Concatenation.html#Concatenation
///   - http://stackoverflow.com/questions/1489932
///
/// \hideinitializer
///
#ifdef __DOXYGEN__
  #define CONCATENATE(a, b)
#else
  #ifndef CONCATENATE
    #define CONCATENATE(a, b) CONCATENATE_(a, b)
    #ifndef CONCATENATE_
      #define CONCATENATE_(a, b) a ## b
    #else
      #error MACRO COLLISION. Macro 'CONCATENATE_' is already defined.
    #endif
  #else
    #error MACRO COLLISION. Macro 'CONCATENATE' is already defined.
  #endif
#endif

/// \brief Alias for macro #CONCATENATE().
///
/// \hideinitializer
///
#ifdef __DOXYGEN__
  #define CAT
#else
  #ifndef CAT
    #define CAT CONCATENATE
  #else
    #error MACRO COLLISION. Macro 'CAT' is already defined.
  #endif
#endif

#define CONCATENATE ( a,
       )   

 

Concatenate the parameters a and b to form one token.

If a or b are macros they are expanded first before concatenation.

 

Example:

 #define FLAGS_REG 0
 #define ERROR_FLAG 3
 ...
   CONCATENATE(GPIOR, FLAGS_REG) |= (1<<ERROR_FLAG);

This will expand to:

   GPIOR0 = (1<<3);

Which will of course be further expanded as per the definition of GPIOR0.

 

External references:

Definition at line 910 of file convenience.h.

"Experience is what enables you to recognise a mistake the second time you make it."

"Good judgement comes from experience.  Experience comes from bad judgement."

"Wisdom is always wont to arrive late, and to be a little approximate on first possession."

"When you hear hoofbeats, think horses, not unicorns."

"Fast.  Cheap.  Good.  Pick two."

"Read a lot.  Write a lot."

"We see a lot of arses on handlebars around here." - [J Ekdahl]

 

Last Edited: Wed. May 4, 2016 - 08:49 PM