[C preprocessor] - Page 1
Forum section: electronics / general

author: Simon
author_rank: Unknown
timestamp: August 03, 2018, 01:43:27 pm
content: 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 50will one_two get replaced with 12 or 50 ?

author: DJohn
author_rank: Regular Contributor
timestamp: August 03, 2018, 01:56:56 pm
content: #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 #definea, that will only replaceawhen it appears on its own, not in (for example)abc

author: RoGeorge
author_rank: Super Contributor
timestamp: August 03, 2018, 02:02:46 pm
content: one_two will be 50You can always probe for it online:https://www.onlinegdb.com/online_c_compiler

author: Simon
author_rank: Unknown
timestamp: August 03, 2018, 02:16:13 pm
content: Thank you

author: rstofer
author_rank: Super Contributor
timestamp: August 03, 2018, 02:28:42 pm
content: A word of caution when using #define to alias arithmetic expressions (and constants), put the definition inside parentheses.Code:#define one (1)That one works fine either way. This one is a problem:Code:#define WIDTH 80+20int a = WIDTH * 2; //expect a==200 but a==120To actually get the anticipated 200, it needs to look like this:Code:#define WIDTH (80+20) // now the addition will happen firstint a = WIDTH * 2; //now you should get 200

author: Simon
author_rank: Unknown
timestamp: August 03, 2018, 02:34:52 pm
content: Yes well i usually only use definitions for single numbers as constants, and yes a very good idea to put any calculation in parenthesis.

author: NorthGuy
author_rank: Super Contributor
timestamp: August 03, 2018, 02:40:25 pm
content: Quote from: Simon on August 03, 2018, 01:43:27 pmIf you remove the underscore, it will not replace it neither:Code:#define one 1#define two 2#define one two 50The last line wil not be treated asCode:#define 1 2 50Rather, 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.

author: newbrain
author_rank: Super Contributor
timestamp: August 03, 2018, 02:51:03 pm
content: 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:#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 50static int i = glue( one, two ); // i will take the value 12The 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.

author: andyturk
author_rank: Frequent Contributor
timestamp: August 03, 2018, 03:46:19 pm
content: Quote from: newbrain on August 03, 2018, 02:51:03 pmThat's a good example of why macros should be considered (an occasionally necessary) evil.

author: rstofer
author_rank: Super Contributor
timestamp: August 03, 2018, 03:50:08 pm
content: Quote from: newbrain on August 03, 2018, 02:51:03 pmAs 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...

author: Simon
author_rank: Unknown
timestamp: August 03, 2018, 03:55:10 pm
content: Quote from: andyturk on August 03, 2018, 03:46:19 pmWhy?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.

author: Kjelt
author_rank: Super Contributor
timestamp: August 03, 2018, 03:58:23 pm
content: 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)

author: Simon
author_rank: Unknown
timestamp: August 03, 2018, 04:07:22 pm
content: 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?

author: helius
author_rank: Super Contributor
timestamp: August 03, 2018, 04:09:51 pm
content: Literal numbers are of typeintunless 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.

author: Cerebus
author_rank: Super Contributor
timestamp: August 03, 2018, 04:10:35 pm
content: Quote from: Simon on August 03, 2018, 03:55:10 pmAs 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:#define MAGIC_NUMBER 1234versusCode:const unsigned int MAGIC_NUMBER = 1234;orCode:#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".

author: Simon
author_rank: Unknown
timestamp: August 03, 2018, 04:17:57 pm
content: OK so constants are better than macro's as they speak straight in "C" but I do many things in macro's:Code:#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.

author: helius
author_rank: Super Contributor
timestamp: August 03, 2018, 04:22:14 pm
content: The advantage of preprocessor definitions over const variables (shouldn't that be a contradiction?) is avoiding linker issues. If you#includea header files of#definesinto 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 likeCode: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 anexterndeclaration 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.Theconstdefinition 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#defineconstants is to use enums. You can combine several constants into a single enum declaration.Code:enum { MAGIC_NUMBER = 1234 };

author: Simon
author_rank: Unknown
timestamp: August 03, 2018, 04:42:27 pm
content: enum?my last project did infact end up with a C file and h file for all of the shared variables.

author: newbrain
author_rank: Super Contributor
timestamp: August 03, 2018, 06:16:04 pm
content: Quote from: rstofer on August 03, 2018, 03:50:08 pmLet's say newishIt'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 edgesBut I agree: these things, though powerful and standardized, should be used sparingly.Quote from: helius on August 03, 2018, 04:22:14 pmTrue, though the linker will never see the#defines, which are resolved even before the compiling phase starts.Quote from: helius on August 03, 2018, 04:22:14 pmHere I think C++ got it exactly right: internal linkage forconstqualified names, and they can be used as constants at compile time.This also helps getting rid of the associated storage, if possible.

author: andyturk
author_rank: Frequent Contributor
timestamp: August 03, 2018, 06:23:07 pm
content: Quote from: Simon on August 03, 2018, 03:55:10 pmIt'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 inclusionX-Macrosconditional 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.

author: nctnico
author_rank: Super Contributor
timestamp: August 03, 2018, 07:08:29 pm
content: Quote from: Simon on August 03, 2018, 01:43:27 pmOne 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.

author: legacy
author_rank: Super Contributor
timestamp: August 03, 2018, 07:58:59 pm
content: ergo, don't abuse the preprocessor's power

author: Kjelt
author_rank: Super Contributor
timestamp: August 04, 2018, 08:06:11 am
content: 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.

author: tggzzz
author_rank: Super Contributor
timestamp: August 04, 2018, 08:52:21 am
content: Quote from: andyturk on August 03, 2018, 06:23:07 pmWould 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_TMPBut 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.

author: hans
author_rank: Super Contributor
timestamp: August 04, 2018, 08:57:25 am
content: 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 youneed toshould 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 couldchooseto 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.

author: Simon
author_rank: Unknown
timestamp: August 04, 2018, 09:16:11 am
content: I simply use defines to make the code readable by replacing text as shown above. The new register names make a little more sense but with thing that require multiple bits I get fed up with looking them up so I just define what i want to do and it is replaced by the code.

author: tggzzz
author_rank: Super Contributor
timestamp: August 04, 2018, 09:27:58 am
content: Quote from: hans on August 04, 2018, 08:57:25 amYou are lucky. You couldn't have objected if it chose to "rm -rf /", or cause demons to fly out from your nose

author: Simon
author_rank: Unknown
timestamp: August 04, 2018, 09:42:36 am
content: Quote from: tggzzz on August 04, 2018, 09:27:58 amErr, I was made to put return statements in functions as CCC threw a wobbly and insisted. Mainly on interrupt functions.

author: tggzzz
author_rank: Super Contributor
timestamp: August 04, 2018, 10:09:58 am
content: Quote from: Simon on August 04, 2018, 09:42:36 am"undefined behaviour" can cause "nasal demons". But I'm sure you know that.

author: hans
author_rank: Super Contributor
timestamp: August 04, 2018, 10:11:42 am
content: Very funny.

author: tggzzz
author_rank: Super Contributor
timestamp: August 04, 2018, 10:34:56 am
content: Quote from: hans on August 04, 2018, 10:11:42 am"Many a truth is spoken in jest". The underlying problem isn't funny, unfortunately!

author: andyturk
author_rank: Frequent Contributor
timestamp: August 07, 2018, 02:37:10 am
content: Quote from: tggzzz on August 04, 2018, 08:52:21 amWould I? Yeah, probably. But I've never actually needed to do that (yet). I did look at some TMP code that generated a constexpr trig lookup table at compile-time, which was very cool and far better (IMO) than a two-stage build with a code generator that spit out some C source to be compiled in the next phase.QuoteSo, how aboutm4? It makes the C preprocessor look rather toy-like. And m4 was also designed by K&R.