20

Why the fuck does Arduino use C++ macros for min, max, abs, etc. ???????

---
For those who don't know what a macro is, basic example:
#define abs(x) (x > 0 ? x : -(x))
Which looks like a function, but instead abs() will be replaced by the expression and all x will be replaced literally with what you pass to abs
So
abs(i++)
would evaluate to
(i++ > 0 ? i++ : -(i++))
which would call i++ twice
---

I guess I will have to double check every built in function and rewrite it if it's a macro -_-. Not taking any chances. Btw all Arduino functions are in one big file, so now I have to pollute my code with namespaces.
Macros can be great but what the fuck.

Comments
  • 9
    Macros are not the problem are. The ++ is
  • 5
    There is discussions for fixing this in their github repo.

    https://github.com/arduino/...
  • 6
    @asgs
    I know but the Arduino platform is used by many as an entry into programming. They want to make some simple things work without having to learn about the C++ preprocessor.
    Also in "normal" C++ all the min, max, etc. are inline functions
  • 12
    You never use side effect operators in function arguments because that's a portability issue, as you experienced.
  • 8
    @pythonPlusPlus doing fun(i++) is nothing you do in your first year of coding.^^

    These are probably macros because a whole jump and return for a function could get slow at some point. Same for the whole bitfiddling stuff.
  • 10
    And you've arrived at the reason why most neo-languages make side effects really hard or impossible to perform as function arguments.
  • 4
    @nitwhiz Maybe fun(i++) isn't that common but mathematical functions maybe.
    sin() or exp() in a min/max/abs/clamp, these expensive operations would be executed multiple times.
  • 4
    Do not use increments in expressions. It's a bug prone action in itself.
  • 2
    Like most things, pre- and especially post-increment operations can be used in ways that make code opaque or wrong. But this just means they should be used discretionally. Idioms such as *a++ = *b++ are concise, clear, and well known.

    Macros that pretend to be pure functions are much more of a danger.
  • 3
    @halfflat that idiom should be a memmove instead.
  • 1
    @iiii Sure, if it's simply copying stuff, use (in C++) std::copy. It will do the right thing, and efficiently.

    But the idiom is nonetheless well known, and turns up outside of bulk copies as well.
  • 5
    @halfflat Last month, I had a loop where one of the lines went like this:
    *ptr++ >>= 2;
    I wondered how much of syntactic sugar the >>= actually is, and while it did work as intended, whether that might fuck up with a different compiler. I ended up rewriting it like this:
    *ptr >>= 2; ptr++;
    which resulted in the same assembly anyway.
  • 2
    @Fast-Nop good call, clear readable code is almost always the right choice unless your writing very special code and know exactly what you are doing.

    For ex: very high performance, cryptographic or code golf.
    And those cases most often will need to be specific for each compiler anyway.
  • 2
    @pythonPlusPlus I see constructs like that all the time in other languages so its not to much of a stretch for someone to see an example using it and doing the same in c++ without realizing the trap.

    As a minimum the compiler should spew out a clear warning a out using such constructs and any decent editor should highlight macros differently than functions so you know what you are using.

    Having two indistinguishable language constructs that work in incompatible ways is a design flaw in my opinion and aside from the ++ operator you could have the same with what you thought was nested functions orcas other mentioned side effect free but performance heavy function being called multiple times.

    Maybe there should be an option you could add to the file that require you to explicitly permit every macro you are going to use with some local definition like
    #explicit_macros
    #allow: Max

    That way you will at least know what to look out for, but the extra security will be optin so to not break existing code.

    Just like the “use strict” in javascript or c#’s nullable reference types
  • 0
    Macros put the logic in-line. No function jumps required. This leads to slightly better performance.

    In some languages you can tell the compiler to put the function body in line, not sure about Arduino.
  • 2
    @olback most modern compilers try to inline small functions if they can, I know both c++ and c# does this but it can depend on optimization flags in c++.

    And that inlining uses a local variable for the argents if they are expressions so no problem like macros.

    But it does depend, macros can do some things that functions just cannot do, for good and bad :)

    So if they had been designed with some different syntax, like an @ when calling you would know it.

    But since they have been a part of the language since early days it probably was not seen as s problem, most programs was built by smaller teams and rarely using external libraries except the standard ones.

    Once it became a problem it was to late to change.
  • 0
    @Voxera You can have static inline functions also in C, even in header files. However, "inline" is only a suggestion that the compiler may follow or ignore. If you want to force the compiler to actually inline, you have to use compiler specific function attributes.
  • 0
    Unless you're using some hacked up pos C compiler for some embedded system environment, chances are inline will do what you want when it comes to replacing macros that behave like functions.

    But C preprocessor macros are basically awful, and also, unfortunately, necessary. Modules in C++ may contain much of the awfulness, especially as modularization of stdlib progresses. But I'd love to see something with the safety and expressiveness of e.g. racket macros in C++.
Add Comment