Author Topic: [C preprocessor]  (Read 4273 times)

0 Members and 1 Guest are viewing this topic.

Offline SimonTopic starter

  • Global Moderator
  • *****
  • Posts: 17817
  • Country: gb
  • Did that just blow up? No? might work after all !!
    • Simon's Electronics
[C preprocessor]
« on: August 03, 2018, 01:43:27 pm »
when I write a #define does this come into action as soon as the sequence of characters is found anywhere or does the text need to be in isolation:

#define one 1
#define two 2
#define one_two 50

will one_two get replaced with 12 or 50 ?
 

Offline DJohn

  • Regular Contributor
  • *
  • Posts: 103
  • Country: gb
Re: [C preprocessor]
« Reply #1 on: August 03, 2018, 01:56:56 pm »
#define works on identifiers, not parts of them.  one_two is an identifier, so it will be replaced with 50 when it appears later.  If you #define a, that will only replace a when it appears on its own, not in (for example) abc
 

Online RoGeorge

  • Super Contributor
  • ***
  • Posts: 6203
  • Country: ro
Re: [C preprocessor]
« Reply #2 on: August 03, 2018, 02:02:46 pm »
one_two will be 50

You can always probe for it online: https://www.onlinegdb.com/online_c_compiler

Offline SimonTopic starter

  • Global Moderator
  • *****
  • Posts: 17817
  • Country: gb
  • Did that just blow up? No? might work after all !!
    • Simon's Electronics
Re: [C preprocessor]
« Reply #3 on: August 03, 2018, 02:16:13 pm »
Thank you
 

Offline rstofer

  • Super Contributor
  • ***
  • Posts: 9890
  • Country: us
Re: [C preprocessor]
« Reply #4 on: August 03, 2018, 02:28:42 pm »
A word of caution when using #define to alias arithmetic expressions (and constants), put the definition inside parentheses.
Code: [Select]
#define one (1)

That one works fine either way.  This one is a problem:
Code: [Select]
#define WIDTH 80+20

int a = WIDTH * 2; //expect a==200 but a==120

To actually get the anticipated 200, it needs to look like this:
Code: [Select]
#define WIDTH (80+20) // now the addition will happen first

int a = WIDTH * 2; //now you should get 200
 
The following users thanked this post: TomS_

Offline SimonTopic starter

  • Global Moderator
  • *****
  • Posts: 17817
  • Country: gb
  • Did that just blow up? No? might work after all !!
    • Simon's Electronics
Re: [C preprocessor]
« Reply #5 on: August 03, 2018, 02:34:52 pm »
Yes well i usually only use definitions for single numbers as constants, and yes a very good idea to put any calculation in parenthesis.
 

Offline NorthGuy

  • Super Contributor
  • ***
  • Posts: 3147
  • Country: ca
Re: [C preprocessor]
« Reply #6 on: August 03, 2018, 02:40:25 pm »
#define one 1
#define two 2
#define one_two 50

If you remove the underscore, it will not replace it neither:

Code: [Select]
#define one 1
#define two 2
#define one two 50

The last line wil not be treated as

Code: [Select]
#define 1 2 50

Rather, it is an attempt to re-define "one". According to C99, the attempt should fail. In real life, it depends on the compiler. So, you better avoid re-definitions.
« Last Edit: August 03, 2018, 03:30:55 pm by NorthGuy »
 

Offline newbrain

  • Super Contributor
  • ***
  • Posts: 1719
  • Country: se
Re: [C preprocessor]
« Reply #7 on: August 03, 2018, 02:51:03 pm »
You've already got the right answers, but I will add this:

It's possible to achieve the "join the expansion of the object-like macros" effect (i.e. get 12) through the use of C preprocessor ## operator:

Code: [Select]
#define one 1
#define two 2
#define onetwo 50

#define join( a, b ) a ## b
#define glue( a, b ) join( a, b )

static int k = join( one, two );  // j will be 50
static int i = glue( one, two );  // i will take the value 12

The explanation is in chapter 6.10.3.3 of the C99 standard, one of the most incomprehensible (at least to me  :-[) briefly:
  • ## joins two "preprocessing token".
  • join( a, b ) will then just join the literal arguments it's passed, 'one' and 'two', generating 'onetwo', an object-like macro defined as 50.
  • glue( a, b ) will call join, but its parameter 'one' and 'two' are subject to macro replacement, hence they will take the values '1' and '2', finally yielding 12.


@northguy:
A small nitpick: redefinition is, in fact, allowed if the replacement list (and spelling of the parameters for a function-like macro)  is the same.
Nandemo wa shiranai wa yo, shitteru koto dake.
 

Offline andyturk

  • Frequent Contributor
  • **
  • Posts: 895
  • Country: us
Re: [C preprocessor]
« Reply #8 on: August 03, 2018, 03:46:19 pm »

The explanation is in chapter 6.10.3.3 of the C99 standard, one of the most incomprehensible (at least to me  :-[) briefly:
  • ## joins two "preprocessing token".
  • join( a, b ) will then just join the literal arguments it's passed, 'one' and 'two', generating 'onetwo', an object-like macro defined as 50.
  • glue( a, b ) will call join, but its parameter 'one' and 'two' are subject to macro replacement, hence they will take the values '1' and '2', finally yielding 12.

That's a good example of why macros should be considered (an occasionally necessary) evil.
 
The following users thanked this post: hans

Offline rstofer

  • Super Contributor
  • ***
  • Posts: 9890
  • Country: us
Re: [C preprocessor]
« Reply #9 on: August 03, 2018, 03:50:08 pm »
You've already got the right answers, but I will add this:

It's possible to achieve the "join the expansion of the object-like macros" effect (i.e. get 12) through the use of C preprocessor ## operator:

As I watch in amazement of the expanded features of C, and particularly C++, I see a great future in K&R C.  Simple and clean.  Straight to the point!

There is no way in the world I would ever use the new features simply because 2 days later I would have no idea what I had done.  I want to be able to pick up my code years later and understand what I had in mind when I wrote it.

Good thing I have retired...



 

Offline SimonTopic starter

  • Global Moderator
  • *****
  • Posts: 17817
  • Country: gb
  • Did that just blow up? No? might work after all !!
    • Simon's Electronics
Re: [C preprocessor]
« Reply #10 on: August 03, 2018, 03:55:10 pm »

The explanation is in chapter 6.10.3.3 of the C99 standard, one of the most incomprehensible (at least to me  :-[) briefly:
  • ## joins two "preprocessing token".
  • join( a, b ) will then just join the literal arguments it's passed, 'one' and 'two', generating 'onetwo', an object-like macro defined as 50.
  • glue( a, b ) will call join, but its parameter 'one' and 'two' are subject to macro replacement, hence they will take the values '1' and '2', finally yielding 12.

That's a good example of why macros should be considered (an occasionally necessary) evil.

Why?

If you are sensible which is why am asking. With macro's i can write code that explains itself and all of my code in the top level files is macro based. I use descriptive macro names to hide anything that does stuff to registers. That way when i read it later it is clear what the hell is going on.

Any fixed numbers are replaced with definitions that again make them descriptive and easy to change in one location only. The last project i wrote could have the top level files transfer to an entire new chip if needed without any change needed.
 

Offline Kjelt

  • Super Contributor
  • ***
  • Posts: 6460
  • Country: nl
Re: [C preprocessor]
« Reply #11 on: August 03, 2018, 03:58:23 pm »
Just to add to your learning curve, if you define an integer constant like this

#define ONE (1)

The compiler sees it as a signed int. So a signed integer. If you want it to behave like an unsigned integer which can prevent certain errors in some cases (like overflowing), it is common practice to define it like:

#define ONE (1U)


 
The following users thanked this post: newbrain

Offline SimonTopic starter

  • Global Moderator
  • *****
  • Posts: 17817
  • Country: gb
  • Did that just blow up? No? might work after all !!
    • Simon's Electronics
Re: [C preprocessor]
« Reply #12 on: August 03, 2018, 04:07:22 pm »
well the sign is not a problem in itself other that limiting the range of positive numbers held. So does it automatically pick the variable length?
 

Offline helius

  • Super Contributor
  • ***
  • Posts: 3643
  • Country: us
Re: [C preprocessor]
« Reply #13 on: August 03, 2018, 04:09:51 pm »
Literal numbers are of type int unless suffixed by "L" or "UL". The number is converted using the "usual unary conversions" to the type of the variable if you perform an assignment.

There is also syntax for literal floats and chars, of course, and suffixes for different float types.
« Last Edit: August 03, 2018, 04:12:02 pm by helius »
 

Offline Cerebus

  • Super Contributor
  • ***
  • Posts: 10576
  • Country: gb
Re: [C preprocessor]
« Reply #14 on: August 03, 2018, 04:10:35 pm »

That's a good example of why macros should be considered (an occasionally necessary) evil.

Why?

If you are sensible which is why am asking. With macro's i can write code that explains itself and all of my code in the top level files is macro based. I use descriptive macro names to hide anything that does stuff to registers. That way when i read it later it is clear what the hell is going on.

Any fixed numbers are replaced with definitions that again make them descriptive and easy to change in one location only. The last project i wrote could have the top level files transfer to an entire new chip if needed without any change needed.

As has been shown, macros can have unintended consequences. If all you are using them for is to give magic numbers symbolic names there is a much better way.

Compare:

Code: [Select]
#define MAGIC_NUMBER 1234
versus

Code: [Select]
const unsigned int MAGIC_NUMBER = 1234;
or
Code: [Select]
#include <stdint.h>
const uint16_t MAGIC_NUMBER = 1234;

Equally as readable in the main body of code, but you've given the compiler some solid information about your intentions for the meaning of "1234".
Anybody got a syringe I can use to squeeze the magic smoke back into this?
 

Offline SimonTopic starter

  • Global Moderator
  • *****
  • Posts: 17817
  • Country: gb
  • Did that just blow up? No? might work after all !!
    • Simon's Electronics
Re: [C preprocessor]
« Reply #15 on: August 03, 2018, 04:17:57 pm »
OK so constants are better than macro's as they speak straight in "C" but I do many things in macro's:

Code: [Select]
#include <avr/io.h>

#define BIT_GET(p,m) ((p) & (BIT(m)))
#define BIT_S(p,m) ((p) |= (BIT(m)))
#define BIT_C(p,m) ((p) &= ~(BIT(m)))
#define BIT_FLIP(p,m) ((p) ^= (BIT(m))) // p is the register, m is the bit name
#define BIT_WRITE(c,p,m) (c ? BIT_S(p, BIT(m)) : BIT_C(p, BIT(m)))
#define BIT(x) (0x01 << (x))
#define LONGBIT(x) ((unsigned long)0x00000001 << (x))


#define BIT1(p,m) ((p) |= (BIT(m)))
#define BIT0(p,m) ((p) &= ~(BIT(m)))


// PortA setup and manipulation

#define PA0_INPUT PORTA.DIRCLR = 0x01
#define PA1_INPUT PORTA.DIRCLR = 0x02
#define PA2_INPUT PORTA.DIRCLR = 0x04
#define PA3_INPUT PORTA.DIRCLR = 0x08
#define PA4_INPUT PORTA.DIRCLR = 0x10
#define PA5_INPUT PORTA.DIRCLR = 0x20
#define PA6_INPUT PORTA.DIRCLR = 0x40
#define PA7_INPUT PORTA.DIRCLR = 0x80

#define IS_PA0_LOW !BIT_GET(PORTA.IN, 0)
#define IS_PA1_LOW !BIT_GET(PORTA.IN, 1)
#define IS_PA2_LOW !BIT_GET(PORTA.IN, 2)
#define IS_PA3_LOW !BIT_GET(PORTA.IN, 3)
#define IS_PA4_LOW !BIT_GET(PORTA.IN, 4)
#define IS_PA5_LOW !BIT_GET(PORTA.IN, 5)
#define IS_PA6_LOW !BIT_GET(PORTA.IN, 6)
#define IS_PA7_LOW !BIT_GET(PORTA.IN, 7)

#define IS_PA0_HI BIT_GET(PORTA.IN, 0)
#define IS_PA1_HI BIT_GET(PORTA.IN, 1)
#define IS_PA2_HI BIT_GET(PORTA.IN, 2)
#define IS_PA3_HI BIT_GET(PORTA.IN, 3)
#define IS_PA4_HI BIT_GET(PORTA.IN, 4)
#define IS_PA5_HI BIT_GET(PORTA.IN, 5)
#define IS_PA6_HI BIT_GET(PORTA.IN, 6)
#define IS_PA7_HI BIT_GET(PORTA.IN, 7)

#define PA0_OUTPUT PORTA.DIRSET = 0x01
#define PA1_OUTPUT PORTA.DIRSET = 0x02
#define PA2_OUTPUT PORTA.DIRSET = 0x04
#define PA3_OUTPUT PORTA.DIRSET = 0x08
#define PA4_OUTPUT PORTA.DIRSET = 0x10
#define PA5_OUTPUT PORTA.DIRSET = 0x20
#define PA6_OUTPUT PORTA.DIRSET = 0x40
#define PA7_OUTPUT PORTA.DIRSET = 0x80

#define PA0_HI PORTA.OUTSET = 0x01
#define PA1_HI PORTA.OUTSET = 0x02
#define PA2_HI PORTA.OUTSET = 0x04
#define PA3_HI PORTA.OUTSET = 0x08
#define PA4_HI PORTA.OUTSET = 0x10
#define PA5_HI PORTA.OUTSET = 0x20
#define PA6_HI PORTA.OUTSET = 0x40
#define PA7_HI PORTA.OUTSET = 0x80

#define PA0_LOW PORTA.OUTCLR = 0x01
#define PA1_LOW PORTA.OUTCLR = 0x02
#define PA2_LOW PORTA.OUTCLR = 0x04
#define PA3_LOW PORTA.OUTCLR = 0x08
#define PA4_LOW PORTA.OUTCLR = 0x10
#define PA5_LOW PORTA.OUTCLR = 0x20
#define PA6_LOW PORTA.OUTCLR = 0x40
#define PA7_LOW PORTA.OUTCLR = 0x80

// PORTA SLEW RATE

#define PORTA_SLEWRATE_ON  BIT1(PORTA.PORTCTRL, SRL)
#define PORTA_SLEWRATE_OFF BIT0(PORTA.PORTCTRL, SRL)

// PA0 setup
 
#define PA0_MODE_INVERTED     BIT1(PORTA.PIN0CTRL, INVEN)
#define PA0_MODE_NON_INVERTED BIT0(PORTA.PIN0CTRL, INVEN)

#define PA0_PULLUP_ON         BIT1(PORTA.PIN0CTRL, PULLUPEN)
#define PA0_PULLUP_OFF        BIT0(PORTA.PIN0CTRL, PULLUPEN)

#define PA0_INT_OFF           BIT0(PORTA.PIN0CTRL, ISC2);  BIT0(PORTA.PIN0CTRL, ICS1);  BIT0(PORTA.PIN0CTRL, ICS0);
#define PA0_INT_BOTHEDGES     BIT0(PORTA.PIN0CTRL, ISC2);  BIT0(PORTA.PIN0CTRL, ICS1);  BIT1(PORTA.PIN0CTRL, ICS0);
#define PA0_INT_RISING        BIT0(PORTA.PIN0CTRL, ISC2);  BIT1(PORTA.PIN0CTRL, ICS1);  BIT0(PORTA.PIN0CTRL, ICS0);
#define PA0_INT_FALLING       BIT0(PORTA.PIN0CTRL, ISC2);  BIT1(PORTA.PIN0CTRL, ICS1);  BIT1(PORTA.PIN0CTRL, ICS0);
#define PA0_INT_INPUT_DISABLE BIT1(PORTA.PIN0CTRL, ISC2);  BIT0(PORTA.PIN0CTRL, ICS1);  BIT0(PORTA.PIN0CTRL, ICS0);
#define PA0_INT_LEVEL         BIT1(PORTA.PIN0CTRL, ISC2);  BIT0(PORTA.PIN0CTRL, ICS1);  BIT1(PORTA.PIN0CTRL, ICS0);

// PA1 setup

#define PA1_MODE_INVERTED     BIT1(PORTA.PIN1CTRL, INVEN)
#define PA1_MODE_NON_INVERTED BIT0(PORTA.PIN1CTRL, INVEN)

#define PA1_PULLUP_ON         BIT1(PORTA.PIN1CTRL, PULLUPEN)
#define PA1_PULLUP_OFF        BIT0(PORTA.PIN1CTRL, PULLUPEN)

#define PA1_INT_OFF           BIT0(PORTA.PIN1CTRL, ISC2);  BIT0(PORTA.PIN1CTRL, ICS1);  BIT0(PORTA.PIN1CTRL, ICS0);
#define PA1_INT_BOTHEDGES     BIT0(PORTA.PIN1CTRL, ISC2);  BIT0(PORTA.PIN1CTRL, ICS1);  BIT1(PORTA.PIN1CTRL, ICS0);
#define PA1_INT_RISING        BIT0(PORTA.PIN1CTRL, ISC2);  BIT1(PORTA.PIN1CTRL, ICS1);  BIT0(PORTA.PIN1CTRL, ICS0);
#define PA1_INT_FALLING       BIT0(PORTA.PIN1CTRL, ISC2);  BIT1(PORTA.PIN1CTRL, ICS1);  BIT1(PORTA.PIN1CTRL, ICS0);
#define PA1_INT_INPUT_DISABLE BIT1(PORTA.PIN1CTRL, ISC2);  BIT0(PORTA.PIN1CTRL, ICS1);  BIT0(PORTA.PIN1CTRL, ICS0);
#define PA1_INT_LEVEL         BIT1(PORTA.PIN1CTRL, ISC2);  BIT0(PORTA.PIN1CTRL, ICS1);  BIT1(PORTA.PIN1CTRL, ICS0);

// PA2 setup

#define PA2_MODE_INVERTED     BIT1(PORTA.PIN2CTRL, INVEN)
#define PA2_MODE_NON_INVERTED BIT0(PORTA.PIN2CTRL, INVEN)

#define PA2_PULLUP_ON         BIT1(PORTA.PIN2CTRL, PULLUPEN)
#define PA2_PULLUP_OFF        BIT0(PORTA.PIN2CTRL, PULLUPEN)

#define PA2_INT_OFF           BIT0(PORTA.PIN2CTRL, ISC2);  BIT0(PORTA.PIN2CTRL, ICS1);  BIT0(PORTA.PIN2CTRL, ICS0);
#define PA2_INT_BOTHEDGES     BIT0(PORTA.PIN2CTRL, ISC2);  BIT0(PORTA.PIN2CTRL, ICS1);  BIT1(PORTA.PIN2CTRL, ICS0);
#define PA2_INT_RISING        BIT0(PORTA.PIN2CTRL, ISC2);  BIT1(PORTA.PIN2CTRL, ICS1);  BIT0(PORTA.PIN2CTRL, ICS0);
#define PA2_INT_FALLING       BIT0(PORTA.PIN2CTRL, ISC2);  BIT1(PORTA.PIN2CTRL, ICS1);  BIT1(PORTA.PIN2CTRL, ICS0);
#define PA2_INT_INPUT_DISABLE BIT1(PORTA.PIN2CTRL, ISC2);  BIT0(PORTA.PIN2CTRL, ICS1);  BIT0(PORTA.PIN2CTRL, ICS0);
#define PA2_INT_LEVEL         BIT1(PORTA.PIN2CTRL, ISC2);  BIT0(PORTA.PIN2CTRL, ICS1);  BIT1(PORTA.PIN2CTRL, ICS0);

// PA3 setup

#define PA3_MODE_INVERTED     BIT1(PORTA.PIN3CTRL, INVEN)
#define PA3_MODE_NON_INVERTED BIT0(PORTA.PIN3CTRL, INVEN)

#define PA3_PULLUP_ON         BIT1(PORTA.PIN3CTRL, PULLUPEN)
#define PA3_PULLUP_OFF        BIT0(PORTA.PIN3CTRL, PULLUPEN)

#define PA3_INT_OFF           BIT0(PORTA.PIN3CTRL, ISC2);  BIT0(PORTA.PIN3CTRL, ICS1);  BIT0(PORTA.PIN3CTRL, ICS0);
#define PA3_INT_BOTHEDGES     BIT0(PORTA.PIN3CTRL, ISC2);  BIT0(PORTA.PIN3CTRL, ICS1);  BIT1(PORTA.PIN3CTRL, ICS0);
#define PA3_INT_RISING        BIT0(PORTA.PIN3CTRL, ISC2);  BIT1(PORTA.PIN3CTRL, ICS1);  BIT0(PORTA.PIN3CTRL, ICS0);
#define PA3_INT_FALLING       BIT0(PORTA.PIN3CTRL, ISC2);  BIT1(PORTA.PIN3CTRL, ICS1);  BIT1(PORTA.PIN3CTRL, ICS0);
#define PA3_INT_INPUT_DISABLE BIT1(PORTA.PIN3CTRL, ISC2);  BIT0(PORTA.PIN3CTRL, ICS1);  BIT0(PORTA.PIN3CTRL, ICS0);
#define PA3_INT_LEVEL         BIT1(PORTA.PIN3CTRL, ISC2);  BIT0(PORTA.PIN3CTRL, ICS1);  BIT1(PORTA.PIN3CTRL, ICS0);

// PA4 setup

#define PA4_MODE_INVERTED     BIT1(PORTA.PIN4CTRL, INVEN)
#define PA4_MODE_NON_INVERTED BIT0(PORTA.PIN4CTRL, INVEN)

#define PA4_PULLUP_ON         BIT1(PORTA.PIN4CTRL, PULLUPEN)
#define PA4_PULLUP_OFF        BIT0(PORTA.PIN4CTRL, PULLUPEN)

#define PA4_INT_OFF           BIT0(PORTA.PIN4CTRL, ISC2);  BIT0(PORTA.PIN4CTRL, ICS1);  BIT0(PORTA.PIN4CTRL, ICS0);
#define PA4_INT_BOTHEDGES     BIT0(PORTA.PIN4CTRL, ISC2);  BIT0(PORTA.PIN4CTRL, ICS1);  BIT1(PORTA.PIN4CTRL, ICS0);
#define PA4_INT_RISING        BIT0(PORTA.PIN4CTRL, ISC2);  BIT1(PORTA.PIN4CTRL, ICS1);  BIT0(PORTA.PIN4CTRL, ICS0);
#define PA4_INT_FALLING       BIT0(PORTA.PIN4CTRL, ISC2);  BIT1(PORTA.PIN4CTRL, ICS1);  BIT1(PORTA.PIN4CTRL, ICS0);
#define PA4_INT_INPUT_DISABLE BIT1(PORTA.PIN4CTRL, ISC2);  BIT0(PORTA.PIN4CTRL, ICS1);  BIT0(PORTA.PIN4CTRL, ICS0);
#define PA4_INT_LEVEL         BIT1(PORTA.PIN4CTRL, ISC2);  BIT0(PORTA.PIN4CTRL, ICS1);  BIT1(PORTA.PIN4CTRL, ICS0);

// PA5 setup

#define PA5_MODE_INVERTED     BIT1(PORTA.PIN5CTRL, INVEN)
#define PA5_MODE_NON_INVERTED BIT0(PORTA.PIN5CTRL, INVEN)

#define PA5_PULLUP_ON         BIT1(PORTA.PIN5CTRL, PULLUPEN)
#define PA5_PULLUP_OFF        BIT0(PORTA.PIN5CTRL, PULLUPEN)

#define PA5_INT_OFF           BIT0(PORTA.PIN5CTRL, ISC2);  BIT0(PORTA.PIN5CTRL, ICS1);  BIT0(PORTA.PIN5CTRL, ICS0);
#define PA5_INT_BOTHEDGES     BIT0(PORTA.PIN5CTRL, ISC2);  BIT0(PORTA.PIN5CTRL, ICS1);  BIT1(PORTA.PIN5CTRL, ICS0);
#define PA5_INT_RISING        BIT0(PORTA.PIN5CTRL, ISC2);  BIT1(PORTA.PIN5CTRL, ICS1);  BIT0(PORTA.PIN5CTRL, ICS0);
#define PA5_INT_FALLING       BIT0(PORTA.PIN5CTRL, ISC2);  BIT1(PORTA.PIN5CTRL, ICS1);  BIT1(PORTA.PIN5CTRL, ICS0);
#define PA5_INT_INPUT_DISABLE BIT1(PORTA.PIN5CTRL, ISC2);  BIT0(PORTA.PIN5CTRL, ICS1);  BIT0(PORTA.PIN5CTRL, ICS0);
#define PA5_INT_LEVEL         BIT1(PORTA.PIN5CTRL, ISC2);  BIT0(PORTA.PIN5CTRL, ICS1);  BIT1(PORTA.PIN5CTRL, ICS0);

// PA6 setup

#define PA6_MODE_INVERTED     BIT1(PORTA.PIN6CTRL, INVEN)
#define PA6_MODE_NON_INVERTED BIT0(PORTA.PIN6CTRL, INVEN)

#define PA6_PULLUP_ON         BIT1(PORTA.PIN6CTRL, PULLUPEN)
#define PA6_PULLUP_OFF        BIT0(PORTA.PIN6CTRL, PULLUPEN)

#define PA6_INT_OFF           BIT0(PORTA.PIN6CTRL, ISC2);  BIT0(PORTA.PIN6CTRL, ICS1);  BIT0(PORTA.PIN6CTRL, ICS0);
#define PA6_INT_BOTHEDGES     BIT0(PORTA.PIN6CTRL, ISC2);  BIT0(PORTA.PIN6CTRL, ICS1);  BIT1(PORTA.PIN6CTRL, ICS0);
#define PA6_INT_RISING        BIT0(PORTA.PIN6CTRL, ISC2);  BIT1(PORTA.PIN6CTRL, ICS1);  BIT0(PORTA.PIN6CTRL, ICS0);
#define PA6_INT_FALLING       BIT0(PORTA.PIN6CTRL, ISC2);  BIT1(PORTA.PIN6CTRL, ICS1);  BIT1(PORTA.PIN6CTRL, ICS0);
#define PA6_INT_INPUT_DISABLE BIT1(PORTA.PIN6CTRL, ISC2);  BIT0(PORTA.PIN6CTRL, ICS1);  BIT0(PORTA.PIN6CTRL, ICS0);
#define PA6_INT_LEVEL         BIT1(PORTA.PIN6CTRL, ISC2);  BIT0(PORTA.PIN6CTRL, ICS1);  BIT1(PORTA.PIN6CTRL, ICS0);

// PA7 setup

#define PA7_MODE_INVERTED     BIT1(PORTA.PIN7CTRL, INVEN)
#define PA7_MODE_NON_INVERTED BIT0(PORTA.PIN7CTRL, INVEN)

#define PA7_PULLUP_ON         BIT1(PORTA.PIN7CTRL, PULLUPEN)
#define PA7_PULLUP_OFF        BIT0(PORTA.PIN7CTRL, PULLUPEN)

#define PA7_INT_OFF           BIT0(PORTA.PIN7CTRL, ISC2);  BIT0(PORTA.PIN7CTRL, ICS1);  BIT0(PORTA.PIN7CTRL, ICS0);
#define PA7_INT_BOTHEDGES     BIT0(PORTA.PIN7CTRL, ISC2);  BIT0(PORTA.PIN7CTRL, ICS1);  BIT1(PORTA.PIN7CTRL, ICS0);
#define PA7_INT_RISING        BIT0(PORTA.PIN7CTRL, ISC2);  BIT1(PORTA.PIN7CTRL, ICS1);  BIT0(PORTA.PIN7CTRL, ICS0);
#define PA7_INT_FALLING       BIT0(PORTA.PIN7CTRL, ISC2);  BIT1(PORTA.PIN7CTRL, ICS1);  BIT1(PORTA.PIN7CTRL, ICS0);
#define PA7_INT_INPUT_DISABLE BIT1(PORTA.PIN7CTRL, ISC2);  BIT0(PORTA.PIN7CTRL, ICS1);  BIT0(PORTA.PIN7CTRL, ICS0);
#define PA7_INT_LEVEL         BIT1(PORTA.PIN7CTRL, ISC2);  BIT0(PORTA.PIN7CTRL, ICS1);  BIT1(PORTA.PIN7CTRL, ICS0);



repeat for as many ports as necessary.
 

Offline helius

  • Super Contributor
  • ***
  • Posts: 3643
  • Country: us
Re: [C preprocessor]
« Reply #16 on: August 03, 2018, 04:22:14 pm »
The advantage of preprocessor definitions over const variables (shouldn't that be a contradiction?) is avoiding linker issues. If you #include a header files of #defines into multiple source files, no linker names are instantiated, but only literals in each file that will be coalesced by the linker.
On the other hand, if you place a line like
Code: [Select]
const unsigned int MAGIC_NUMBER = 1234;into several files and try to link them together, you will have linker errors because several object files are trying to add the same name to the executable. To solve this you would have to use an extern declaration in every file except the one that actually defines the variable. This is why variables (even const ones like your example) are not placed in .h files, even when they would be useful across multiple source files.

The const definition also requires that there be space in the data section allocated for the variable: since some part of the program might take its address, which can never happen to a literal.

A better solution and the one that is generally recommended as an alternative to #define constants is to use enums. You can combine several constants into a single enum declaration.

Code: [Select]
enum { MAGIC_NUMBER = 1234 };
 

Offline SimonTopic starter

  • Global Moderator
  • *****
  • Posts: 17817
  • Country: gb
  • Did that just blow up? No? might work after all !!
    • Simon's Electronics
Re: [C preprocessor]
« Reply #17 on: August 03, 2018, 04:42:27 pm »
enum?

my last project did infact end up with a C file and h file for all of the shared variables.
 

Offline newbrain

  • Super Contributor
  • ***
  • Posts: 1719
  • Country: se
Re: [C preprocessor]
« Reply #18 on: August 03, 2018, 06:16:04 pm »
There is no way in the world I would ever use the new features
Let's say newish  ;)

It's been there since C89, when the C preprocessor was given a major overhaul.
It went from being a blunt instrument, to being a blunt instrument with sharp edges  >:D

But I agree: these things, though powerful and standardized, should be used sparingly.

The advantage of preprocessor definitions over const variables (shouldn't that be a contradiction?) is avoiding linker issues. If you #include a header files of #defines into multiple source files, no linker names are instantiated, but only literals in each file that will be coalesced by the linker.
True, though the linker will never see the #defines, which are resolved even before the compiling phase starts.

On the other hand, if you place a line like
Code: [Select]
const unsigned int MAGIC_NUMBER = 1234;into several files and try to link them together, you will have linker errors because several object files are trying to add the same name to the executable. To solve this you would have to use an extern declaration in every file except the one that actually defines the variable. This is why variables (even const ones like your example) are not placed in .h files, even when they would be useful across multiple source files.

The const definition also requires that there be space in the data section allocated for the variable: since some part of the program might take its address, which can never happen to a literal.
Here I think C++ got it exactly right: internal linkage for const qualified names, and they can be used as constants at compile time.
This also helps getting rid of the associated storage, if possible.

Nandemo wa shiranai wa yo, shitteru koto dake.
 

Offline andyturk

  • Frequent Contributor
  • **
  • Posts: 895
  • Country: us
Re: [C preprocessor]
« Reply #19 on: August 03, 2018, 06:23:07 pm »
Why?

If you are sensible which is why am asking. With macro's i can write code that explains itself and all of my code in the top level files is macro based. I use descriptive macro names to hide anything that does stuff to registers. That way when i read it later it is clear what the hell is going on.

Any fixed numbers are replaced with definitions that again make them descriptive and easy to change in one location only. The last project i wrote could have the top level files transfer to an entire new chip if needed without any change needed.
It's largely a matter of style, I suppose. But I think if you can do something "in the language" directly, you ought to do it that way rather than relying on some preprocessing of the program text.

Others have already made the point about constant definitions. IMO, they're better done as language constants (type checked by the compiler, known to the debugger, namespaced in C++, etc.) than as #defines. Opinions differ on the subject and it's certainly possible to write code that works with lots of #defines.

How much you're *forced* to do in the preprocessor comes down to which language spec you're coding to. If you don't have typed enums, then maybe a #define really is your best option. All my projects are C++ these days, so I only have to use the preprocessor for a few specific things:

  • file inclusion
  • X-Macros
  • conditional compilation in implementation files (i.e., not headers)

I'd rather use language/compiler features for constants, "inline" functions, and abstractions intended to make code more portable.

YMMV.
 
The following users thanked this post: hans, newbrain

Offline nctnico

  • Super Contributor
  • ***
  • Posts: 26907
  • Country: nl
    • NCT Developments
Re: [C preprocessor]
« Reply #20 on: August 03, 2018, 07:08:29 pm »
when I write a #define does this come into action as soon as the sequence of characters is found anywhere or does the text need to be in isolation:

#define one 1
#define two 2
#define one_two 50

will one_two get replaced with 12 or 50 ?
One thing to remember is that the preprocessor does text search & replace. defines and macros are no more than (sophisticated) text search & replace tools. Besides the use for conditional compilation.
There are small lies, big lies and then there is what is on the screen of your oscilloscope.
 

Offline legacy

  • Super Contributor
  • ***
  • !
  • Posts: 4415
  • Country: ch
Re: [C preprocessor]
« Reply #21 on: August 03, 2018, 07:58:59 pm »
ergo, don't abuse the preprocessor's power  :D
 

Offline Kjelt

  • Super Contributor
  • ***
  • Posts: 6460
  • Country: nl
Re: [C preprocessor]
« Reply #22 on: August 04, 2018, 08:06:11 am »
In my previous company a very smart SW engineer replaced all the init code for the uC with X macros.
Great if it worked saved a lot of work, but if you made a small mistake it could take hours to find out since the x macros expanded over 10000 lines.
Another negative sideeffect it was unreadable and unmaintainable.
So yeah handle with care.
 

Offline tggzzz

  • Super Contributor
  • ***
  • Posts: 19517
  • Country: gb
  • Numbers, not adjectives
    • Having fun doing more, with less
Re: [C preprocessor]
« Reply #23 on: August 04, 2018, 08:52:21 am »
I'd rather use language/compiler features for constants, "inline" functions, and abstractions intended to make code more portable.

Would you use C++ templates to compute the sequence of prime numbers during compilation? (Naturally the compilation can never finish!)
Example: https://en.wikibooks.org/wiki/C%2B%2B_Programming/Templates/Template_Meta-Programming#History_of_TMP

But back to the original question...

#defines were are reasonable solution to technology limitations in the 70s, e.g. small memory size, early compiler technology, early language expressiveness. Those limitation are not significant any more, so those advantages are only of historic interest.

#defines have significant disadvantges, e.g. code readability and maintainability, "throwing away" information that could be used later in the compilation process and/or during debugging.

Yes, we all use them.
Yes, we've all been infuriated by other people's use of them.
There are lies, damned lies, statistics - and ADC/DAC specs.
Glider pilot's aphorism: "there is no substitute for span". Retort: "There is a substitute: skill+imagination. But you can buy span".
Having fun doing more, with less
 

Offline hans

  • Super Contributor
  • ***
  • Posts: 1641
  • Country: nl
Re: [C preprocessor]
« Reply #24 on: August 04, 2018, 08:57:25 am »
I despise macro's. I would only use them if I cannot find or write a suitable "just C/C++" code replacement that is as efficient.

Two examples:

I use macro's almost exclusively to play with the linker. I litter my code with assertion statements everywhere. The easiest way to remove these statements from final build is define assert(x) as empty. You could check if the NDEBUG macro is defined to switch the assert definition accordingly for debug and release builds.

On the other hand, I either inline bit set/clr operations or rather redirect them to dedicated functions than a macro. Okay, it's very slow if you run the code without optimizer. But you can single step through everything without having to switch to assembly views. With macro's the debugger may single step over all the macro's at once, or it will appear to make no progress while it is evaluating macro's underneath.

Functions are better supported "inside" the C language. Macro's are part of the preprocessor which happens before compilation and linking. It's nice that it is there, but I wouldn't use it to "write code" with.

Also, if you enable GCC compiler even to level 1 the aforementioned overhead problems largely disappear.
If your code doesn't run with GCC level 1, then you need to should fix it as statements could have side effects that you haven't explained to the compiler. Things can break in future compilers, as usually that kind of code has undefined behaviour which GCC (or any other compiler) has no intention to support.
As I've found just this week... forget a return statement in a small function. Looked at assembly output; half of my program was gone. Turns out, newer GCC revisions could choose to not generate code at all for a statement with undefined behaviour, and all surrounding bits of that code. Nice try finding that, but also more reasons not to write crap code quickly.
 


Share me

Digg  Facebook  SlashDot  Delicious  Technorati  Twitter  Google  Yahoo
Smf