7

I found this line in boost source:

const boost::uint64_t m = UINT64_C(0xc6a4a7935bd1e995);

I wonder what is the purpose of using a MACRO here?

All this one does is to add ULL to the constant provided.

I assume it may be used to make it harder for people to make mistake of typing UL instead of ULL, but I wonder if there is any other reason to use it.

Remy Lebeau
  • 555,201
  • 31
  • 458
  • 770
NoSenseEtAl
  • 28,205
  • 28
  • 128
  • 277
  • Is the macro always defined the same way? There's no define that changes what it might be defined at for different systems / hardware? – Tas Sep 25 '18 at 03:44
  • See [How to input int64_t / uint64_t constants?](https://stackoverflow.com/q/22363102/1708801) – Shafik Yaghmour Sep 25 '18 at 04:01
  • The main point is communication, this is an unsigned 64-bit constant. It is redundant in the context of initializing a `uint64_t` but perhaps they have a policy of always using `UINT64_C` on what are meant to be uint64_t constants in other contexts. – M.M Sep 25 '18 at 04:39

1 Answers1

12

If you look at boost/cstdint.h, you can see that the definition of the UINT64_C macro is different on different platforms and compilers.

On some platforms it's defined as value##uL, on others it's value##uLL, and on yet others it's value##ui64. It all depends on the size of unsigned long and unsigned long long on that platform or the presence of compiler-specific extensions.

I don't think using UINT64_C is actually necessary in that context, since the literal 0xc6a4a7935bd1e995 would already be interpreted as a 64-bit unsigned integer. It is necessary in some other context though. For example, here the literal 0x00000000ffffffff would be interpreted as a 32-bit unsigned integer if it weren't specifically specified as a 64-bit unsigned integer by using UINT64_C (though I think it would be promoted to uint64_t for the bitwise AND operation).

In any case, explicitly declaring the size of literals where it matters serves a valuable role in code-clarity. Sometimes, even if an operation is perfectly well-defined by the language, it can be difficult for a human programmer to tell what types are involved. Saying it explicitly can make code easier to reason about, even if it doesn't directly alter the behavior of the program.

Miles Budnek
  • 28,216
  • 2
  • 35
  • 52
  • Is this necessary when dealing with integer literals though, as `uint64_t` will be the correct size anyway? Will the compiler on certain platforms complain if it encounters an unannotated integer literal that needs annotation? – Dai Sep 25 '18 at 03:48
  • It is basically giving a hint to the compiler so it does not have to guess what type is required and then correct it later. – cup Sep 25 '18 at 03:52
  • 1
    I don't think it's necessary in this context, but there are places it's needed. [Here](https://github.com/boostorg/log/blob/master/src/timestamp.cpp#L88), for example, I believe `0x00000000ffffffff` would be an unsigned 32-bit type if `UINT64_C` weren't used (though I think it would get promoted to `uint64_t` for the bitwise AND operation). There were a lot of "I think"s in this comment though, which just proves the usefulness of this macro as a code-clarity aid. – Miles Budnek Sep 25 '18 at 04:01
  • @MilesBudnek: Hmm, isn't it the case, that even the whole `& UINT64_C(0x00000000ffffffff)` not needed there? Just a cast would be sufficient, wouldn't it? And even, if `UINT64_C` wasn't there, the expression would be the same? – geza Sep 25 '18 at 10:45
  • funny, i never knew borland was so broken... but then again it is source file that is 20 y old :) – NoSenseEtAl Sep 25 '18 at 15:51