what the hell does "uint8_t const foo" do? - Page 1
Forum section: electronics / general

author: DavidAlfa
author_rank: Super Contributor
timestamp: December 30, 2023, 05:11:08 pm
content: I accepted a PR from someone quite some time ago, appart from strange bugs I had to fix and strange over-complicated programming syntax, all ram variables were declared this way.It's notconst var, butvar const.Code:void foo(uint8_t a){uint8_t const b = a+1;do_something(b);}Was puzzling me out so I removed them all, but does it mean anything?

author: tellurium
author_rank: Frequent Contributor
timestamp: December 30, 2023, 05:16:50 pm
content: It declares a constant value, so if you attempt to change it in the function body, you'll get a compiler warning.However, in that snippet, "b" gets passed to the function. Since arguments are passed by value, it renders "const" useless.

author: ledtester
author_rank: Super Contributor
timestamp: December 30, 2023, 05:18:07 pm
content: On this page:https://en.cppreference.com/w/cpp/language/cvin the example at the bottom you will see this code:Code:#include <cstdlib>int main(){int n1 = 0; // non-const objectconst int n2 = 0; // const objectint const n3 = 0; // const object (same as n2)volatile int n4 = 0; // volatile object...So it appears that "const int" and "int const" are the same.

author: eutectique
author_rank: Frequent Contributor
timestamp: December 30, 2023, 05:21:09 pm
content: Quote from: DavidAlfa on December 30, 2023, 05:11:08 pmvar constwould look likeCode:uint8_t b const = a+1;

author: hans
author_rank: Super Contributor
timestamp: December 30, 2023, 05:36:31 pm
content: Relevant read:https://stackoverflow.com/questions/162480/const-int-vs-int-const-as-function-parameters-in-c-and-cThe reading right-to-left trick sounds useful for this. Indeed, const char * and char const * are identical (coding style), whereas char * const is distinctly different, so there is some argument for putting const right behind whatever you're trying to fixate. In that view, char const * is less ambiguous.I personally don't do it. Maybe its a minor inconsistency, but I also didn't knew this, so I might think about it.

author: DavidAlfa
author_rank: Super Contributor
timestamp: December 30, 2023, 06:15:52 pm
content: Then it makes no sense when the function input is variable?And it was weird that "a"=non-const, "b"=const, but b=a caused no compiler warning.

author: mikerj
author_rank: Super Contributor
timestamp: December 30, 2023, 07:09:17 pm
content: Quote from: DavidAlfa on December 30, 2023, 06:15:52 pmIn the example code in your first post the const is pretty much redundant, but if there were any additional code in the function that usesbthen it is useful if you don't want it to be changed.

author: Mechatrommer
author_rank: Super Contributor
timestamp: December 30, 2023, 07:11:03 pm
content: Quote from: DavidAlfa on December 30, 2023, 06:15:52 pmQuote from: tellurium on December 30, 2023, 05:16:50 pmno its not. you only cant modify b in function body. a copy of it (passed by value or assigned to something else), you can.Quote from: DavidAlfa on December 30, 2023, 06:15:52 pmduring first definition/declaration, its ok... much like const X = 77;Quote from: DavidAlfa on December 30, 2023, 05:11:08 pma good way if you dont have anything else to doif its not broken why fix it?

author: Siwastaja
author_rank: Super Contributor
timestamp: December 30, 2023, 07:11:49 pm
content: b does not change, that is told to the compilerand other human readersby the const qualifier. You definitely should be seeing compiler error, not a warning:Code:#include <stdio.h>int main(){int a = 1;const int b = a + 1;b = 2;printf("b = %d\n", b);}$ gcc tt.ctt.c: In function ‘main’:tt.c:7:4: error: assignment of read-only variable ‘b’7 | b = 2;| ^Being const-correct is considered one of the most basic and most important quality requirements, although I admit being sloppy quite often. If you don't know what it is, don't touch it. It's there for a reason; don't make it worse.For that example to be actually const-correct, the function itself should have its argument qualified const, too. Hopefully it is.

author: DavidAlfa
author_rank: Super Contributor
timestamp: December 30, 2023, 07:20:35 pm
content: For example:Code:static void writeFlash(uint32_t* src, uint32_t len, uint32_t dstAddr){uint32_t const numWordsToWrite = (len + sizeof(uint32_t) - 1u) / sizeof(uint32_t);What would be the difference?The compiler will know if the function arguments are constant or coming from volatile sources, and optimize accordingly, right?"Forcing" it constant will cause warnings or errors, thus extra work, if the program changes in the future and takes the argument from elsewhere?Quote from: Mechatrommer on December 30, 2023, 07:11:03 pmBecase I like uniformity. 99% of the code without const, those with const. My eyes catched that every time!

author: Mechatrommer
author_rank: Super Contributor
timestamp: December 30, 2023, 07:34:23 pm
content: Quote from: DavidAlfa on December 30, 2023, 07:20:35 pmits the original programmer's intention he's trying to tell you (the maintainer) something, what was it? who knows? and you dont show the whole codes... so who knows?Quote from: DavidAlfa on December 30, 2023, 07:20:35 pmoh those were the days and spirit. nowadays i only do that on newly created program (i maintain my own code, even i always forgot what was my intention years ago)Quote from: DavidAlfa on December 30, 2023, 07:20:35 pmeither you have naming convention such as const myvar_constor your program is simple enough to catch them... ymmv.

author: SiliconWizard
author_rank: Super Contributor
timestamp: December 30, 2023, 08:44:00 pm
content: - The order of the qualifiers does not matter here. 'const type' or 'type const' is the same. I admit I have rarely seen a code style with the qualifier(s) after the type rather than before, but it's a matter of code style, and no two people have exactly the same, so... (It's actually a good source of endless wars.)- Yes as mentioned above, not to be confused with the distinction between a const pointer and a pointer to const, which is a different case. Not completely trivial to understand at first. But the const is either after or before the '*', so the syntax is different. Not your concern here anyway.- 'const' may not look useful in the case your showed, but at least it conveys the intent of the programmer that the variable should not be modified after having been initialized (and yes, obviously you can initialize a const variable, otherwise how would you ever use them? But you can't modify them after that.) For instance, in the first piece of code you showed, it means that b should not change. As it is, it doesn't make a difference because it's used immediately in a call to a function, and nothing else, but if there is any code added in between, or after the call, in future modifications, then it prevents you from modifying b for any reason you may have. So yes, it can be useful.Remember that code is written to be understood and maintained, not just to compile.

author: peter-h
author_rank: Super Contributor
timestamp: December 30, 2023, 09:48:39 pm
content: SurelyQuotecompiles only because a=1 beforehand, so the compiler can see b cannot be anything other than 2.Otherwise b cannot be "const".Having said that, I don't write code which I don't understand so I don't use "const". If something is really fixed I use #define

author: abeyer
author_rank: Frequent Contributor
timestamp: December 30, 2023, 11:35:49 pm
content: Quote from: peter-h on December 30, 2023, 09:48:39 pmNo,constin C/C++ doesn't mean compile-time constant... it's perfectly valid to have aconstvariable with a value determined at runtime.

author: ejeffrey
author_rank: Super Contributor
timestamp: December 31, 2023, 02:48:17 am
content: Quote from: DavidAlfa on December 30, 2023, 07:20:35 pmIt has almost nothing to do with optimization and nothing to do with volatile other than occupying the same place in the grammar for mostly silly reasons.QuoteIt will only cause errors if you try to modify a variable you declared as not to be modified. In that case you *want* the error.That said, I don't regularly mark local variables as const even when I could, at least not in C. It's much more useful (and hard to get right) with pointers, where it declares that the function accepting a pointer argument won't be modifying the data pointed to.

author: DavidAlfa
author_rank: Super Contributor
timestamp: December 31, 2023, 05:12:39 am
content: Yeah I perfectly know the difference in pointers, it's was this specific case causing some.

author: Siwastaja
author_rank: Super Contributor
timestamp: December 31, 2023, 06:57:40 am
content: Quote from: DavidAlfa on December 30, 2023, 07:20:35 pmThis is the whole point, preventing accidental or poorly thought out modifications. Yes, if you want to modify the code so that numWordsToWrite changeson the fly, after the initial calculation, then the original programmer thought "hmm, that would require quite some care or large modifications". In that case, you obviously remove the const qualifier, and that will be the smallest part of the change. As the code stands now, theconstalone tells the reader that this won't change, making the code quicker to understand.Inside a code block like this, it is true const is not needed for optimization; any half-decent compiler can deduce the value does not change anyway. In interfaces (functions within different modules linked without LTO), e.g. with pointers, using the const qualifier is even more important, and also for performance. See also: restrict qualifier.

author: Siwastaja
author_rank: Super Contributor
timestamp: December 31, 2023, 07:01:26 am
content: Quote from: DavidAlfa on December 30, 2023, 07:20:35 pmThis is way too common, in my code too I'm sloppy and lazy and don't always write const-correct code. Better habit than removing const qualifiers would be to startaddingthem. But if the code base is 99% const-incorrect and 1% const-correct, that would be a daunting task. Still, don't remove it; start getting used to better programming practices instead! C is a surprisingly modern high-level programming language when used to its full capability; yet people, me included, write horrible hacks with it.

author: DavidAlfa
author_rank: Super Contributor
timestamp: December 31, 2023, 01:33:09 pm
content: So you say that it's a way of ensuring the value is always dependant on constant sources:Code:int a=1; // a is assigned at compile time, thus constantint const c = a + 2; // This will compile fineCode:int b= readUART(); // b is volatile hereint const c = b + 2; // This will cause compile errorCode:int a= i; // a is constantint b= readUART(); // b is volatileint const j = b + 2; // Oops! I meant a, not b. Thankfully I will get a compiler error showing this fuck-up.

author: Mechatrommer
author_rank: Super Contributor
timestamp: December 31, 2023, 02:49:58 pm
content: no. its a way to prohibit yourself (or others) to do the bolded line...QuoteorQuotewhy is it so hard to understand?https://www.w3schools.com/cpp/cpp_variables_constants.asp

author: peter-h
author_rank: Super Contributor
timestamp: December 31, 2023, 03:02:14 pm
content: Reading that link above, what is the difference (in terms of read-only protection) betweenconst uint32_t fred = 2;and#define fred 2The examples they giveconst int minutesPerHour = 60;const float PI = 3.14;are perfectly suited to #defineI think, and e.g. herehttps://www.geeksforgeeks.org/const-qualifier-in-c/the intention is thatafter the initial assignmentthe value is read-only. It obviously cannot be read-only the whole time otherwise you could not assign the value in the first placeObviously there are cases where you want a "read only variable" e.g. when creating an array of bytes which you will later be indexing into, each with its initial value, but for lone variables??But would this R/O protection be implemented by the compiler if such a const array was later modified via a runtime computed index, or worse still a pointer? I've seen compiler behaviour where it replaced an array to array data copy loop with a memcpy and for that it must have been sure it had correctly "simulated" the index ranges, but what if you took this a step further and set a pointer p to the array base and then did a calculation on p?

author: gf
author_rank: Super Contributor
timestamp: December 31, 2023, 04:41:11 pm
content: Quote from: peter-h on December 31, 2023, 03:02:14 pmIt may be a personal preference of mine, but what I don't like about macros is that they "pollute" the entire program, so that you cannot re-use the same identifier in a different scope for a different purpose.For instanceCode:#define fred 2int f(){char* fred = "fred"; // <-- syntax error// ... whatever}int g(){return fred; // returns 2}won't compile, whileCode:static const int fred = 2;int f(){char* fred = "fred"; // OK// ... whatever}int g(){return fred; // returns 2}does compile.QuoteExactly that. And for automatic variables, the initial value does not need to be a compile-time constant. Static and global variables, OTOH, can only be initialized with compile-time constant expressions in general.[ C23 also added a "constexpr" specifier for compile time constants. This specifier ensures that the initializer is a compile-time constant expression. Seehttps://en.cppreference.com/w/c/language/constexpr]

author: Siwastaja
author_rank: Super Contributor
timestamp: December 31, 2023, 05:13:30 pm
content: Quote from: DavidAlfa on December 31, 2023, 01:33:09 pmNo, nothing like that. You are overthinking the capabilities of const qualifier. It just means you can't change that variable after initialization, nothing else. Initialization value can be anything, including something only known at runtime. Really, read all the replies carefully - and some book or the standard.

author: gf
author_rank: Super Contributor
timestamp: December 31, 2023, 05:28:11 pm
content: Quote from: peter-h on December 31, 2023, 03:02:14 pmUnfortunately, C does allow the assignment ofconsttype*totype*pointers. Even without cast.Gcc only spits out a warning. Example:https://godbolt.org/z/jfE1jdaEPSo you can indirectly modify const objects.OTOH, C++ does not allow this conversion, neither implicitly, nor with static_cast(), nor with reinterpret_cast().Youexplicitlyneed to use a const_cast(), or a traditional C-style cast (which is rather taboo in C++ anyway) to do that, so it can hardly happen by accident.

author: Siwastaja
author_rank: Super Contributor
timestamp: December 31, 2023, 05:54:40 pm
content: Quote from: gf on December 31, 2023, 05:28:11 pmThe good news is, it does spit out a warning. Better get used to the fact that C becomes a much better language with -Wall -Werror

author: ejeffrey
author_rank: Super Contributor
timestamp: December 31, 2023, 08:47:33 pm
content: Quote from: peter-h on December 31, 2023, 03:02:14 pmThe latter doesn't obey C scoping and lifetime rules and you can't take the address of it. If you use simple literals without casts it is limited to only a few possible types (i.e., you can't make a 'short' literal). If you use casting or complex expressions then you can fall victim to weird macro expansion / parsing problems such as order of operations. In short, #define works OK for simple numeric literals, but rapidly becomes worse than using const variables when you move beyond that. Because there is no name bound to the value, there is no chance for the compiler to detect conflicting definitions in multiple translation units.The only real advantage to #define is that it can be used in places that require a compile time constant, such as array sizes. This is not a problem in C++, which allows:Code:const int size=5;int data;I'm not saying to stop using #define for constants, there are still cases where it is advantageous, and due to long history of usage is considered idiomatic. However, for a lot of cases, defining a global constant variable is better.

author: ejeffrey
author_rank: Super Contributor
timestamp: December 31, 2023, 08:58:48 pm
content: Quote from: gf on December 31, 2023, 05:28:11 pmYeah, this is basically due to const being added to C after it was in wide use, so a lot of code was written without const, or even without considering the possibility of constness -- for instance you can't implement the standard library strchr and strstr functions without casting away const. I guess the thinking was that it would be easier to be adopted if they were a little sloppy with it, since otherwise it's hard to add const in one place without converting your whole codebase at once. C++ has has const since near the beginning, so was able to be stricter with it.

author: gf
author_rank: Super Contributor
timestamp: December 31, 2023, 09:30:08 pm
content: Quote from: ejeffrey on December 31, 2023, 08:47:33 pmFor automatic arrays, the following also works works in C with -std=c99 and later:Code:void f(){const int size=5;int data;}For global and static arrays, constexpr and -std=c23 are required:Code:constexpr int size=5;int data;

author: dorkshoei
author_rank: Frequent Contributor
timestamp: December 31, 2023, 11:47:00 pm
content: Lots of talk here about #define. For some cases consider using enum instead. A symbolic debuuger can do better job of reporting the symbolic value vs if you just #define. Obviously this has only a loose connection to const issue

author: Mechatrommer
author_rank: Super Contributor
timestamp: January 01, 2024, 06:51:30 am
content: #define not only to assign constants, it also for code shortcut such as#define MY_CODE ((a + myfunc(b)) / c)and its not stored anywhere in program, its only allocated during compile time by compiling machine.so its not your runtime thing...

author: pgo
author_rank: Regular Contributor
timestamp: January 01, 2024, 11:06:28 am
content: Quote from: Mechatrommer on January 01, 2024, 06:51:30 amOr just write it as a function and expect any decent compiler to inline it anyway?If the expression is not a constant then using a macro would still mean it would still "exist" in the code so it is stillstored somewhere in programas a fragment.If constant than the function would again be optimised to a constant value at compile time.Just don't be clever with macros, please!!!

author: Mechatrommer
author_rank: Super Contributor
timestamp: January 01, 2024, 12:12:31 pm
content: Quote from: pgo on January 01, 2024, 11:06:28 amthere are some circumstances simply making it a function will not do.. much like a construct with different values to setup before making a call.. and it happens at various places in your code. fixing or changing the #definition will fix all. if you havent found such condition then good for you.

author: DavidAlfa
author_rank: Super Contributor
timestamp: January 01, 2024, 12:24:08 pm
content: Macros are very efficient sometimes.For example, I do use them for GPIO ports.CubeIDE makes easy to name the pins to XX, generating XX_GPIO_Port and XX_Pin, for example a LED.(Or just by hand in any header)Instead doing PORT->IDR/ODR/BSRR etc every time (Long and messy) I use these macros.Code:#define CON2(a,b) a##b#define WritePin(p,o) CON2(p,_GPIO_Port->BSRR) = CON2(p,_Pin)<<(o ? 0 : 16)#define ReadPin(p) ((CON2(p,_GPIO_Port->IDR) & CON2(p,_Pin)) && 1)#define TogglePin(p) CON2(p,_GPIO_Port->ODR) ^= CON2(p,_Pin)Code:#define LED_Pin LL_GPIO_PIN_0#define LED_GPIO_Port GPIOBCode:TogglePin(LED);ReadPin(LED);WritePin(LED, 1);

author: Siwastaja
author_rank: Super Contributor
timestamp: January 01, 2024, 12:39:50 pm
content: Quote from: pgo on January 01, 2024, 11:06:28 amYou can use always_inline attribute to force this, if you need to. The choice between macro vs. function should not be about perceived efficiency, but type requirements:* When you need one type, use a function, to get type checking* When you need generics over many potential types, use macros. That's the C way; C++ hastemplates.

author: gf
author_rank: Super Contributor
timestamp: January 01, 2024, 02:39:41 pm
content: Quote from: Siwastaja on January 01, 2024, 12:39:50 pmFor the calculation of compile-time constant expressions, you still may need to use macros in C, since (unlike C++) C23 does not support constexpr functions.EDIT: I mean:This works in C, of course (with macro):https://godbolt.org/z/xcT533rsoBut this does not work in C:https://godbolt.org/z/69cMnGo8rHowever, with constexpr, this works in C++:https://godbolt.org/z/fvvMoKaav

author: ejeffrey
author_rank: Super Contributor
timestamp: January 01, 2024, 04:37:11 pm
content: Quote from: Mechatrommer on January 01, 2024, 12:12:31 pmNot sure what you mean by that but your example sure was a textbook example of a macro that should be an inline function. "Computing some values and calling another function" is a textbook example of what functions do.DavidAlfas example with GPIOs used token pasting which is indeed something you can't do with a simple function call. If you have painted yourself into a corner by using separate #defines for the GPIO port and pon, go ahead and do that but for everyones sanity don't design that on purpose. It's much cleaner to use a GPIO_pin_t stuct which you can write simple functions to manipulate.

author: DavidAlfa
author_rank: Super Contributor
timestamp: January 01, 2024, 05:15:44 pm
content: For me it's much easier to manipulate a header with pin declarations than diving into the files?

author: cv007
author_rank: Frequent Contributor
timestamp: January 01, 2024, 06:02:52 pm
content: QuoteI already said too much on this subject in another thread, but if you actually use these macros your TogglePin macro uses ^= which leaves an opportunity to corrupt any of the 15 other pins on the port when used in multiple run levels/irq levels. Simply using the other two macros as a replacement would be a solution- #define TogglePin WritePin( p, ReadPin(p) ).You can live without defines/macros a lot more than you think-https://godbolt.org/z/Mza8Ef6x1

author: ejeffrey
author_rank: Super Contributor
timestamp: January 01, 2024, 08:18:04 pm
content: Quote from: DavidAlfa on January 01, 2024, 05:15:44 pmThat doesn't really make any sense. You can put it in the header files or "dive into the files" whatever that means.Your example has a few major shortcomings beyond the (potential) concurrency issue cv007 mentioned. The biggest one is that the poor bastard who has to read:Code:toggle_pin(LED);and tries to look up LED will find that it is not defined anywhere in the source code. It's not a macro, it's not a global variable, it's non existent and can't be used outside the argument of toggle_pin or it's friends.As a consequence of this, you simply can't write a function that takes a pin as an argument nor can you store it in a larger data structure. To do that you have to add more macros, or fall back to LED_GPIO_Port and LED_Pin, and now you have two separate and incompatible ways to refer to the same thing.Again, using token pasting is OK if the alternative is major refactoring or changing vendor code that you don't want to touch, but your approach is not something anyone should do by choice. It's much better to do something like the following:Code:// my_gpio.htypedef struct {GPIO_Base *port;int pin_idx;} gpio_pin_t;static inline write_pin(gpio_pin_t target, int val){target.port->BSRR = 1 << (target.pin_idx + (val ? 0 : 16));}const static gpio_pin_t LED1 = {GPIOA, 1};const static gpio_pin_t LED2 = {GPIOD, 3};// main.cint main(){write_pin(LED1, 1);}now you can write any function you want to accept a gpio_pin_t, it will be type checked, you can store it in a struct for later use and all the identifiers are easily searchable. You can keep all your GPIO name assignments in one header flie, or declare them in the module in which they are used. You can put them in .c files and avoid polluting the global namespace but still able to pass them to generic functions.

author: pgo
author_rank: Regular Contributor
timestamp: January 01, 2024, 11:28:40 pm
content: There is lots of interesting discussion of the advantages of Macros.Personally I don't like them for several reasons (some already given):- They are a textual substitution. This is very powerful in some ways but it also leads to strange and difficult to track down bugs. For example, if you #define a symbol then it is substituted almost everywhere. I have wasted time tracking down strange bugs when a macro defined in a header file happens to have the same name as a structure member name - really weird messages from the compiler! Yes I know - use capitals for the macro and lowercase elsewhere BUT have a look in the standard header files for most CPUs - full of structs waiting to be broken.- Lack of scoping - related to the above. A name defined somewhere applies everywhere.- Novices especially have trouble with defining macros correctly - correct use of brackets, unintended effect of multiple evaluation side-effects etc.Personally I use C++ so I can use templates (wait for the hue and cry!) to achieve many of the things that would be done with clever macros. The macro examples given earlier for GPIO can be safely done with templates. These areequally efficientand can be in namespaces to limit name collisions if needed.bye

author: peter-h
author_rank: Super Contributor
timestamp: January 02, 2024, 08:42:35 am
content: STM use macros heavily for setting up GPIO and peripherals.What I don't like is macros that generate code, rather than just doing compile-time calcs. Then you end up with stuff in .h files which generates code. It can be damn confusing when you are trying to create stuff like a boot block which jumps to a given address, and the code at that address is not what you expected

author: Siwastaja
author_rank: Super Contributor
timestamp: January 02, 2024, 09:57:50 am
content: Quote from: DavidAlfa on January 01, 2024, 05:15:44 pmWut, diving into what files? I mean, code has to be in files, where else? Just put the functions into those same files where you now put macros.static inlineexists just for this reason, the functions can be in header files, which can be included and included again in every compilation unit if you so wish, just like macros.The only reason to use macros is when some macro-exclusive feature is actually needed, e.g. absence of type checking (allowing generic "template"-like thing), or text concatenation/stringify operations. Also great for generating compile-time arrays etc, I prefer them over separate code generation scripts.Otherwise than that, macros require a lot of care:1) You need to wrap them in do{} while(0) which looks ugly and is unnecessary boilerplate. If you fail to do that, macro cannot be used like a function e.g.Code:#define LED_ON() {GPIOA->BSRR = 1UL<<5;}if(condition)LED_ON(); // expands to {GPIOA->BSRR = 1UL<<5;};, which has one semicolon too much, so that the else below doesn't belong to anywhereelsesomething_else();Fixed:Code:#define LED_ON() do{GPIOA->BSRR = 1UL<<5;}while(0)if(condition)LED_ON(); // expands to do{GPIOA->BSRR = 1UL<<5;}while(0); -- just the right number of semicolonselsesomething_else();2) You lose type checking so need to be more careful everywhere in code3) You have to split lines with trailing \, which is tedious,4) You have to wrap all arguments inside ():Fail:Code:#define MACRO(arg) (arg*2)MACRO(5); // 5*2 = 10 -> okMACRO(5+1); // 5+1*2 = 7 -> failOKCode:#define MACRO(arg) ((arg)*2)MACRO(5); // (5)*2 = 10 -> okMACRO(5+1); // (5+1)*2 = 12 -> ok5) For macros that access the argument(s) more than once, you need to make a temporary variable:Fail:Code:#define ENDIAN_SWAP(arg) (((arg)&0x00ff)<<8 | ((arg)&0xff00)>>8) // very common bug, looks fine, but...ENDIAN_SWAP(i++); // fails, when it becomes: (((i++)&0x00ff)<<8 | ((i++)&0xff00)>>8), i is incremented twice, in undefined order.Fuck this, I don't even try to fix it with a temporary variable, because macros do not havereturn, maybe you could do something with the , operator no one normally uses. Instead, let's be reasonable and do this properly:Code:static inline uint16_t ENDIAN_SWAP(uint16_t arg) { return (arg&0x00ff)<<8 | (arg&0xff00)>>8; } // look how many unnecessary parenthesis we removedENDIAN_SWAP(i++); // works now as expected.TLDR, only use macros when they offer something useful, not "just because".

author: gf
author_rank: Super Contributor
timestamp: January 02, 2024, 10:05:21 am
content: Quote from: Siwastaja on January 02, 2024, 09:57:50 amGcc hasstatement expressions({ ... }), but it's a proprietary extension.

author: Siwastaja
author_rank: Super Contributor
timestamp: January 02, 2024, 11:24:54 am
content: Quote from: Mechatrommer on January 02, 2024, 11:21:53 amI don't understand. You can put the static inline function definition EXACTLY where you would put the macro definition. If you need to modify it, you modify it where it is, in one place. Exactly the same thing.

author: Mechatrommer
author_rank: Super Contributor
timestamp: January 02, 2024, 11:29:31 am
content: Quote from: Siwastaja on January 02, 2024, 09:57:50 ami think what he meant is that, if we use #define, its usually in top header file, and then we use it in various places in code files... if we need to change pin number or logic, we just modify the top section of header file, no need to dig in every part in code file if we dont use #define. if we use function instead of #define, we still need to dig where is that function in code file. you all sounds too beginner to understand this? its not easy to find a function implementation in the middle of a large *.c file esp if the programmer used prototype declaration and then too many calls prior to the actual function body. at least we need some few clicks on find tool and look carefully are we looking at actual body? or just a function call? he mentioned about "uniformity" i "think" i know exactly what he meantsometime i can understand "uniformity" as "tidiness" or "logically-correct" arrangement. ymmv.Quote from: Siwastaja on January 02, 2024, 09:57:50 amhow about... just because it offers something useful?having interuppting all these relative or personal favourite issues, i didnt mean to encourage nor discourage the use of #define, its just a tool c/c++ provided. if you think its useful, go for it, everyone has they own coding style, i'm not interested in politically correct (relative thing) discussion. ymmv.Quote from: Siwastaja on January 02, 2024, 11:24:54 amtoo much to type believe me... if we need to modify arguments, we have "double work" ...(ps: sorry for the edited text, i hate "last edited" signature on my post...)

author: DavidAlfa
author_rank: Super Contributor
timestamp: January 02, 2024, 12:12:52 pm
content: "There are 1000 ways of changing a bulb"

author: cv007
author_rank: Frequent Contributor
timestamp: January 03, 2024, 01:17:37 am
content: QuoteModifying my previous example, although commenting out the includes/pragma once is required for the online compiler so have to understand these sections will live in different files as noted-https://godbolt.org/z/o8bdbvzx9If that led changes to another pin, still only 1 place to change. Also note since the online compiler had all these functions accessible in the single file, it inlined test1/test2 although they are still callable since they are not marked static.The defines may have been a requirement in the distant past due to not so smart compilers, but the modern compiler is quite capable of optimizing if you give it the right information (static/const/etc). Whether one wants to go this far or not is up to each user. As noted, there are still some things you just have to get the pre-processor to do, and dealing with strings is one of them.I switched to using mostly c++ some years ago, and at the same time also set out to eliminate creating any define unless no other choice. It takes some effort to do so as the 'standard' way of using defines for just about everything has been around a long time, and tends to becomes your first thought (I need x- create a define for it, I need y- create a macro). Once that habit is broken, it is actually pretty nice to be free from these things, but then still have to deal with code where defines still rule (manufacturers code/headers is one such place). Using c++ does make this easier to do, but you can still go a long ways in c if you want to (want being the key word). Just you and the compiler one-on-one, no middleman which routed/modified your input through an unknown number of headers.If was in charge of an mcu programming 101 course, I would prohibit the creating of any define and make the student learn the language. Whether or not it is useful for them in later stages (like strings), at least the habit will not have been set which normally treats every problem as having a solution involving a define. I am not in charge of anything, so no need to worry.

author: Tation
author_rank: Regular Contributor
timestamp: January 03, 2024, 05:06:27 am
content: Quote from: cv007 on January 03, 2024, 01:17:37 amInterestingly, when I switched to C++ for embedded projects, also a few years ago, I also desired to be freed from macros, specially the clever ones. After this time I can say that it is possible (in C++) to live without them (well, almost, vendor's #defines are still there). Maybe it is that I have not yet confronted the problem which is hard with constexprs or templates or plain functions, but easy with macros.And no, there's neither Flash size increase nor speed decrease, but much more readable code instead.