1

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 '20 at 15:28
4

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.

3

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 '20 at 8:06
  • 3
    @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 '20 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 '20 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 '20 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 '20 at 16:03
0

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.

1
  • Add an #else branch with an implementation in pure C, so it works everywhere.
    – gnasher729
    Dec 10 '20 at 16:04

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy