The C++ preprocessor doesn’t understand anything about C++, including templates
devblogs.microsoft.com
devblogs.microsoft.com
I can tell you exactly why I use batch scripts—they're the only type of script that will run on double click in a stock Windows install.
Powershell scripts won't by default for security reasons, but for some reason for batch it's okay. So I use batch.
Locking down batch files, vbs and js scripts can be easily done at AD level and let people be pissed off with IT instead.
Here how to disable it, https://www.computerhope.com/issues/ch001005.htm
1) parse the source file
2) find the debug info and read it
3) hack the compiler to do this for you
Even for conditional compilation I have always been a fan of using module_os_arch.cpp than dealing with pre-processor spaghetti.
The tools are there, the problem is getting people to use them instead of doing C like coding, assuming access to C++17/20 compilers.
As for C, well I don't have high hopes that when WG14 doesn't care about improving security, they will ever do anything regarding the pre-processor, specially since it is such a crucial part of C development.
That's what's come down to doing in the firmware I write in C. Has the advantage that you don't need to worry as much about other platforms when dicking with platform specific code. With preprocessor spaghetti it's really easy to break other platforms otherwise. That results in most of preprocessor usage is just switching include files.
So lets say you have usb module to talk with USB devices.
It would be composed of the following files:
- usb.h and usb.cpp for the public interface and os agnostic code, including function declarations for the OS specific parts.
- usb_windows.cpp, usb_linux.cpp, ... for OS specific code
- usb_windows_x86.cpp, usb_windows_arm.cpp,... for inline Assembly, intrisics or hardware specific data structures.
Then you get Make, CMake or whatever is your favourite build tool to combine those files depending on the build target.
Pre-processor usage for OS specific code always starts easy on the beginning, but when you look at a bunch of files with 20 years of history and continuous change by consulting companies you need several days just to understand what is actually being generated when dropped into a random code base.
This way is it much easier to navigate across the code.
It is also the approach adopted by Go, and a couple of other languages without direct support for pre-processors.
Oh no, I found that to work very very poorly. It's an antipatterns because architectures change, and that you can't cover all architectures.
Not only for my own software (I've maintained portable open source for decades across many architectures), but also huge frustration when porting software to a new (for that software) architecture.
Examples:
The software should not care if it's "linux" or "freebsd", you care if getpwnam_r is the three or four argument version. When one OS changes (or admin tweaks it) to the POSIX compliant API by default, what do you do then?
OpenBSD is API wise close to FreeBSD. So now I should find all files named ._freebsd_. and make a copy to _openbsd_, forking the code, if I port to OpenBSD?
What about Debian kFreeBSD? You don't have all of these OSs, so just test features, not OS.
This is the way I'm going about it at least, even though I assume there might be complex situations where the boilerplate isn't warranted and it's better to put all the variants in a single file and use the preprocessor to determine which alternative to compile.
Like you say in the end, test features (or maybe standards support), not too-specific things like OS or distribution.
Yes, I entirely agree. But do NOT split it into what OS or architecture it is, like what I was replying to said.
Naturally one has to make the groupings in a way that makes sense, and the code organisation is not set in stone.
Refactorings can and should be done when it makes sense to do so.
My experience doing this is exactly why I'm saying do NOT split into OS and arch.
Many times what should have "just worked" without even needing porting by anyone, turns into a multi thousand line refactoring just because of hard coded operating system names in the source code.
Let's say you have your sound code in sound_linux.cc and sound_freebsd.cc (and sound_openbsd.cc, and...) and then 10 years from now freebsd switches to entirely using ALSA. Your code now breaks. If you'd instead done "sound_alsa.cc" and detected the ability to use ALSA at build time, then zero coding needs to be done.
The one exception I think is Windows. It's uniform enough over time, and entirely different from everything else, that it does make sense to have sound_windows.cc.
There are antipatterns other than foo_os_arch.cc, such as the one you point out (nested mess of #ifdefs in code), but that doesn't invalidate that module_os_arch isn't an antipattern. Because it is.
Go (the language) does "list of OS names", and it's a complete mess. Syscalls that actually are the same are not in the std library, because they were split by OS name. Had they tested for features the implementation would have been the same. And this hurts all users of that language as a result.
We need more developers reading books like " Large-Scale C++ Software Design".
It is such an anti-pattern that C and C like C++ codebases are the only ones that go down the #ifdef spaghetti route, with large majority of everyone else using modules to isolate architecture dependences.
So not sure about it being an anti-pattern when it is the architecture design pattern with winning votes.
Like I said the Go way is by far worse. Not sure if you're familiar with how this works in Go.
> So not sure about it being an anti-pattern
Well it is. I've given you enough examples.
Well, it is an anti-pattern for you, not for those that write best practice large scale architecture design books, and the large majority of language designers out there that have created programming languages that eschew C like pre-processors.
So I guess better leave at that, because we aren't going to agree.
#script javascript
// put your script here, have it change the parse tree, etc.
#endscript
Microsoft compilers could support VBscript. High-performance options include webassembly, the JVM, and the .net runtime.But whoever thought a language with no comments was a good idea -- means I can actually write comments in JSON files.
Getting rid of it is hard, but replacing many uses of it with a more capable codgen/templating your definition of sensible is very simple, just a make rule or two.