Radium Engine  1.5.0
CoreMacros.hpp
1 /* ========================================================================= *
2  * CoreMacros.hpp *
3  * core helper functions and macros for C and C++ *
4  * Valentin Roussellet *
5  * ========================================================================= */
6 
7 // This file contains a host of helper functions and macros that should behave
8 // similarly on all platforms. They may be generally useful or helping to
9 // abstract compiler / architecture / platform specificities to write portable
10 // code when possible.
11 
12 // A good reference : http://sourceforge.net/p/predef/
13 // clang-format off
14 #pragma once
15 
16 #include <cassert>
17 #include <cstdio>
18 #include <iostream>
19 #include <sstream>
20 
21 // ----------------------------------------------------------------------------
22 // Compiler identification
23 // ----------------------------------------------------------------------------
24 #if defined(__clang__)
25 #define COMPILER_CLANG
26 #elif defined(__GNUC__)
27 #define COMPILER_GCC
28 #elif defined (_MSC_VER)
29 #define COMPILER_MSVC
30 #define _USE_MATH_DEFINES
31 #else
32 #error unsupported compiler
33 #endif
34 
35 // ----------------------------------------------------------------------------
36 // OS and architecture identification
37 // ----------------------------------------------------------------------------
38 
39 #if defined(_WIN32) || defined(_WIN64) // ------------------------------ Windows
40 #define OS_WINDOWS
41 # if defined(_M_X64)
42 # define ARCH_X64
43 # elif defined(_M_IX86)
44 # define ARCH_X86
45 # else
46 # error unsupported arch
47 # endif
48 #elif defined(__APPLE__) || defined(__MACH__) // ------------------------ Mac OS
49 # define OS_MACOS
50 #elif defined(__linux__) || defined (__CYGWIN__) // ---------------------- Linux
51 # define OS_LINUX
52 #else
53  #error unsupported OS
54 #endif
55 
56 // Check arch for macos and linux
57 #if defined (OS_MACOS) || defined(OS_LINUX)
58 # if defined(__i386__)
59 # define ARCH_X86
60 # elif defined(__x86_64__) || defined (__x86_64)
61 # define ARCH_X64
62 # elif defined(__arm__) || defined (__arm)
63 # define ARCH_ARM32
64 # elif defined(__aarch64__) || defined(__aarch64)
65 # define ARCH_ARM64
66 # else
67 # error unsupported arch
68 # endif
69 #endif
70 
71 // Todo : endianness, pointer sixe
72 
73 // ----------------------------------------------------------------------------
74 // Build configuration
75 // ----------------------------------------------------------------------------
76 
77 // This tells apart between debug and release builds :
78 // DEBUG is defined in debug builds and RELEASE in release builds.
79 // Additionally REL_DEB is defined on release build with debug info
80 // Also the macro ON_DEBUG() can be used to execute an expression only on debug.
81 // By default, debug has assert macros enabled. In release builds
82 // asserts are disabled except if explicitly required by
83 // defining CORE_USE_ASSERT
84 
85 
86 // Make sure all "debug" macros are defined
87 #if defined (DEBUG) || defined(_DEBUG) || defined (CORE_DEBUG) // ------- Debug
88 # undef CORE_DEBUG
89 # define CORE_DEBUG
90 
91 # undef _DEBUG
92 # define _DEBUG
93 
94 # undef DEBUG
95 # define DEBUG
96 
97 # undef NDEBUG
98 # undef RELEASE
99 
100 # define ON_DEBUG(CODE) CODE
101 #else // --------------------------------------------------------------- Release
102 
103 # define RELEASE
104 
105 #ifndef NO_DEBUG_INFO
106 # define REL_DEB
107 #endif
108 
109 # undef CORE_DEBUG
110 # undef DEBUG
111 # undef _DEBUG
112 
113 # if !defined (NDEBUG)
114 # define NDEBUG
115 # endif
116 
117 # define ON_DEBUG(CODE) /* Nothing */
118 #endif
119 
120 // ----------------------------------------------------------------------------
121 // Multithreading
122 // ----------------------------------------------------------------------------
123 #ifndef RA_MAX_THREAD
124 # include <thread>
125 # define RA_MAX_THREAD std::thread::hardware_concurrency() - 1
126 #endif
127 
128 // ----------------------------------------------------------------------------
129 // Preprocessor magic
130 // ----------------------------------------------------------------------------
131 
132 // Wrapper for multiline macros
133 // In debug we use the standard do..while(0) which is 'nice' to read and debug
134 // in a debugger such as gdb.
135 // In release we use the if(1) else; which compilers can optimize better
136 #ifdef CORE_DEBUG
137 # define MACRO_START do {
138 # define MACRO_END } while (0)
139 #else
140 # define MACRO_START if(1) {
141 # define MACRO_END } else {}
142 #endif
143 
144 // Macro to avoid the "unused variable" warning with no side-effects.
145 #define CORE_UNUSED(X) \
146  MACRO_START \
147  (void) sizeof((X));\
148  MACRO_END
149 
150 // Token concatenation
151 // Preprocessor concatenation can be tricky if arguments are macros unless
152 // "recursively" calling concatenation through chained macros which forces
153 // the preprocessor to run another pass.
154 #define CONCATENATE(XX,YY) CONCATENATE2(XX,YY)
155 #define CONCATENATE2(XX,YY) CONCATENATE3(XX,YY)
156 #define CONCATENATE3(XX,YY) XX##YY
157 
158 // Stringification has a similar problem.
159 #ifdef __STRING
160 # define STRINGIFY(X) __STRING(X)
161 #else
162 # define STRINGIFY(X) STRINGIFY2(X)
163 # define STRINGIFY2(X) #X
164 #endif
165 
166 // ----------------------------------------------------------------------------
167 // Platform abstracted macros
168 // ----------------------------------------------------------------------------
169 
170 // Breakpoints
171 // This macro will trigger a breakpoint where it is placed. With MSVC a dialog
172 // will ask you if you want to launch the debugger.
173 #if defined (COMPILER_GCC) || defined (COMPILER_CLANG)
174 #define BREAKPOINT(ARG) __builtin_trap();
175 //asm volatile ("int $3")
176 #elif defined (COMPILER_MSVC)
177  #define BREAKPOINT(ARG) __debugbreak()
178 #else
179  #error unsupported platform
180 #endif
181 
182 // Platform-independent macros
183 // Note : there is support of deprecated and alignof in C++ 11
184 
185 // Alignment
186 // ALIGN_OF : returns the alignment of a variable or field
187 // ALIGNED : declare an aligned variable
188 
189 // Branch prediction hints (GCC & Clang only)
190 // UNLIKELY tells the compiler to expect the condition to be false
191 // LIKELY tells the compiler to expect the condition to be true
192 
193 // Inlining commands
194 // ALWAYS_INLINE is the strongest. On GCC it will actually inline even
195 // when building without optimization (which is a bad idea most of the time).
196 // STRONG_INLINE is stronger than just "inline" where supported.
197 // NO_INLINE tells the compiler to never inline the function.
198 
199 // STDCALL, CDECL, FASTCALL : keyword for the corresponding calling convention.
200 
201 #if defined (COMPILER_MSVC) // ----------------------------------- Visual Studio
202 
203 # define ALIGN_OF(X) __alignof(X)
204 # define ALIGNED(DECL,ALIGN) __declspec(align(ALIGN)) DECL
205 
206 // Unfortunately visual studio does not have a branch prediction primitive.
207 # define UNLIKELY(IFEXPR) IFEXPR
208 # define LIKELY(IFEXPR) IFEXPR
209 
210 # define ALWAYS_INLINE __forceinline
211 # define STRONG_INLINE __forceinline
212 # define NO_INLINE __declspec(noinline)
213 
214 # define DLL_EXPORT __declspec(dllexport)
215 # define DLL_IMPORT __declspec(dllimport)
216 
217 # define STDCALL __stdcall
218 # ifndef CDECL
219 # define CDECL __cdecl
220 # endif
221 # define FASTCALL __fastcall
222 #elif defined(COMPILER_GCC) || defined (COMPILER_CLANG) // ------- GCC and CLang
223 
224 # define ALIGN_OF(X) __alignof__(X)
225 # define ALIGNED(DECL,ALIGN) DECL __attribute__((aligned(ALIGN)))
226 
227 # define UNLIKELY(IFEXPR) __builtin_expect(bool(IFEXPR),0)
228 # define LIKELY(IFEXPR) __builtin_expect(bool(IFEXPR),1)
229 
230 # define ALWAYS_INLINE __attribute((always_inline))
231 # define STRONG_INLINE inline
232 # define NO_INLINE __attribute__((noinline))
233 
234 # define DLL_EXPORT
235 # define DLL_IMPORT
236 
237 # define STDCALL __attribute__((stdcall))
238 # define CDECL /* default */
239 # define FASTCALL __attribute__((fastcall))
240 #else
241 # error unsupported platform
242 #endif
243 
244 
245 // ----------------------------------------------------------------------------
246 // Useful aliases
247 // ----------------------------------------------------------------------------
248 
249 using uchar = unsigned char;
250 using ushort = unsigned short;
251 using uint = unsigned int;
252 using ulong = unsigned long;
253 
254 // Use this to use double precision for all maths
255 // #define CORE_USE_DOUBLE
256 #ifndef CORE_USE_DOUBLE
257 using Scalar = float;
258 #else
259 using Scalar = double;
260 #endif
261 
270 constexpr Scalar operator"" _ra ( long double n )
271 {
272  return Scalar( n );
273 }
274 
283 constexpr Scalar operator"" _ra ( unsigned long long n )
284 {
285  return Scalar( n );
286 }
287 
288 // ----------------------------------------------------------------------------
289 // Debug tools
290 // ----------------------------------------------------------------------------
291 
292 namespace compile_time_utils
293 {
294 template<int x> struct size;
295 }
296 // This macro will print the size of a type in a compiler error
297 // Note : there is a way to print it as a warning instead on StackOverflow
298 #define STATIC_SIZEOF(TYPE) compile_time_utils::size<sizeof(TYPE)> static_sizeof
299 
300 // This macro controls if asserts are triggered or not.
301 #if defined CORE_DEBUG || defined CORE_USE_ASSERT
302 # define CORE_ENABLE_ASSERT
303 # define ON_ASSERT( CODE ) CODE
304 #else
305 # undef CORE_ENABLE_ASSERT
306 # define ON_ASSERT( CODE ) /* nothing */
307 #endif
308 
309 
310 
311 // Common code to report a failure
312 // expects the expression which triggered the report,
313 // the description of the error, and a format string.
314 // (arguments to printf are, in order :
315 // filename (%s), line (%i), expr(%s), desc (%s)
316 #define REPORT_FAIL( EXP, DESC, FMT ) \
317  MACRO_START \
318  std::stringstream stream; \
319  stream << DESC; \
320  fprintf(stderr, \
321  FMT,__FILE__,__LINE__, \
322  #EXP, stream.str().c_str() ); \
323 MACRO_END
324 
325 
326 
327 // Custom assert, warn and error macros.
328 // Standard assert has two main drawbacks : on some OSes it aborts the program,
329 // and the debugger will break in assert.c or and not where the calling code
330 // uses assert(). CORE_ASSERT guarantees a breakpoint when in a debugger,
331 // and always prints a useful message.
332 // CORE_WARN_IF has the same effect but it will only print a message.
333 #ifdef CORE_ENABLE_ASSERT
334 #define CORE_ASSERT( EXP, DESC ) \
335  MACRO_START \
336  if (UNLIKELY(!(EXP))) { \
337  REPORT_FAIL(EXP, DESC, "%s:%i: Assertion `%s` failed : %s\n");\
338  BREAKPOINT(0); \
339  } else {} \
340  MACRO_END
341 
342 #define CORE_WARN_IF( EXP, DESC ) \
343  MACRO_START \
344  if (UNLIKELY((EXP))) { \
345  REPORT_FAIL(EXP, DESC, "%s:%i: WARNING `%s` : %s\n");\
346  } else{} \
347  MACRO_END
348 #else
349 #define CORE_ASSERT( EXP, DESC ) // nothing
350 #define CORE_WARN_IF( EXP, DESC ) // nothing
351 #endif
352 
353 // Print an error and break, even in release.
354 #define CORE_ERROR( DESC ) \
355  MACRO_START \
356  REPORT_FAIL(ERROR, DESC, "%s:%i %s: %s\n");\
357  BREAKPOINT(0); \
358  exit(EXIT_FAILURE); \
359  MACRO_END
360 
361 // Print an error and break if condition is not met, even in release
362 #define CORE_ERROR_IF( EXP, DESC ) \
363  MACRO_START \
364  if( UNLIKELY(!(EXP))) { \
365  REPORT_FAIL(EXP, DESC, "%s:%i ERROR `%s`: %s\n");\
366  BREAKPOINT(0); \
367  exit(EXIT_FAILURE); \
368  }else{} \
369  MACRO_END
370 
371 
372 
373 // ----------------------------------------------------------------------------
374 // Explicit compiler warning disables.
375 // ----------------------------------------------------------------------------
376 
377 // With the most sensitive warning settings, using this file can trigger lots
378 // of unwanted warnings, so we explicitly disable them. Add more at your
379 // own risk...
380 
381 #if defined(COMPILER_GCC)
382 // Triggered by the alias in static assert.
383  #pragma GCC diagnostic ignored "-Wunused-local-typedefs"
384 #endif
385 #if defined(COMPILER_MSVC)
386  #pragma warning(disable: 4244) // Conversion from double to float loses data.
387  #pragma warning(disable: 4251) // stl dllexports
388  #pragma warning(disable: 4267) // conversion from size_t to uint
389  #pragma warning(disable: 4275) // non - DLL-interface class 'class_1' used as base for DLL-interface class 'class_2'
390  #pragma warning(disable: 4577) // noexcept used with no exception handling mode
391  #pragma warning(disable: 4838) // conversion from enum to uint.
392  #pragma warning(disable: 4996) // sprintf unsafe
393  #pragma warning(disable: 4503) // Truncated decorated name
394  #ifndef NOMINMAX
395  #define NOMINMAX
396  #endif
397  #include <windows.h>
398 #endif
399 
400 #ifndef eigen_assert
401 #define eigen_assert(XXX) CORE_ASSERT(XXX, "Eigen Assert");
402 #endif
403 // clang-format on