top | item 7896428

(no title)

orangeduck | 11 years ago

Macros in C can sometimes be nasty, but "min" is a pathological case for a macro because it isn't what macros were meant for.

In reality "min" should just be a function, and you should write a separate function for each type you want to define it over. This is how this problem is solved in C, and almost all other languages. It just looks deceptive because C's native operators like "<" and ">" happen to be generic across some number types. This is just for convenience, it isn't how C is meant to be programmed. Pretty much every other language also makes you define different implementations of functions when you have a different type you want it to work on. Lots let you type the same function name, independent of the type - via generics, interfaces, or type-classes - but all call out to different implementations at the end of the day. Very few can derive those implementations.

As far as I am concerned, Macros in C are best used just to save on typing. They get a lot of hate because the syntax looks like functions - so people try to use them as functions. I don't recommend using them as functions, instead use them to save typing - because saving typing and avoiding repeating yourself can only reduce errors.

discuss

order

Someone|11 years ago

"min" is a pathological case for a macro because it isn't what macros were meant for.

Tell that to Kernighan and Ritchie. The second macro in K&R (I googled a PDF of the second edition) is

  #define  max(A, B)  ((A) > (B) ? (A) : (B))
Yes, that's lowercase 'max', and they even mention "This macro will work for any data type; there is no need for different kinds of max for different data types, as there would be with functions"

Macros certainly were intended for this, as it was the only way to guarantee that the compiler inlined code (probably the only way it ever inlined function calls)

And for the curious: the first macro they give is

  #define  forever  for(;;)
That certainly is what macros were meant for :-), and doesn't even save on typing.

But yes, in modern C, nobody would use a macro for this.

hornetblack|11 years ago

The issue is for some programmers propensity to use ++ and -- whenever they can. So you end up with

    max(a++, b--)
Which expands to

    ((a++)>(b--)?(a++):(b--))
creating unexpected results. The solution is, don't use return values of `++` and `--` in calls, unless you know it's a function, and always will be. Upper-case macros may help, but then you need to make sure all macros are upper case.

StefanKarpinski|11 years ago

> In reality "min" should just be a function, and you should write a separate function for each type you want to define it over. This is how this problem is solved in C, and almost all other languages.

This is not how it's solved in any languages with any degree of polymorphism. Of course, C is entirely devoid of polymorphism – except for its math operators like `<` and `+` – so having a version of `min` for every type you ever want to take a min of is how you're forced to do things. But no, `min` is not pathological – it's completely normal for math programming. If you look at math libraries written in monomorphic languages like C and Fortran – BLAS and LAPACK, or hell, just math.h – they are full of multiple nearly identical versions of functions that take different argument types and begin and/or end in different letters to indicate these types: `round`, `roundl`, `roundf`, etc. In LAPACK, it's completely conventional – "s" for single float, "d" for double float, "c" for single complex, "z" for double complex [1]. You'll note that the LAPACK naming scheme also encodes the type of matrix it operates on (there are 28 of them).

That's all fine, right? Well, it's fine if you're ok with writing every function four times. And that's only dealing with polymorphism in one argument. Hint: it's not at all uncommon for math functions to take multiple arguments. This is precisely why the first thing that a principled approach to mathematical programming does is allow polymorphism – ideally on multiple arguments. This is why people do math work in dynamic languages with lots of polymorphism, like Mathematica, Matlab, R and Python. Otherwise you can't get any work done without writing everything dozens of times – or struggling with C macro programming that makes this min puzzle look utterly tame. Sussman et al.'s SICM (SICP but for classical mechanics), not content with a dynamic language, immediately builds a multiple dispatch system on top of Scheme. R supports an awkward multiple dispatch system in one of its four object systems. This is why Guy Steele included multiple dispatch in Fortress. This is why Julia has multiple dispatch as its core paradigm. You need it for math. Badly.

[1] http://www.netlib.org/lapack/lug/node24.html

14113|11 years ago

exactly my attitude towards them - modern compilers will generally optimise simple functions like "min" so that they're just as efficient as macro equivalents.

For example, for a project that I did, I needed specific access methods for each member of a struct, and for each method to be written with a separate function. With macros, it was as easy as defining a macro to generate said function (given the member name), and then calling said macro for each member to generate the code.

zatkin|11 years ago

In my experience, some compilers will inline functions when it is an optimization (particularly if you declare them static inline).