Skip to content

Instantly share code, notes, and snippets.

@fosterbrereton
Last active January 16, 2026 19:36
Show Gist options
  • Select an option

  • Save fosterbrereton/1439318ab60e1d02d8f9315631ac7fd9 to your computer and use it in GitHub Desktop.

Select an option

Save fosterbrereton/1439318ab60e1d02d8f9315631ac7fd9 to your computer and use it in GitHub Desktop.
Companion source code for the article "Better Macros, Better Flags."
/*
MIT License
Copyright 2019 Foster T. Brereton
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and
associated documentation files (the "Software"), to deal in the Software without restriction,
including without limitation the rights to use, copy, modify, merge, publish, distribute,
sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all copies or substantial
portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT
NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES
OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*/
/**************************************************************************************************/
#include <iostream>
/***************************************************************************************************
# How to use this document
The code can be compiled simply:
```shell
clang++ -DBMBF_PRODUCT_CONFIG=DESKTOP better_macros_better_flags.cpp && ./a.out
```
It should print out a series of notes about compilation state.
`BMBF_PRODUCT_CONFIG` can be any one of `DESKTOP`, `MOBILE`, or `EMBEDDED`, and will produce
slightly different output.
***************************************************************************************************/
/*
PRODUCT macros denote what product (target) is being built. There is exactly one product defined
per translation unit. The product macro is defined at the project level, and must precede any
preprocessing.
*/
#define BMBF_PRODUCT_PRIVATE_DEFINITION_DESKTOP() 0
#define BMBF_PRODUCT_PRIVATE_DEFINITION_MOBILE() 1
#define BMBF_PRODUCT_PRIVATE_DEFINITION_EMBEDDED() 2
#define BMBF_PRODUCT_XSMASH(X, Y) X##Y()
#define BMBF_PRODUCT_SMASH(X, Y) BMBF_PRODUCT_XSMASH(X, Y)
#define BMBF_PRODUCT(X) \
(BMBF_PRODUCT_PRIVATE_DEFINITION_##X() == \
BMBF_PRODUCT_SMASH(BMBF_PRODUCT_PRIVATE_DEFINITION_, BMBF_PRODUCT_CONFIG))
/**************************************************************************************************/
/*
PLATFORM macros denote operating-system- or machine-level features. They are automatically
derived based on built-in preprocessor definitions defined at compile-time. It is common to have
more than one platform defined per translation unit.
*/
#define BMBF_PLATFORM_PRIVATE_DEFINITION_APPLE() 0
#define BMBF_PLATFORM_PRIVATE_DEFINITION_IOS() 0
#define BMBF_PLATFORM_PRIVATE_DEFINITION_IOS_SIMULATOR() 0
#define BMBF_PLATFORM_PRIVATE_DEFINITION_LINUX() 0
#define BMBF_PLATFORM_PRIVATE_DEFINITION_MACOS() 0
#define BMBF_PLATFORM_PRIVATE_DEFINITION_MICROSOFT() 0
#define BMBF_PLATFORM_PRIVATE_DEFINITION_POSIX() 0
#define BMBF_PLATFORM_PRIVATE_DEFINITION_UWP() 0
#define BMBF_PLATFORM_PRIVATE_DEFINITION_WIN32() 0
#define BMBF_PLATFORM(X) BMBF_PLATFORM_PRIVATE_DEFINITION_##X()
#if defined(_WIN32)
#undef BMBF_PLATFORM_PRIVATE_DEFINITION_MICROSOFT
#define BMBF_PLATFORM_PRIVATE_DEFINITION_MICROSOFT() 1
#if defined(WINAPI_FAMILY) && !WINAPI_FAMILY_PARTITION(WINAPI_PARTITION_DESKTOP)
#undef BMBF_PLATFORM_PRIVATE_DEFINITION_UWP
#define BMBF_PLATFORM_PRIVATE_DEFINITION_UWP() 1
#else
#undef BMBF_PLATFORM_PRIVATE_DEFINITION_WIN32
#define BMBF_PLATFORM_PRIVATE_DEFINITION_WIN32() 1
#endif
#elif defined(__APPLE__)
#include "TargetConditionals.h"
#undef BMBF_PLATFORM_PRIVATE_DEFINITION_POSIX
#define BMBF_PLATFORM_PRIVATE_DEFINITION_POSIX() 1
#undef BMBF_PLATFORM_PRIVATE_DEFINITION_APPLE
#define BMBF_PLATFORM_PRIVATE_DEFINITION_APPLE() 1
#if TARGET_OS_SIMULATOR
#undef BMBF_PLATFORM_PRIVATE_DEFINITION_IOS
#define BMBF_PLATFORM_PRIVATE_DEFINITION_IOS() 1
#undef BMBF_PLATFORM_PRIVATE_DEFINITION_IOS_SIMULATOR
#define BMBF_PLATFORM_PRIVATE_DEFINITION_IOS_SIMULATOR() 1
#elif TARGET_OS_IPHONE
#undef BMBF_PLATFORM_PRIVATE_DEFINITION_IOS
#define BMBF_PLATFORM_PRIVATE_DEFINITION_IOS() 1
#elif TARGET_OS_MAC
#undef BMBF_PLATFORM_PRIVATE_DEFINITION_MACOS
#define BMBF_PLATFORM_PRIVATE_DEFINITION_MACOS() 1
#endif
#elif defined(__LINUX__)
#undef BMBF_PLATFORM_PRIVATE_DEFINITION_POSIX
#define BMBF_PLATFORM_PRIVATE_DEFINITION_POSIX() 1
#undef BMBF_PLATFORM_PRIVATE_DEFINITION_LINUX
#define BMBF_PLATFORM_PRIVATE_DEFINITION_LINUX() 1
#endif
/**************************************************************************************************/
/*
FEATURE macros define what application-level features should be included in the compilation. The
set of features is always derived from a combination of the target product and platform(s).
There are any number of features defined per translation unit.
*/
#define BMBF_FEATURE_PRIVATE_DEFINITION_APPLE_DESKTOP() \
BMBF_PLATFORM(APPLE) && BMBF_PRODUCT(DESKTOP)
#define BMBF_FEATURE_PRIVATE_DEFINITION_MICROSOFT_MOBILE() \
BMBF_PLATFORM(MICROSOFT) && BMBF_PRODUCT(MOBILE)
#define BMBF_FEATURE_PRIVATE_DEFINITION_EMBEDDED_LINUX() \
BMBF_PLATFORM(LINUX) && BMBF_PRODUCT(EMBEDDED)
#define BMBF_FEATURE_PRIVATE_DEFINITION_METAL() \
BMBF_PLATFORM(APPLE) && !BMBF_PLATFORM(IOS_SIMULATOR)
#define BMBF_FEATURE(X) BMBF_FEATURE_PRIVATE_DEFINITION_##X()
/**************************************************************************************************/
int main(int argc, char** argv) {
#if BMBF_PRODUCT(DESKTOP)
std::cout << "Hello, Desktop!\n";
#endif
#if BMBF_PRODUCT(MOBILE)
std::cout << "Hello, Mobile!\n";
#endif
#if BMBF_PRODUCT(EMBEDDED)
std::cout << "Hello, Embedded!\n";
#endif
#if BMBF_PLATFORM(APPLE)
std::cout << "Hello, Apple!\n";
#endif
#if BMBF_PLATFORM(IOS)
std::cout << "Hello, iOS!\n";
#endif
#if BMBF_PLATFORM(IOS_SIMULATOR)
std::cout << "Hello, iOS Simulator!\n";
#endif
#if BMBF_PLATFORM(LINUX)
std::cout << "Hello, Linux!\n";
#endif
#if BMBF_PLATFORM(MACOS)
std::cout << "Hello, macOS!\n";
#endif
#if BMBF_PLATFORM(MICROSOFT)
std::cout << "Hello, Microsoft!\n";
#endif
#if BMBF_PLATFORM(POSIX)
std::cout << "Hello, POSIX!\n";
#endif
#if BMBF_PLATFORM(UWP)
std::cout << "Hello, UWP!\n";
#endif
#if BMBF_PLATFORM(WIN32)
std::cout << "Hello, Win32!\n";
#endif
#if BMBF_FEATURE(METAL)
std::cout << "Hello, Metal feature!\n";
#endif
#if BMBF_FEATURE(APPLE_DESKTOP)
std::cout << "Hello, Apple desktop feature!\n";
#endif
#if BMBF_FEATURE(MICROSOFT_MOBILE)
std::cout << "Hello, Microsoft mobile feature!\n";
#endif
#if BMBF_FEATURE(EMBEDDED_LINUX)
std::cout << "Hello, embedded Linux feature!\n";
#endif
}
@reneparis
Copy link

Hey Foster. Great article on FluentCpp and great solution. Right now, from tracing it back to the article, the snippet runs under Creative Commons license CC BY-NC-SA 4.0, "unless stated otherwise". I'd really love to see this snippet under e.g. MIT, so potential commercial use is not a showstopper. Would this be possible?

@fosterbrereton
Copy link
Author

I am glad to hear you got something valuable out of the article. I have added the MIT license to the top of the gist. Please let me know if this is not sufficient.

@bhaller
Copy link

bhaller commented Jan 16, 2026

Hi @fosterbrereton! I just discovered your blog post on this, and find it quite useful. I'm converting some preprocessor code in my project to use function-like macros so that I get an error if the macro is unintentionally undefined. That risk has always made me nervous; it's nice to have a clean solution!

I have a question, though. It seems like there is still a vulnerability, and I can't find a way around it. Suppose I have:

// header
#define MY_FUNC_LIKE_MACRO() 1

// .cpp
#if MY_FUNC_LIKE_MACRO
... do stuff ...
#endif

I've forgotten the parentheses, and this compiles just fine, and it does the wrong thing – the compiler thinks that MY_FUNC_LIKE_MACRO has not been defined, and so the stuff is not done. Is there a way to remove this vulnerability? I tried also defining MY_FUNC_LIKE_MACRO as something illegal, like:

// header
#define MY_FUNC_LIKE_MACRO     |
#define MY_FUNC_LIKE_MACRO()  1

so that use of MY_FUNC_LIKE_MACRO without the parens would error, but the compiler considers the MY_FUNC_LIKE_MACRO() definition as re-defining MY_FUNC_LIKE_MACRO; both definitions can't co-exist. What is really needed in C/C++ is a #if-like preprocessor directive that errors if the thing being tested is not defined. The above, with the missing parens, is really a special case of the more general problem that a typo will still silently do the wrong thing, like:

#if MY_FUN_LIKE_MACRO()
... stuff is not done because MY_FUN_LIKE_MACRO is a typo ...
#endif

It would be nice for typos like this to generate an error, somehow. Got a graceful solution?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment