Advanced macro technology

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

Here's all I know about macro substitution.

 

If I have

#define abc xyz

and I have a name abc in my program, it will be changed to xyz.

 

But I want to do something else.  If I have a name abcXdef in my program, I want to use

#define X Y

and have abcXdef changed to abcYdef.

 

Is there a way to do this that isn't so obtuse that only an insane person could understand it?

 

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

C/C++ preprocessor can't do such things. You can create macro that takes argument Y and builds abcYdef, but you can't create macro that substitutes abcXdef with abcYdef.

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

Okay, I kind of thought that was the case.  That could be okay though.

 

So if the user simply used

#define X Y

is there a "helper" macro that the user doesn't need to see that would do the job?

 

 

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

Have a look at the preprocessors concatenation operator!

 

https://gcc.gnu.org/onlinedocs/c...

"He used to carry his guitar in a gunny sack, or sit beneath the tree by the railroad track. Oh the engineers would see him sitting in the shade, Strumming with the rhythm that the drivers made. People passing by, they would stop and say, "Oh, my, what that little country boy could play!" [Chuck Berry]

 

"Some questions have no answers."[C Baird] "There comes a point where the spoon-feeding has to stop and the independent thinking has to start." [C Lawson] "There are always ways to disagree, without being disagreeable."[E Weddington] "Words represent concepts. Use the wrong words, communicate the wrong concept." [J Morin] "Persistence only goes so far if you set yourself up for failure." [Kartman]

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

This is starting to sound like an XY problem.

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

Perhaps if you explained specifically what you want to do... ???

David (aka frog_jr)

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0
// Helper macros
#define TOKEN_CATENATE_HELPER(A, B) A ## B
#define TOKEN_CATENATE(A, B) TOKEN_CATENATE_HELPER(A, B)

// Customer-defined part
#define X Y

// Your macro, putting it all together
#define YOURMACRO TOKEN_CATENATE(abc, TOKEN_CATENATE(X, def))

int main(void) {
	YOURMACRO;
	while(1);
}

produces this pre-processed source:

int main(void) {
 abcYdef;
 while(1);
}

 

The "two-stage" macros are necessary, or this will not work. The problem is recurring and you'll find many discussions on the web (previous threads here on 'freaks, StackOverflow etc...) now that you know what to look for. Example: https://stackoverflow.com/questi... .

 

All this assuming you want to manipulate something that is to be used as an identifier in your code.

 

If you're after putting together a string literal, then things change.

 


 

I know it's a despicable question, but still: What are you actually want to accomplish here? Reason for asking is that macros, when going for "advanced use" like the above, are error prone and can lead to hellish debugging experiences. Maybe we can find a better way to solve the original problem..c

"He used to carry his guitar in a gunny sack, or sit beneath the tree by the railroad track. Oh the engineers would see him sitting in the shade, Strumming with the rhythm that the drivers made. People passing by, they would stop and say, "Oh, my, what that little country boy could play!" [Chuck Berry]

 

"Some questions have no answers."[C Baird] "There comes a point where the spoon-feeding has to stop and the independent thinking has to start." [C Lawson] "There are always ways to disagree, without being disagreeable."[E Weddington] "Words represent concepts. Use the wrong words, communicate the wrong concept." [J Morin] "Persistence only goes so far if you set yourself up for failure." [Kartman]

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

JohanEkdahl wrote:
What are you actually want to accomplish here?

That was my first thought on reading:

steve17 wrote:
Is there a way to do this (sic) that isn't so obtuse that only an insane person could understand it?

Let's go back to what, exactly, "this" is 

 

 

JohanEkdahl wrote:
macros, when going for "advanced use" like the above, are error prone and can lead to hellish debugging experiences.

No kidding!!

And lead to things which certainly are very obtuse indeed!

 

surprise

 

Maybe we can find a better way to solve the original problem..

+1

 

 

 

EDIT

 

fix quote

 

Last Edited: Thu. Oct 26, 2017 - 06:15 PM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 1

I'd just use Python to generate the C. Much easier and it's a full programming language so you can achieve anything.

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

The availability of .i files will generally make errors from macro mishaps much less mysterious.

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

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

clawson wrote:
I'd just use Python to generate the C.

I've heard of the "million monkeys" and producing literary works:

The infinite monkey theorem states that a monkey hitting keys at random on a typewriter keyboard for an infinite amount of time will almost surely type a given text, such as the complete works of William Shakespeare. In fact the monkey would almost surely type every possible finite text an infinite number of times. ...

Can you estimate how many pythons would be needed to do my C programming?

 

[I think I know the answer:  "Only one; his name is Monty."]

You can put lipstick on a pig, but it is still a pig.

I've never met a pig I didn't like, as long as you have some salt and pepper.

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

clawson wrote:
I'd just use Python to generate the C. 

I wish Atmel had done that with the ASF for the SAM Ds!

 

Instead, they play a whole load of nasty preprocessor macro trick  - leading to all the evils noted in #8 !

 

angry

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

I want to use the following macro to replace abcXdef with abcYdef where abc and def can be long strings with parentheses etc.  And I want it simple so it doesn't drive me crazy.

 

#define X Y

 

The way I do it now is sufficient without macros.  I just wondered if there was some simple magic I could employ, but I doubt it.  Thanks anyway.

 

 

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

I'm not sure this is what you're after, but it works for me:

 


// conf.h:
#define CONF_MT8870_TC_NUM	    1

---------------------------------------------------------------------

// tc16.h:
#include "tpaste.h"
#define TC_COMPA_vect(n)        ATPASTE3(TIMER, n, _COMPA_vect)
#define TC_COMPB_vect(n)        ATPASTE3(TIMER, n, _COMPB_vect)
#define TC_CAPT_vect(n)         ATPASTE3(TIMER, n, _CAPT_vect)
#define TC_OVF_vect(n)          ATPASTE3(TIMER, n, _OVF_vect)
#define TC_TIFR(n)              TPASTE2(TIFR, n)
#define TC_TIMSK(n)             TPASTE2(TIMSK, n)
#define TC_TIMER(n)             TPASTE2(TIMER, n)
#define TC(n)                   TPASTE2(TC_, n)

---------------------------------------------------------------------

// dtmf_mt8870.c:
#include "tc16.h"
#include "conf.h"
#define MT8870_CAPT_vect        TC_CAPT_vect(CONF_MT8870_TC_NUM)
#define MT8870_COMPA_vect       TC_COMPA_vect(CONF_MT8870_TC_NUM)
#define MT8870_COMPB_vect       TC_COMPB_vect(CONF_MT8870_TC_NUM)
#define MT8870_OVF_vect         TC_OVF_vect(CONF_MT8870_TC_NUM)
#define MT8870_TIFR             TC_TIFR(CONF_MT8870_TC_NUM)
#define MT8870_TIMER            TC(CONF_MT8870_TC_NUM)
#define MT8870_TIMSK            TC_TIMSK(CONF_MT8870_TC_NUM)
    .
    .
    .
    .
    .
    .
    .
ISR(MT8870_CAPT_vect)	// expands to (TIMER1_CAPT_vect)
{
	.
	.
	.
}


 

I don't remember why some of these use TPASTE (token paste) while others user ATPASTE (Absolute Token Paste).

 

Greg Muth

Portland, OR, US

Atmel Studio 7.0 on Windows 10

Xplained/Pro/Mini Boards mostly

 

 

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

steve17 wrote:
I want to use the following macro to replace abcXdef with abcYdef where abc and def can be long strings with parentheses etc.  And I want it simple so it doesn't drive me crazy.   #define X Y

 

Given you accept that there is no preprocessor functionality to replace  something in an existing symbol, but that you accept to build your final symbol from its part (seems so from #3): Didn't I show you exactly that in #7?

 

No, it can not be done with macros any way more easy than how I showed in #7. And if you accept that "it just works" then Note that the TOKEN_CATENATE_HELPER and TOKEN_CATENATE macros are generic/general and can be used for several of your specific macros.

 

Let's say you have code that writes to a match register in a timer. The general format of the name of such a register is OCRnx, where n is the timer number (1, 2, 3...), and x is the channel (A, B, C...). Your code writes values to channel A and B, but you want the user to be able to select which timer by its number.

 

user.h:

#define USER_TIMER 2

your code, e.g in main.c:

#include "user.h"

// Helper macros
#define TOKEN_CATENATE_HELPER(A, B) A ## B
#define TOKEN_CATENATE(A, B) TOKEN_CATENATE_HELPER(A, B)


// Channel A compare register, on timer chosen in user.h
#define OCR_A TOKEN_CATENATE(OCR, TOKEN_CATENATE(USER_TIMER, A))

// Channel B compare register, on timer chosen in user.h
#define OCR_B TOKEN_CATENATE(OCR, TOKEN_CATENATE(USER_TIMER, B))

   .
   .
   .
   
int main(void) {
   .
   .
   OCR_A = 123;     // After preprocessing, effectively OCR2A = ...
   OCR_B = 321;     // After preprocessing, effectively OCR2B = ...
   .
   .
   .
}

 

At another time, in another project, you have an identical main.c but the user.h now has

#define USER_TIMER 4

 

and the expansion will be

 

   OCR_A = 123;     // After preprocessing, effectively OCR4A = ...
   OCR_B = 321;     // After preprocessing, effectively OCR4B = ...

 

Still too complicated? Perhaps. the 

#define OCR_A TOKEN_CATENATE(OCR, TOKEN_CATENATE(TIMER_TO_USE, A))

is a wee bit convoluted, but you could of-course

// Helper macros
#define TOKEN_CATENATE_HELPER(A, B) A ## B
#define TOKEN_CATENATE_3(A, B, C) TOKEN_CATENATE_HELPER(A, TOKEN_CATENATE_HELPER(B, C))


// Channel A compare register, on timer chosen in user.h
#define OCR_A TOKEN_CATENATE_3(OCR, USER_TIMER, A)

// Channel B compare register, on timer chosen in user.h
#define OCR_B TOKEN_CATENATE_3(OCR, USER_TIMER, B)

Caveat Emptor: Sketchy and untested! While this approach should work, it's late and I didn't bother to actually test anything in this post. Typos and minor mistakes might occur. I'm off to hit the sack..

 

In real life you would of-course have the generic CATENATE macros in a separate header file..

"He used to carry his guitar in a gunny sack, or sit beneath the tree by the railroad track. Oh the engineers would see him sitting in the shade, Strumming with the rhythm that the drivers made. People passing by, they would stop and say, "Oh, my, what that little country boy could play!" [Chuck Berry]

 

"Some questions have no answers."[C Baird] "There comes a point where the spoon-feeding has to stop and the independent thinking has to start." [C Lawson] "There are always ways to disagree, without being disagreeable."[E Weddington] "Words represent concepts. Use the wrong words, communicate the wrong concept." [J Morin] "Persistence only goes so far if you set yourself up for failure." [Kartman]

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

steve17 wrote:
I want to use the following macro to replace abcXdef with abcYdef 

Yes - you said that.

 

But it's not clear why you want to do that.

 

What does it actually achieve? What is the goal?

 

If we knew that, as Johan suggested, we might be able to offer appropriate suggestions...

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

As I say, Python:

C:\SysGCC\avr\bin>type avr.c
#include <avr/io.h>

char text[] = "abcXdef";

int main(void) {
}

C:\SysGCC\avr\bin>replace.py avr.c
#include <avr/io.h>

char text[] = "abcYdef";

int main(void) {
}

The astronomically complex Python that did this?...

C:\SysGCC\avr\bin>type replace.py
import sys

if len(sys.argv) != 2:
        print("Give .c filename")
        sys.exit()

with open(sys.argv[1]) as file:
        data = file.read()
        data = data.replace("X", "Y")
        print(data)

Most of that is just about getting the file name as argv[1]. The "core" is that with statement that opened (then later closed) the file and the .replace() method that will replace one piece of text with another.

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

Will that work, Cliff?

 

It'll change every X to Y, even when its not occurring in "abcXdef".

 

Substitution using regular expression might be more appropriate?

"He used to carry his guitar in a gunny sack, or sit beneath the tree by the railroad track. Oh the engineers would see him sitting in the shade, Strumming with the rhythm that the drivers made. People passing by, they would stop and say, "Oh, my, what that little country boy could play!" [Chuck Berry]

 

"Some questions have no answers."[C Baird] "There comes a point where the spoon-feeding has to stop and the independent thinking has to start." [C Lawson] "There are always ways to disagree, without being disagreeable."[E Weddington] "Words represent concepts. Use the wrong words, communicate the wrong concept." [J Morin] "Persistence only goes so far if you set yourself up for failure." [Kartman]

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

It'll change every X to Y, even when its not occurring in "abcXdef".

AFAICS that is what the specification in #1 suggested.

 

But yeah, the way I'd actually do it is something more like:

C:\SysGCC\avr\bin>type avr.c.template
#include <avr/io.h>

char text[] = "abc$PLACEHOLDER$def";

int main(void) {
        char c;
        c = '$PLACEHOLDER$';
}

C:\SysGCC\avr\bin>replace.py avr.c.template
#include <avr/io.h>

char text[] = "abcYdef";

int main(void) {
        char c;
        c = 'Y';
}

which obviously uses:

import sys

if len(sys.argv) != 2:
	print("Give .c filename")
	sys.exit()
	
with open(sys.argv[1]) as file:
	data = file.read()
	data = data.replace("$PLACEHOLDER$", "Y")
	print(data)

 

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

 

Well I came up with something better than the way I originally did it.  I think it's pretty good.  Like others, I try to avoid macros but for configuring software so it can run on different boards they seem to be a good idea.

 

Some configuration is easy.  For example specifying the port and pin where there is a LED or pushbutton.  But my task was more complicated.  I want to export the system clock, actually the related peripheral clock, divided down so my DMM can display the frequency, on a pin of my choice.  This is more difficult to configure than for a LED or pushbutton because a counter/timer is involved.  Actually 2 counter/timers for each port.  One uses the low order 4 pins and the other uses the high order pins.

 

I use macros, but only simple ones.  Even I can understand them.  This is what I came up with.  First the configuration header file:

 


#define EXPORT_CLOCK_PORT_C
#define EXPORT_CLOCK_PIN_NUMBER 4
#define EXPORT_CLOCK_DIVISOR  100

 

Then the code that uses it:

 


#pragma once

#include "myTypes.h"
#include "configure_export_clock.h"
#include "counter_regs.h"

// **** NOTE:  To measure PER clock accurately, it must not stop.  The only CPU sleep must be idle sleep.

class Export_divided_per_clock   {
public:
   Export_divided_per_clock()  {

      Counter_regs* counter_regs_p;
      u_int8 port_output_pin;

      #if EXPORT_CLOCK_PIN > 7
         #error EXPORT_CLOCK_PIN > 7
      #endif

      port_output_pin = 1 << EXPORT_CLOCK_PIN_NUMBER;

      #ifdef EXPORT_CLOCK_PORT_C
         #include "port_c_regs.h"
         (new Port_c_regs)->Set_pins_direction_out(port_output_pin);
         #if EXPORT_CLOCK_PIN_NUMBER < 4
            #include "counter_c0_regs.h"
            counter_regs_p = new Counter_c0_regs;
            port_output_pin <<= 4;
         #else
            #include "counter_c1_regs.h"
            counter_regs_p = new Counter_c1_regs;
         #endif

      #elif defined(EXPORT_CLOCK_PORT_D)
         #include "port_d_regs.h"
         (new Port_d_regs)->Set_pins_direction_out(port_output_pin);
         #if EXPORT_CLOCK_PIN_NUMBER < 4
            #include "counter_d0_regs.h"
            counter_regs_p = new Counter_d0_regs;
            port_output_pin <<= 4;
         #else
            #include "counter_d1_regs.h"
            counter_regs_p = new Counter_d1_regs;
         #endif

      #elif defined(EXPORT_CLOCK_PORT_E)
         #include "port_e_regs.h"
         (new Port_e_regs)->Set_pins_direction_out(port_output_pin);
         #if EXPORT_CLOCK_PIN_NUMBER < 4
            #include "counter_e0_regs.h"
            counter_regs_p = new Counter_e0_regs;
            port_output_pin <<= 4;
         #else
            #include "counter_e1_regs.h"
            counter_regs_p = new Counter_e1_regs;
         #endif

      #elif defined(EXPORT_CLOCK_PORT_F)
         #include "port_f_regs.h"
         (new Port_f_regs)->Set_pins_direction_out(port_output_pin);
         #if EXPORT_CLOCK_PIN_NUMBER < 4
            #include "counter_f0_regs.h"
            counter_regs_p = new Counter_f0_regs;
            port_output_pin <<= 4;
         #else
            #include "counter_f1_regs.h"
            counter_regs_p = new Counter_f1_regs;
         #endif

      #else
         return;
      #endif

      counter_regs_p->Set_mode(Counter_regs::Frequency);

      counter_regs_p->Set_compare_or_capture_buffer_a(EXPORT_CLOCK_DIVISOR /2 -1);

      counter_regs_p->Set_freq_or_PWM_output_pin(port_output_pin);

      counter_regs_p->Set_clock(Counter_regs::Div_by_1);
      }

   };

 

Last Edited: Fri. Oct 27, 2017 - 11:29 PM