6 ms·
"sqr is a place where one might be tempted to use a macro in C, but writing such a macro in a way that x doesn't get evaluated twice is tricky." Slightly off-t
by kstenerud 7y ago
"sqr is a place where one might be tempted to use a macro in C, but writing such a macro in a way that x doesn't get evaluated twice is tricky."
Slightly off-topic, but it's important to point out that in modern C, implementing such a function as a macro is always a mistake. You'd do it as an inline function.
Macros in modern C should only be used for code generation (for DRY). If the language supports doing it without a macro, then do it without a macro.
- LandR 7y agoSame advice in LISP. Don't use a macro where a function will do.
- petrogradphilos 7y agoNever use a macro to do an inline function’s job.
- jnordwick 7y agoThe point for doing it as a macro is to be generic over types. In C++ you would do a template function, but no real option in C.
- gallier2 7y agoSince C11 there's _Generic which allows to do it. Using macros in that case is strongly advised (or else it would be very verbose).
- GlitchMr 7y agoYou don't need macros for generic code either :), Rust has generics. fn sqr<T: Copy + Mul>(x: T) -> T::Output { x * x }
- simias 7y agoThat's true, there are many situations where you simply can't replace a macro with a function because of this fact. MIN/MAX is a common example, unless you want to have one specialized version for every type. Unfortunately there are always tradeoffs using macros for something like that, you either end up evaluating the parameters more than once or you have to introduce variables in the macro that may shadow existing variables. Some compilers have extensions to help with that: https://gcc.gnu.org/onlinedocs/gcc/Statement-Exprs.html https://gcc.gnu.org/onlinedocs/gcc/Statement-Exprs.html
- oefrha 7y ago> you have to introduce variables in the macro that may shadow existing variables. You certainly don’t have to introduce the risk of shadowing existing variables. You just use the standard #define MACRO(x) \ do { \ /* Define whatever variables you like */ \ /* Do things... */ \ } while (0)
- jlokier 7y agoFirst, you can't do that for the macros like MIN/MAX mentioned in the comment you just replied to, and sqr mentioned earlier. Second, you still risk shadowing existing variables, even if you make an effort to use "unlikely" names for the locals such as "___my_macro_secret_variable_1", if the macro might be used in one of its own arguments. For example MACRO(MACRO(x)). If that sounds unlikely, consider MAX(a,MAX(b,c)), which is likely to happen eventually, if your codebase uses such macros or if they are part of a library.
- oefrha 7y agoWhen you decide to define new variables you have already decided to write more than just an expression (which by definition can’t contain a statement), so “you can’t do that for...” is a moot point. And since it’s not an expression, it’s not gonna appear as its own argument, unless you’re using more advanced substituting a whole block trick which is not what the do while(0) idiom is for.
- simias 7y agoI can't make sense of what you're saying. See the page I linked in my previous post for an example of what we're talking about: https://gcc.gnu.org/onlinedocs/gcc/Statement-Exprs.html https://gcc.gnu.org/onlinedocs/gcc/Statement-Exprs.html #define maxint(a,b) \ ({int _a = (a), _b = (b); _a > _b ? _a : _b; }) >Note that introducing variable declarations (as we do in maxint) can cause variable shadowing [...] this example using maxint will not [produce correct results]: int _a = 1, _b = 2, c; c = maxint (_a, _b);
- nguoi 7y agoInline was added in C99, which MSVC still doesn't support entirely. If this has to be taken into account when you choose what standard to use for your codebase, that's a quarter of a century trickle down for features to reach the consumer. I hope I get to use C2x* before I retire. *postmodern C?
- flohofwoe 7y agoThe Microsoft C compiler actually has pretty good C99 support since VS2015 (e.g. inline is definitely supported). AFAIK the only non-supported C99 features in the C compiler are VLAs (optional since C11 anyway), and type-generic macros (those would be good to have though). Of course it would be nice if Microsoft gave the C compiler a bit more love, especially since it's much less work keeping a C compiler uptodate than a C++ compiler, but at least we got "most of C99".
- jlokier 7y agoThe quarter of a century thing does not apply to "inline". Although inline was added in C99, it was already an extremely widely supported extension in mainstream compilers, even since the C89 days, when we just called it "ANSI C". MSVC has supported inline for a long time, long before it started supporting other C99 features.
- nguoi 7y agoThat doesn't matter if features you're able to used are gated on the standard you use. If the standard you choose is based on what your target platforms 'support': no inline for you.
- jlokier 7y agoIt seems weird to decide that you won't use features because they are in a public standard that your target platform doesn't support, even though your target platform fully supports those features themselves. I could understand the concern if it was about portability to other target platforms, or keeping the option of doing so. But in that case, the public standard your current target supports is irrelevant.
- flohofwoe 7y ago...or link with LTCG / LTO, this automatically considers all functions for inlining across compilation units, no matter if they are marked as inline or not. For C code this is preferable IMHO because it allows to keep declaration and implementation code strictly separate, inline functions muddy that line because they must live in headers.
- pingyong 7y agoWhile I vastly prefer this workflow wise, for large projects it can increase compile time drastically. https://lwn.net/Articles/744507/ https://lwn.net/Articles/744507/ For projects that will never have more than ~1 million LOC it's probably fine. Less than ~100K, definitely preferable.
- Avamander 7y agoThose large projects benefit the most from LTO as well though.
- mikepurvis 7y agoWhat about ThinLTO? The idea of having a overnight full build that creates some kind of cache which incremental builds throughout the day can use to do fast whole-binary LTO seems like the best of both worlds. Basically just wondering if anyone has first hand experience using this kind of thing on a large project.
- rwem 7y agoThinLTO was written by people responsible for peak optimization of very large programs at Google. I honestly can’t call it “fast” but it’s a lot faster than GCC LTO. You normally only peak-optimize your release builds so it’s not like developers are sitting there counting the seconds.
- derefr 7y ago> You normally only peak-optimize your release builds so it’s not like developers are sitting there counting the seconds. Sometimes you do want to debug a release build because the optimizations have gone wrong, though. In that case, it's helpful if release builds build quickly, and especially if they rebuild quickly as you turn various sub-optimization flags on and off.