min, max, abs.. are macroses?

kirbergus
I don't mind a good discussion but please watch your language. Definitely if you make statements which are not solid.
1) your language:
It is not macroses but macros
You do not use WTF. Definitely not in a title.
2) the "must be a function"
The C89 standard you refer to is more commonly known as ANSI C. ANSI C was officially released in 1989. I have been programming in C from 1991 till 2000 for my living. In all those years I used a define for max. It is new to me that this must be a function. If you are sure that ANSI C states that max must be a function please tell me where this is stated.
The C99 is the newest version of C. The compiler used by the Arduino team is (like most other C compilers) not 100% compliant with C99. As far as I know there is no statement that max must be a function. Please state where it states min max and abs must be functions. Also refer to where it states all C compilers must comply to C99.
3) No macros but Inline
You state that these macros must be rewritten as inline. Inline is new in C99. It comes from C++. Inline is class related and on a AVR you do not want to have classes for something as basic as integers.
4) why a macro?
A macro has a great advantage. That is, it works with any type that implements the methods you use. So you do not need a maxint maxlong maxstring.....
5) You are right there is a risk with macros
On the content you are right that

 MyNumber=5; 
max(++MyNumber);

will result in MyNumber being 7 and not 6; This is however a design decision that has been taken a long time ago on very good ground by people that didn't know about Arduino because it simply didn't exist yet. So blaming Arduino is not the way to go. It has been like this for decades now. I feel no sense of urgency.
6) overloaded functions from C++ std
I see no reason why you could not overload. You can use the #undef instructions if you want.

My advice: before you post again read this article:How To Ask Questions The Smart Way
If you do not comply to these rules I will no longer respond on your posts.
Best regards
Jantje

Jantje:
kirbergus
I don't mind a good discussion but please watch your language. Definitely if you make statements which are not solid.
1) your language:
It is not macroses but macros
You do not use WTF. Definitely not in a title.

Thanks. I was a little bit upset that I have to reinvent standard library. I've corrected errors.

Jantje:
2) the "must be a function"
The C89 standard you refer to is more commonly known as ANSI C. ANSI C was officially released in 1989. I have been programming in C from 1991 till 2000 for my living. In all those years I used a define for max. It is new to me that this must be a function. If you are sure that ANSI C states that max must be a function please tell me where this is stated.
The C99 is the newest version of C. The compiler used by the Arduino team is (like most other C compilers) not 100% compliant with C99. As far as I know there is no statement that max must be a function. Please state where it states min max and abs must be functions. Also refer to where it states all C compilers must comply to C99.

I didn't want to say that max must be a function accordingly to standard. Only abs must, in ANSI C too. I wanted to say that today in 2011, nearly in 2012 there is no need to put user right at the edge of a pit, even if you warn him. Look at this macro:

define sq(x) ((x)*(x))

It squares a variable. Why you would ever like to use it? If you have a single variable x it is easier and shorter to write x*x. But if you have a big statement which you would like to square it is very handy just to put it inside sq(). If sq was a function it would be computationally efficient too.
So if you see that using sq()would ease your life you nearly definitely going to make a mistake. This macro pushes user to a wrong way.

Jantje:
3) No macros but Inline
You state that these macros must be rewritten as inline. Inline is new in C99. It comes from C++. Inline is class related and on a AVR you do not want to have classes for something as basic as integers.

inline has nothing with classes. inline is just a hint for compiler that you would like to inline the function, nothing more. Using inlined function for integer does not convert them to classes.

Jantje:
4) why a macro?
A macro has a great advantage. That is, it works with any type that implements the methods you use. So you do not need a maxint maxlong maxstring.....

Actually this macro works only with different types of integers, float, double, pointers which can be implicitly converted to void*. If you have operator< defined for other type you are already using C++ and you can use templates which are much better then macro in this situation.

Jantje:
5) You are right there is a risk with macros
On the content you are right that

 MyNumber=5; 

max(++MyNumber);


will result in MyNumber being 7 and not 6; This is however a design decision that has been taken a long time ago on very good ground by people that didn't know about Arduino because it simply didn't exist yet. So blaming Arduino is not the way to go. It has been like this for decades now. I feel no sense of urgency.

If you look at main arduino.cc page you ca read "It's intended for artists, designers, hobbyists, and anyone interested in creating interactive objects or environments". Time has changed, programming languages has changed.This design decision was made "a long time ago on very good " old ground. Artists, designers, hobbyists do not need carefully documented well known rakes. 20 years ago it was the best solution but now we have more fool proof programming technologies. Which are still as efficient as old ones. They can be even more efficient in some cases.

Jantje:
6) overloaded functions from C++ std
I see no reason why you could not overload. You can use the #undef instructions if you want.

Thanks for the hint. I've did it for my building environment.

Jantje:
4) why a macro?
A macro has a great advantage. That is, it works with any type that implements the methods you use. So you do not need a maxint maxlong maxstring.....

But macros also can have issues and create some intended consequences that functions do not.

Consider this example:

bar = sq(foo++);

How much is foo incremented?
foo is incremented twice when sq() is a macro and if it is a function then it is incremented only once
as most people would probably expect.

Other unexpected things happen if the data is not a simple variable type and is a function.
Consider this example:

bar = abs(foo());

The function foo() will be called multiple times if abs() is a macro vs only once if it is a function.
If foo() has the potential to return different values on each call, then the results become unpredictable.
When abs() is a macro, consider the case when the first call to foo() returns a negative value and
the second time it returns a positive value.
In this case the final result from abs() will be negative which nobody would expect from an abs() "function".

So there is a functional difference that can create non obvious issues when these "functions" are
implemented as macros vs real functions.

--- bill

Yah, I agree with the OP. Macros like these should have gone out with bell-bottoms. They conflict with the ::abs and friends definitions, and they lead to unexpected side-effects. Yes we could work around it with #undefs. You can work around many problems, that doesn't make it NOT a problem.

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.