Hacker News new | ask | show | jobs
by cryptonector 1779 days ago
You might as well have written that "any time you're reaching for C, it's time to reach for a more powerful language".

But if -sadly- you must use C, metaprogramming using macros is not a terrible thing.

1 comments

> is not a terrible thing

It is a terrible thing. It's possible to do without them, and you'll like your code better. Your symbolic debugger and syntax directed editor will work properly. The poor schlub who has to fix the bugs in your code after you leave will be grateful. Your spouse will be happy and your children will prosper.

For example,

    #define foo(x) ((x) + 1)
replace with:

    int foo(int x) { return x + 1; }
The compiler will inline it for you. There's no penalty.

    #define FOO 3
Replace with:

    enum { FOO = 3 };
or:

    const int FOO = 3;
Replace:

    #if FOO == 3
    bar();
    #endif
with:

    if (FOO == 3) bar();
The optimizer will delete bar() if FOO is a manifest constant that is not 3.
Having made a significant contribution to Simon Tatham's PuTTY, I have to respectfully disagree. At the time the entire SSHv2 key exchange and user authentication protocols were implemented in a single function using a massive Duff's device (for asynchrony) implemented with C metaprogramming macros. It was a surprisingly pleasant experience.
There is a reason why such clevernesses are forbiden by security standards like MISRA-C.
Agree except the last example - the clarity of intent that you want a conditional compilation is lost. Also, non-optimized debug builds are affected.
I think it was RMS who argued that conditional compilation should be considered harmful in general. Code that you put in an inactive #if(def) block will not be maintained, and is basically guaranteed to rot. If it's needed in the future, it'll likely have to be rewritten from scratch.

According to this stance, any code that's suppressed by the C preprocessor should either be written in an if {} statement so that it will at least continue to compile as the surrounding code changes, or be replaced with comments describing what it does (or did), if it's important enough to keep track of.

Can't really think of many good counterarguments to this. Machine dependence might be one, but then you could argue that the preprocessor is being used to cover up for an inadequate HAL.

When it comes to cross-platform code, you build for several platforms, so the block is active (for particular platforms) and maintained.
> clarity of intent

Conditional compilation is an optimization, not a semantic intent.

> non-optimized debug builds are affected

The code size will be larger. Doesn't matter.

> Conditional compilation is an optimization, not a semantic intent.

Why can't it have semantic intent? I frequently use it for cross-platform adjustments, and those don't usually compile on the defined-away platform.

> those don't usually compile on the defined-away platform

Then you're doing it wrong :-/

All these can be made to work.

P.S. compiling successfully is not the same thing as linking successfully. Think stubs and deciding which files to link together.

Making it clear in the code itself that something is platform specific is useful as well. Also, I'd say such stubs and extra files would clutter the project.
They can be made to work by introducing stubs for every os/arch/compiler/dependency I support, but that's way more work for dubious gain.

And who is to say I'm doing it wrong? Just because conditional compilation CAN lead to a rat nest doesn't mean it MUST. And if it happens to be well-organized and works as is, there is no problem.

   The optimizer will delete bar() if FOO is a manifest constant that is not 3. 
Yes, but the compiler won't delete it and complain if bar is not defined. if constexpr is not a direct replacement for if macro
> the compiler won't delete it and complain if bar is not defined

    extern void bar();
will define it to the satisfaction of the compiler. The linker won't complain if the compiler removes the call to it.
And then you have the issue of not having types. consider this

  void my_bar_class_type::bar(my_bar_type<my_bar_type_2>::my_bar_inside_type paramater);
and you are calling something like

   myObject->bar(mySecondObject.getWhatever());
You have to mock up like everything
Or go up an abstraction layer so the type details are not needed.