2

My requirement is simple, I want to be able to count the number of bits in a number. With a little bit of research, I found that MSVC has __popcnt, GCC has __builtin_popcount and so on.

At this stage, it's one function. Later on here are some that would also be very useful in my program, but are again compiler-specific

  • _byteswap_uint64
  • __shiftright128, __shiftleft128

But then I would have to do

#if defined(_MSC_VER)
...
#elif defined(__GNUG__)
...
...
#endif 

to make it work on other compilers. But this makes the program look very ugly and very hard to maintain.

The question: Is it bad to use compiler-specific functions in your source? i.e Should I avoid using them?

1
  • 1
    No programming technique is good or bad without any context. You need to know what your program's intended life cycle is, then you can evaluate how many and which compilers you want to support.
    – Doc Brown
    Dec 8, 2020 at 15:28

4 Answers 4

5

Is it bad to use compiler-specific functions in your source? i.e Should I avoid using them?

The answer is "it depends." A naive hand-rolled popcount function requires but a few lines of code that are easy to understand, easy to test, and highly portable.

Suppose on performance testing you find that the CPU spends a tiny, tiny fraction of the execution time in this nice simple chunk of code. In this case, there is no reason to pollute your code with #ifdef this, #elif defined that, and #elif defined something_else. Optimized code oftentimes is rather ugly code, with #ifdef being about as ugly as it gets.

On the other hand, suppose on performance testing you find that the CPU spends a significant fraction of the execution time in this nice simple chunk of code, and suppose you find that the compiler-specific intrinsics reduce this by a significant amount. This can justify getting ugly, but keep in mind that that ugly code may be fragile. Your code is now at the mercy of vendors maintaining backward compatibility with their intrinsics. There is no guarantee that that will continue.

One final note: In the future you might well find that your naive hand-rolled popcount function suddenly becomes just as fast as the compiler-specific ugliness. Compiler writers (and hence compilers) have become ever more adept at recognizing the intent of code. A compiler might well recognize that your small number of lines of code does exactly what __popcount_using_specialized_assembly_instructions does, and thereby compiles your code to call that specialized function. You could have done the same using a bunch of macro commands. But what happens when the authors of the library change the name of the function? The compiler will be cognizant of that change and compile your naive hand-rolled popcount function to call the renamed function. Your macro plagued code will not.

2
  • 1
    "In the future you might well...". This already happens today: Implementing a bit rotate using bit shifting results in an internal rotate call on all relevant compilers. The thing only is: Using the built-in extension directly guarantees you that it is used instead of having to hope for the best and that the compiler will understand your code. If I tell you you can get $1000 right now, guaranteed or you can fill out a form and maybe that will get you $1000, which option would you take?
    – Mecki
    Nov 5, 2023 at 2:22
  • 1
    @Mecki It's more like You can track down and fill out a simple but little known form right now, and get $1000 right now, guaranteed, as long as you don't move over the border, or you may potentially get the $1000 without condition, though also without guarantee. Nov 5, 2023 at 8:50
4

You should avoid using such compiler-specific functions in the main body of your code, exactly because of the #ifdef hell it creates.

If such a compiler-specific function is the best choice at some point in your code, you should wrap it in an (inline) function of your own. That way, you can

  • keep all the #ifdef logic in one place, or
  • provide multiple implementations in different files, where the build system ensures the correct file for the current compiler is chosen
5
  • Thanks for the answer! Yes, that's what I tried to do, which is to keep all the #ifdef in one place. But again I think I could only cover three of the major compilers (gcc, msvc, clang) . Would that still be bad?
    – user374476
    Dec 8, 2020 at 8:06
  • 4
    @AryanParekh, whether that is bad or not depends on the set of compilers you need/want to support. You could always provide a (less efficient) general implementation that gets used by other compilers. Dec 8, 2020 at 8:13
  • One of the best places to keep all the #ifdef logic in one place is a library designed for portability. In C++, some examples are Boost and Qt.
    – mouviciel
    Dec 8, 2020 at 11:07
  • @mouviciel I haven't seen a very good library for fast and easy bit-manipulation. However C++20 introduces the bit header with a lot of them
    – user374476
    Dec 8, 2020 at 11:12
  • @AryanParekh You would have an implementation like #ifdef gcc ... #elif defined(msvc) ... #elif defined(clang) ... #else <plain C implementation> #endif so you cover all cases. If you add a new compiler, your code works but is slow for that compiler, but then you can add a special case at any time.
    – gnasher729
    Dec 10, 2020 at 16:03
1

You can define your own function in terms of the corresponding builtin, For example:

#if defined(_MSC_VER)
#define POPCOUNT(x) __popcnt(x)
#elif defined(GNUG)
#define POPCOUNT(x) __builtin_popcount(x)
#endif

Then in your code use POPCOUNT() and the correct builtin is used.

2
  • Add an #else branch with an implementation in pure C, so it works everywhere.
    – gnasher729
    Dec 10, 2020 at 16:04
  • The key insight is to use the available facilities in C++ (e.g. macros, if necessary) to create a programming abstraction layer so that the users of these functions (i.e. algorithm writers who need to count the bits in the fastest possible, and possibly hardware-specific ways) can rest assured that a function (abstraction) will take care of that, and not having to see the ugliness. OpenCV has an hardware abstraction layer (HAL) library for this purpose.
    – rwong
    Nov 5, 2023 at 3:43
0

Not really a C++ related question, as this question applies to all languages and compiler (e.g. also C) or runtime (e.g. different JavaScript implementation in browsers).

And to me the answer always has been:

  • If your code only has to support one specific compiler/runtime, of course you should use compiler specific language extensions. E.g. when I add C code to an Objective-C project for macOS, this project will never be built with anything but Xcode and Xcode will very likely never switch to another C compiler than clang, so using clang specific extensions is certainly okay.

  • Otherwise it is still okay as long as you offer a compiler/runtime neutral alternative as a fallback, since what would you lose that way? For compilers you support directly, you've made the best choice and for all other compilers, you made the same choice that you had made for all compilers otherwise and you can still add support for more compilers later on.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.