min, max, abs.. are macroses?

If they are not macros, how should they be implemented? Templates? Inline-functions?

The idea is to get them out of the pre-processor and into the compiler. So the basic answer to that question is "anything parsed by the compiler".

A classic implementation is this:

template<typename T>
  const T&
  max(const T& a, const T& b)
  { return (a > b) ? a : b; }

This has the interesting side effect of requiring the types to be identical, which safeguards against accidentally comparing disparate types, which gives the compiler permission to get funky on your types (er, I mean, implicitly convert them on you), which can lead to unpredictable results. If that's overly pedantic, max() can be declared with two types, in which case it will behave more like normal.

Leaving it in the pre-processor means you rob files from defining it another namespace, or even as a method in a class!

Try compiling this:

class wtf
{
  int max(void)
  {
    return 1;
  }
};

void setup(void)
{
  wtf w;
  Serial.print(w.max());
}

You get this helpful error...

sketch_dec26a.cpp:6:15: error: macro "max" requires 2 arguments, but only 1 given
sketch_dec26a.cpp:15:22: error: macro "max" requires 2 arguments, but only 1 given
sketch_dec26a:2: error: function definition does not declare parameters

The risk is that existing C source files will no longer compile. But, as far as I can tell, nothing in the core is effected and none of the libraries available from this tool are effected...

http://arduino.cc/forum/index.php/topic,63215.0.html

Any other C source files to worry about?

This is what I currently use in my project. Including this file from C++ source would result in undefining macros and defining appropriate templates. Including from C source should have no effect because of macro guards.
It uses some C++11 features so -std=gnu++0x flag should be passed to gcc. It also calls gcc specific abs realizations for floating point types so it is not possible to use this with compiler other than gcc.
I've also defined new and delete operators. As far as I understand they are already defined in latest arduino. So you may need to remove them to compile your project.

P.S. Using it shouldn't have any impact on program size or speed if you are using macros only with single variables.

cpp_utils.h (2.49 KB)

No round? Did you exclude it on purpose?

You allow the parameters to be different types. You're not concerned about the possibility of implicit type-conversions?

It uses some C++11 features so -std=gnu++0x flag should be passed to gcc.

I suspect that will not happen.

P.S. Using it shouldn't have any impact on program size or speed if you are using macros only with single variables.

In my testing it does. In some cases this...

template <class T> const T& minT ( const T& a, const T& b )
{
  return( (a<b) ? a : b );
}

Produces less code the the min macro.

In my version of arduino library round is commented out:

//#define round(x)     ((x)>=0?(long)((x)+0.5):(long)((x)-0.5))

So I didn't include it.

Right. That's how it is done in gcc's library:

  template<typename _Tp>
    inline const _Tp&
    max(const _Tp& __a, const _Tp& __b)
    {
      // concept requirements
      __glibcxx_function_requires(_LessThanComparableConcept<_Tp>)
      //return  __a < __b ? __b : __a;
      if (__a < __b)
	return __b;
      return __a;
    }

So I think C++ standard allows to have different types here. At first version I didn't allow to have different types but I've got weird errors nearly identical types. Default C++ types have quite logical type conversion rules and class constructors in most cases should be defined as explicit so as to prohibit implicit type conversions. So if your classes are well written you should not be aware of implicit type conversions much.

C++11 is cool. Using it for my project allows me to spend less time and write more readable code. And I really need it for implementing good mixin design patterns. If you use makefiles you can add compiler flag easily. I do use makefiles because I'm used to vim.

I think, that you can find C++99 compliant solutions here:

or here:

But unfortunately I didn't succeeded in compiling these libraries today.

Well, it shouldn't if you pass single variable to macro which is an advised way to use macro. And it easily can be faster and consume less memory if you pass expressions in-to macro.

I personally just yesterday ran into the problems mentioned above.

int value = constrain(Serial.readByte(), 0, 5);

Anyway, I would just add that:
a) I am strongly in favor of replacing these macros by something better, if at all feasible.
b) If they end up not being replaced, I would at least suggest that they are properly documented and warned about in the reference. I had to look in the source files to find out.

The real problem with the macros isn't that they are macros but that they are too simplistic.
gcc provides a way to declare and use locals as well as extract the data types of
the arguments to declare the locals inside the macros.

This only works for gcc as it is a gcc extension.
Does anybody use anything else... :slight_smile:

But it provides a very simple way to create macros that work properly and
will work for both C and C++

So for example this is better definition of max() that is still a macro:

#define max(a,b) \
       ({ typeof (a) _a = (a); \
           typeof (b) _b = (b); \
         _a > _b ? _a : _b; })

And a few others:

#define min(a,b) \
       ({ typeof (a) _a = (a); \
           typeof (b) _b = (b); \
         _a < _b ? _a : _b; })
#define abs(x) \
       ({ typeof (x) _x = (x); \
          _x > 0 ? _x : -_x; })

Although I would probabaly want to take a closer look at abs() to see if it is better to use >= vs >
i.e. does -0 create some problems?

etc...
So this could also easy be used for constrain() and round()

For my C entrenched brain it seems a whole lot easier than all the
template and class foo.

--- bill

that looks like a good fix, but IMO it is more obfuscated than a direct template.

As an object orientated programmer, templates are a natural choice.

Leaving bugs in software and expecting novice's to know the in's and out's of #define's is quite silly. since this thread stated there have been a number of threads with confused posters as to why an operation is done multiple times and undocumented when using arduino core functionality.

People having to learn the basics of templates is far more beneficial than, rare case #define logic

having to learn the basics of templates

I personally would prefer that Arduino NOT become overly full of C++isms.

The only difference to an end user is calling functions where parameters go in '<>' rather than '()', This is incredibly simple even for a novice.

I don't see how the bugs imposed on #defines are a trade off for secure guaranteed execution of a well defined template.
This would save mass amounts of headaches.

C is great but C++ is better*

*this is my own opinion, Moved from C to C++ and never looked back.

This only works for gcc as it is a gcc extension. Does anybody use anything else...

Good question. The Arduino folks are trying to add support for more processor types (like ARM). There is a possibility that a non-GCC compiler may come into play.

pYro_65:
I don't see how the bugs imposed on #defines are a trade off for secure guaranteed execution of a well defined template.
This would save mass amounts of headaches.

I believe that a final solution needs to work for both C and C++

There are no "bugs imposed on #defines"
The existing bugs in the min/max/abs() macros are there because the implementation is too simplistic
and silently generates unintended code.
It is no different than having a bug in your main line code.
The solution is to simply fix the code.
These simplistic macros started showing up in the early to mid 80s and I've hated them
ever since. They go back to the days before there was much optimization, before function prototypes
and even inline functions and were an attempt to generate faster code.

About macros in general:
there are some things that you can do with macros that cannot be done in any other way.

--- bill

Technically unexpected behaviour is a bug.
Documentation specifying the 'unexpected behaviour' makes it an 'undefined behaviour' and explicitly leaves it to the programmer to investigate, this however is not the case.

Vastly different to having an explicitly defined error in your own code.

However I agree, the solution needs to be workable in C & C++.

that is as easy as a #ifdef for the C++ define, which leaves reason to provide a safer version when possible.

However I agree, the solution needs to be workable in C...

Why does the solution need to work in C ?

Why does the solution need to work in C ?

Haha, it doesn't. GCC is capable of both. I'm only trying to conform to the masses that do not want to migrate to C++

My main point at this stage is whatever comes out of this, the macros as is now should be rolled up and smoked. I would prefer using long winded template meta programming for replacing everything 'macro' that inline functions cannot do, but I assume this would receive negative responses too.

Really? What other no cost ARM compiler is out there
that works as well as gcc? Is there one?

pYro_65:
Technically unexpected behaviour is a bug.
Documentation specifying the 'unexpected behaviour' makes it an 'undefined behaviour' and explicitly leaves it to the programmer to investigate, this however is not the case.

Vastly different to having an explicitly defined error in your own code.

However I agree, the solution needs to be workable in C & C++.

that is as easy as a #ifdef for the C++ define, which leaves reason to provide a safer version when possible.

The C language has alot of unexpected behaviors that do not qualify as bugs as there
are many folks that are not intimately familiar with many of the low level details around
precedence, char type handling and promotion.

In this case I did say it was a bug.
No different than a math library generating an incorrect sine value or floating point calculation.
The bug just happens to be in system supplied code rather than a users code.

There is no need to punish the C code in favor of C++ when looking for solutions
especially when, as I've shown, it is possible to write better macros that work
properly for both C and C++

--- bill

@bperrybap: Are you claiming that the Arduino folks, for the foreseeable future, will only be using GCC?

I also have not seen any modern free optimising compilers that do not support C++ apart from non standard implementations. I'm sure they exist, but are their optimisations worth a grain of salt.