let's imagine we have a C++ project that should work on several platforms (for example, arm/arm64) and we need to have target-specific values for each of them.
Right now we do:
#ifdef PLATFORM_ARM
#define TIMEOUT_VALUE 0
#define OUR_MAGIC_VALUE 1
#elif PLATFORM_ARM64
#define TIMEOUT_VALUE 2
#define OUR_MAGIC_VALUE 3
#endif
where could I place a comment for each defined name that it could be accessed from each definition?
Note: I can't define each value in its own #ifdef like
// a comment for TIMEOUT_VALUE
#ifdef PLATFORM_ARM
#define TIMEOUT_VALUE 0
#elif PLATFORM_ARM64
#define TIMEOUT_VALUE 2
#endif
// a comment for OUR_MAGIC_VALUE
#ifdef PLATFORM_ARM
#define OUR_MAGIC_VALUE 1
#elif PLATFORM_ARM64
#define OUR_MAGIC_VALUE 2
#endif
because I have lists and trees of such values.
Thank you.
Edit 1:
for example, we have 6 targets and 4 of them support a FEATURE,
so we write:
#if defined(ARM)
#define FEATURE 1
#elif defined(ARM64)
#define FEATURE 0
#elif define(MIPS)
#define FEATURE 1
etc... for other platforms.
then I have code that reads this define somewhere:
#if FEATURE
do something. Note that this part can't be described in a target specific file, because it can have the same implementation for several targets.
#endif
and now I want to have a place to describe in general what this FEATURE means and do.
You can define a proxy macro and write a single comment for macro to be used by end user:
#ifdef PLATFORM_ARM
#define TIMEOUT_VALUE_IMPL 0
#define OUR_MAGIC_VALUE_IMPL 1
#elif PLATFORM_ARM64
#define TIMEOUT_VALUE_IMPL 2
#define OUR_MAGIC_VALUE_IMPL 3
#endif
// a comment for TIMEOUT_VALUE
#define TIMEOUT_VALUE TIMEOUT_VALUE_IMPL
// a comment for OUR_MAGIC_VALUE
#define OUR_MAGIC_VALUE OUR_MAGIC_VALUE_IMPL
You may also consider using constants instead of macros.
Related
There is a file that I downloaded from the Unity web-site
#pragma once
// Standard base includes, defines that indicate our current platform, etc.
#include <stddef.h>
// Which platform we are on?
// UNITY_WIN - Windows (regular win32)
// UNITY_OSX - Mac OS X
// UNITY_LINUX - Linux
// UNITY_IOS - iOS
// UNITY_TVOS - tvOS
// UNITY_ANDROID - Android
// UNITY_METRO - WSA or UWP
// UNITY_WEBGL - WebGL
#if _MSC_VER
#define UNITY_WIN 1
#elif defined(__APPLE__)
#if TARGET_OS_TV //'TARGET_OS_TV' is not defined, evaluates to 0
#define UNITY_TVOS 1
#elif TARGET_OS_IOS //'TARGET_OS_IOS' is not defined, evaluates to 0
#define UNITY_IOS 1
#else
#define UNITY_OSX 1 //'UNITY_OSX' macro redefined
#endif
#elif defined(__ANDROID__)
#define UNITY_ANDROID 1
#elif defined(UNITY_METRO) || defined(UNITY_LINUX) || defined(UNITY_WEBGL)
// these are defined externally
#elif defined(__EMSCRIPTEN__)
// this is already defined in Unity 5.6
#define UNITY_WEBGL 1
#else
#error "Unknown platform!"
#endif
...
The problem is that when I try to include the file in my XCode project I got a warning (put them in comments)
...
#if TARGET_OS_TV //'TARGET_OS_TV' is not defined, evaluates to 0
#define UNITY_TVOS 1
#elif TARGET_OS_IOS //'TARGET_OS_IOS' is not defined, evaluates to 0
#define UNITY_IOS 1
#else
#define UNITY_OSX 1 //'UNITY_OSX' macro redefined
#endif
...
I tried to use #pragma warning(suppress: 4101) and a few similar approaches, but it doesn't help
UPD
...
#ifdef TARGET_OS_TV
#define UNITY_TVOS 1
#elif TARGET_OS_IOS //'TARGET_OS_IOS' is not defined, evaluates to 0
#define UNITY_IOS 1
#else
#define UNITY_OSX 1
#endif
...
Using ifdef helps to get rid of the first warning, but the second one is still in place
UPD2
...
#ifdef TARGET_OS_TV
#define UNITY_TVOS 1
#elifdef TARGET_OS_IOS //Invalid preprocessing directive
#define UNITY_IOS 1
#else
#define UNITY_OSX 1
#endif
...
You should not use #if to test an undefined macro. The warning implies that you should use #ifdef instead.
You may not define a previously defined macro. You could first undefined the old definition, but that's rarely a good idea.
Using ifdef helps to get rid of the first warning, but the second one is still in place
In c++23 you will be able to use #elifdef instead.
Otherwise, you can use #elif defined(the_macro).
What's the simplest way to ensure that only one of two names is defined, for example:
#define USE_OPTION1
#define USE_OPTION2
#if not(USE_OPTION1 ^ USE_OPTION2)
#error "You must use at least one option, but not both"
#endif
I know there's no logical XOR in C or C++, so what's the best way of doing this? It doesn't have to be this, does it:
#define USE_OPTION1
#define USE_OPTION2
#ifdef USE_OPTION1
#ifdef USE_OPTION2
#error "You can't use both"
#endif
#endif
#ifdef USE_OPTION2
#ifdef USE_OPTION1
#error "You can't use both"
#endif
#endif
#ifndef USE_OPTION1
#ifndef USE_OPTION2
#error "You must use at least one"
#endif
#endif
you can solve this by checking if both are defined equally. so 0 0 and 1 1 will throw the error, while 0 1 and 1 0 are allowed.
#if defined(USE_OPTION1) == defined(USE_OPTION2)
#error "You must use at least one option, but not both"
#endif
I'm trying to compile a project using a library I made on Windows, using MinGW 4.8.1 x86. The project compiles fine on Linux.
Common.hpp is included before everything else, and defines some macros depending on the current OS. Then ConsoleFmt.hpp is included, and it includes a file depending on the previously defined macros.
I'm getting an error, however - here's the code and the error message:
Common.hpp
#if (__linux || __unix || __posix)
#define SSVU_OS_LINUX
#elif (_WIN64 || _WIN32)
#define SSVU_OS_WINDOWS
#else
#define SSVU_OS_UNKNOWN
#endif
ConsoleFmt.hpp
#include "Common.hpp"
#ifdef SSVU_OS_LINUX
#include "SSVUtils/Core/ConsoleFmt/Internal/ConsoleFmtImplUnix.hpp"
#elif SSVU_OS_WINDOWS
#include "SSVUtils/Core/ConsoleFmt/Internal/ConsoleFmtImplWin.hpp"
#else
#include "SSVUtils/Core/ConsoleFmt/Internal/ConsoleFmtImplNull.hpp"
#endif
Error:
error: #elif with no expression:
#elif SSVU_OS_WINDOWS
Is my code invalid or is there a bug in MinGW? I think I'm using #elif correctly.
#elif is a contraction of #else and #if, not #else and #ifdef, so your line should read:
#elif defined(SSVU_OS_WINDOWS)
Edit: Because undefined macros expand to 0 in #if expressions, you could also use the original variant and define the active macro with a value of 1. (As hvd has just posted and explained.)
The safe solution, IMO, is to define SSVU_OS_* in such a way that it doesn't matter whether you use #if or #ifdef.
#if (__linux || __unix || __posix)
#define SSVU_OS_LINUX 1
#elif (_WIN64 || _WIN32)
#define SSVU_OS_WINDOWS 1
#else
#define SSVU_OS_UNKNOWN 1
#endif
This lets your #ifdef/#elif work already as it is. For consistency, you can then clean that up to
#if SSVU_OS_LINUX
#include "SSVUtils/Core/ConsoleFmt/Internal/ConsoleFmtImplUnix.hpp"
#elif SSVU_OS_WINDOWS
#include "SSVUtils/Core/ConsoleFmt/Internal/ConsoleFmtImplWin.hpp"
#else
#include "SSVUtils/Core/ConsoleFmt/Internal/ConsoleFmtImplNull.hpp"
#endif
but it isn't strictly necessary.
Making sure your macros work without needing #ifdef/defined checks allows for simpler expressions if you combine multiple checks (like you already do with the macros of others: you check multiple using ||).
To explain why it works in Linux but fails in Windows:
#elif (_WIN64 || _WIN32)
If _WIN64 has been defined then it gets replaced, e.g. if these had been defined
#define _WIN64
#define _WIN32
then the first line expands to
#elif ( || )
which would generate an error. However, if those tokens were not defined at all, they get replaced by 0:
#elif (0 || 0)
which is OK.
Question has been answered with VC and GCC in
How do I show the value of a #define at compile-time?
but is it possible to do it with ARM RVCT?
Actually I can do my own macro2string convertion as RVCT doesn't have stringification support. But I didn't even find "#pragma message" support in RVCT. It seems it has only something like
#pragma diag_error 223
which you must specify a tag, you can not freely output a string.
In fact I work on some legacy code now, here is an simplified example from the code base:
in product_conf.h:
#define VALUE_A 1
in platform_conf.h:
#ifndef VALUE_A
#define VALUE_A 2
#endif
in component_conf.h:
#ifndef VALUE_A
#define VALUE_A 3
#endif
in component.c:
#include product_conf.h
#include platform_conf.h
#include component_conf.h
It is a bit difficult to know VALUE_A is 1 or 2 or 3 when you are reading the component.c, actually in the real cases there can be 4~5 layers configurations and the c files may include or not include some certain conf.h, you have to go through the different header files case by case.
So I thought something like:
/* definition to expand macro then apply to pragma message */
#define VALUE_TO_STRING(x) #x
#define VALUE(x) VALUE_TO_STRING(x)
#define VAR_NAME_VALUE(var) #var "=" VALUE(var[[)]]
#pragma message(VAR_NAME_VALUE(VALUE_A))
will help for a quick check, I just make the component and I will find out what is defined in the compiling output. This is doable with GCC, but I want to know how to do similar things with ARM RVCT.
or the only way to do it is:
#if (VALUE_A==1)
#warning "VALUE_A is 1"
#elif (VALUE_A==2)
#warning "VALUE_A is 2"
#elif (VALUE_A==3)
#warning "VALUE_A is 3"
#else
#error "VALUE_A is not properly defined!"
#endif
Can somebody explain the following code please?
#if 1
// loop type
#define FOR_IS_FASTER 1
#define WHILE_IS_FASTER 0
// indexing type
#define PREINCREMENT_IS_FASTER 1
#define POSTINCREMENT_IS_FASTER 0
#else
// loop type
#define FOR_IS_FASTER 1
#define WHILE_IS_FASTER 0
// indexing type
#define PREINCREMENT_IS_FASTER 0
#define POSTINCREMENT_IS_FASTER 1
#endif
#if PREINCREMENT_IS_FASTER
#define ZXP(z) (*++(z))
#define ZX(z) (*(z))
#define PZ(z) (++(z))
#define ZP(z) (z)
#define ZOFF (1)
#elif POSTINCREMENT_IS_FASTER
#define ZXP(z) (*(z)++)
#define ZX(z) (*(z))
#define PZ(z) (z)
#define ZP(z) ((z)++)
#define ZOFF (0)
#endif
I can understand what the functions are doing but for example
how does the pre-processor choose which ZXP will be execute if we call it later?
What do the 1 and 0 stand for?
The #if 1 triggers the first group of #defines, which set PREINCREMENT_IS_FASTER to 1. Because of this, #if PREINCREMENT_IS_FASTER triggers the first #define ZXP....
There is nothing exceptional about 1 and 0 in this context. The #if preprocessor directive succeeds if its argument is non-zero.
You can switch to the alternate form by changing the #if 1 at the top of the file with #if 0. (Thank you #rabidmachine for the tip.)
I'm probably inclined to agree with UncleBens and suggest that it's done so that you don't understand it, because the whole lot is totally useless.