Avoiding preprocessor for mutual exclusive function call in C++20

Viewed 160

Please consider the following little function. It provides compiler abstraction for breaking the debugger programmatically:

inline constexpr void BreakDebug()
{
   #ifdef __GNUC__
      __builtin_trap();
   #elif defined _MSC_VER
      __debugbreak();
   #endif
}

I would like to rewrite the function and replace the preprocessor instructions using C++20 code. Since __builtin_trap and __debugbreak are compiler specific and mutual exclusive, I can't use a simple if constexpr since I would get a compilation error.

Assuming I would wrap the compiler macros __GNUC__ and _MSC_VER using a constexpr enumeration constant... how could that be done?

2 Answers

You only need a declaration of the function that is not called (and no definition, as it won't be called):

inline constexpr void BreakDebug()
{
   #ifdef __GNUC__
   constexpr bool GNU = true; 
   constexpr void __debugbreak();
   #elif defined _MSC_VER
   constexpr bool GNU = false;  
   constexpr void __builtin_trap();
   #endif


   if constexpr (GNU){
      __builtin_trap();
   } else {
      __debugbreak();
   }
}

By declaring only that function that does not exist and that you will never call, you can avoid linker errors in case the signature is not the correct one.

Apparently, those builtin functions can also be turned into dependent name. I checked this here. So the usual trick that consists in making potentially ill-formed code dependent works:

enum compiler_t{
    gnu,
    msvc
    };

inline constexpr compiler_t compiler = 
#ifdef __GNUC__
  compiler_t:: gnu;
#else
  compiler_t:: msvc;
#endif

template <class...Args>
inline constexpr void BreakDebug(Args...args)
{

   if constexpr (compiler == gnu){
      __builtin_trap(args...);
   } else {
      __debugbreak(args...);
   }
}

int main(){
   BreakDebug();
   return 0;
}
Related