7 ms·
It bears noting that at some point, you will likely need to #ifdef. There's no way around it for non-trivial code. Simple example: Getting the size of a file if
by beefhash 8y ago
It bears noting that at some point, you will likely need to #ifdef. There's no way around it for non-trivial code. Simple example: Getting the size of a file if said file is greater than the maximum size of long, meaning you can't fseek to the end and use ftell(); you'll need platform-specific APIs like stat(2) or GetFileSizeEx().
You can also do the platform checks in the build system instead and have multiple per-platform C files implement APIs for portability shims -- but given that the platform check is likely unportable (at least in make as specified by POSIX), you're just making different portability assumptions.
- dvfjsdhgfv 8y ago> You can also do the platform checks in the build system instead and have multiple per-platform C files implement APIs for portability shims -- but given that the platform check is likely unportable (at least in make as specified by POSIX), you're just making different portability assumptions. And yet, it seems that's what the authors of the article did: The best method of managing system-specific variants is to follow those same basic principles: define a portable interface to suitably-chosen primitives, and then implement different variants of the primitives for different systems. The well-defined interface is the important part: the bulk of the software, including most of the complexity, can be written as a single version using that interface, and can be read and understood in portable terms.
- avar 8y agoI may be wrong, but I was under the impression that this was portable on all POSIX systems: uname_S := $(shell sh -c 'uname -s 2>/dev/null || echo not') ifeq ($(uname_S),Linux) HAVE_LINUX_SPECIFIC_STUFF = Yes endif This is how git does its conditional compilation purely powered by Makefile logic: https://github.com/git/git/blob/master/config.mak.uname https://github.com/git/git/blob/master/config.mak.uname
- LukeShu 8y agoThat requires GNU Make; neither $(shell) nor ifeq are in POSIX Make.
- jwilk 8y ago":=" assignments are not in POSIX either.
- jstanley 8y agoMakefile logic is basically just an ifdef one level up.
- chris_wot 8y agoSo what do you do then?
- convolvatron 8y agoany place you have to parameterize signatures or behaviors or types, calling it something meaningful and define it in a platform/compiler/os specific header. sure, in the end its the the same thing, but i find it to be easier to manage over time. and when you need to port to a new platform/compiler/os, you have a list of the important dependencies at hand...maybe you have to define a new one
- jstanley 8y agoI don't consider ifdefs harmful. But if you do, you must surely consider logic in Makefiles equally harmful.
- chris_wot 8y agoWhy so?
- monocasa 8y agoBut your per platform build logic pretty much has to exist anyway on any non-trivial project, as differences in compilers (even different versions of the same compiler) crop up. Better to just stick that distinction all in one place, IMO.
- MaulingMonkey 8y agoWhich is exactly why I'll often try and keep build configuration in C++ #ifdef s. Everyone wants/needs proper Visual Studio projects in gamedev. And XCode projects. Maybe makefiles on top of that for some of your other platforms. Maybe a project generator to generate it all. Maybe some .props files to express things that the project generator can't. And then CI build server configuration on top of that. And then, because you're trying to another CI solution, a second CI build configuration. It's a mess of multiple unrelated tools - so it will never be in "one place" at the build system level. Shoving it in there anyways causes tons of duplicate build configuration logic. Alternatively, I could minimize the configuration in there - to just the bare minimum needed to drive high level configuration - and then centralize all the derived configuration in the one thing that's shared between them all: C++. C++ is a common denominator among my coworkers. When they have to dig through Gradle or Ant configuration because we're doing Android builds, I become a bottleneck. Lame. Yet another project generator? Ends up on my plate again. Lame. My coworkers have legitimately better things to be doing than becoming build system experts. They already understand "hey, I can pull configuration #define s into a separate file and only rebuild the things that #include it", meaning minor settings tweaks don't invalidate several build hours worth of compiled translation units, something I still don't know how to effectively do in some build systems.
- jwilk 8y ago> $(shell sh -c 'uname -s 2>/dev/null || echo not') Is there a reason they don't use simpler: $(shell uname -s 2>/dev/null || echo not) ?
- JdeBP 8y ago* https://git.kernel.org/pub/scm/git/git.git/commit/Makefile?id=f7c153431b4049e7697664dee4a3d71013422f9d https://git.kernel.org/pub/scm/git/git.git/commit/Makefile?i...
- comex 8y agoAck… please don’t do this. It will break if you’re cross-compiling binaries for a different OS than the OS you’re building on. If you want to do per-OS configuration in a Makefile or some type of configure step, that’s fine – but you should do it either based on a target triple (--host in autoconf-speak) which can be overridden by the user, or by running the given compiler on test programs and checking the output (like autoconf does, but ideally with one or two tests rather than a bazillion of them).
- Annatar 8y agoNo; this is GNUmake specific syntax. The simplest and cleanest thing to do is to write a Makefile per uname(1).compiler, like so: Makefile.Linux.SunStudio Makefile.Linux.icc Makefile.Linux.PGI Makefile.Linux.GCC Makefile.AmigaOS.vbcc Makefile.AmigaOS.DiceC Makefile.AtariTOS.gcc Makefile.Spearmint.gcc Makefile.illumos.gcc Makefile.illumos.SunStudio ...and so on. The source code and generic targets like clean: would go into a central Makefile.core which would then be included by the OS.compiler specific Makefile. The OS specific Makefile would only set $CC, $CXX, $FC, $CFLAGS, $CXXFLAGS, $FFLAGS and so on. This way, no configure system would be needed and adding support for a new $OS.compiler suite would be trivial. The code would have to be so clean as to allow compilation by any compiler of course.
- ape4 8y agoYes, nobody likes using #ifdef but if you need it, its great that its there
- pjmlp 8y agoHence, why the abstraction translation units should be named according to their abstraction level. Meaning module_posix would be for all POSIX platforms, whereas module_aix would be Aix only.
- Avamander 8y agoLet's not forget microcontrollers, the moment one wants to write something more universal (different MCU support) using ifdefs is basically unavoidable.
- pjmlp 8y agoNot when using one translation unit per MCU and letting the Makefile pick the right one. Back on my C days, I was able to write portable code between Windows NT, Windows 2000, Red-Hat Linux, Debian Linux, Aix, HP-UX, Solaris where the #ifdef were only used as include guards, regarding my own code.
- monocasa 8y agoSeconded. And it's only gotten better, as uc code is about the best case for LTO being both small and all in one final image. Now there's not even the specious argument about "artificial" compilation unit boundaries hurting performance terribly.
- namibj 8y agoAlso, you'd be surprised how heavy software can get without outgrowing single-unit LTO, as long as you don't try to pull in all of Qt/GTK or similar bloat in. (I don't see those as a bad choice, but they are too heavywight for this style of LTO to work well.)
- Avamander 8y ago> Not when using one translation unit per MCU and letting the Makefile pick the right one. Could you please link any examples of that I could examine myself? Also, makefiles aren't really an option with platforms like Arduino, the IDE does not really support them.
- namibj 8y agoErr, you can use the Arduino ecosystem with gnu make.... look for it, there is documentation, which does have ease of use to wish for and requires installation that drives me towards believing in chroot.
- yzmtf2008 8y agoSimilarly, if you’re writing an native extension targeting multiple versions of ruby, python, etc., ifdefs are unavoidable.