Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

long long int vs. long int vs. int64_t in C++

Tags:

c++

gcc

cstdint

I experienced some odd behavior while using C++ type traits and have narrowed my problem down to this quirky little problem for which I will give a ton of explanation since I do not want to leave anything open for misinterpretation.

Say you have a program like so:

#include <iostream> #include <cstdint>  template <typename T> bool is_int64() { return false; }  template <> bool is_int64<int64_t>() { return true; }  int main() {  std::cout << "int:\t" << is_int64<int>() << std::endl;  std::cout << "int64_t:\t" << is_int64<int64_t>() << std::endl;  std::cout << "long int:\t" << is_int64<long int>() << std::endl;  std::cout << "long long int:\t" << is_int64<long long int>() << std::endl;   return 0; } 

In both 32-bit compile with GCC (and with 32- and 64-bit MSVC), the output of the program will be:

int:           0 int64_t:       1 long int:      0 long long int: 1 

However, the program resulting from a 64-bit GCC compile will output:

int:           0 int64_t:       1 long int:      1 long long int: 0 

This is curious, since long long int is a signed 64-bit integer and is, for all intents and purposes, identical to the long int and int64_t types, so logically, int64_t, long int and long long int would be equivalent types - the assembly generated when using these types is identical. One look at stdint.h tells me why:

# if __WORDSIZE == 64 typedef long int  int64_t; # else __extension__ typedef long long int  int64_t; # endif 

In a 64-bit compile, int64_t is long int, not a long long int (obviously).

The fix for this situation is pretty easy:

#if defined(__GNUC__) && (__WORDSIZE == 64) template <> bool is_int64<long long int>() { return true; } #endif 

But this is horribly hackish and does not scale well (actual functions of substance, uint64_t, etc). So my question is: Is there a way to tell the compiler that a long long int is the also a int64_t, just like long int is?


My initial thoughts are that this is not possible, due to the way C/C++ type definitions work. There is not a way to specify type equivalence of the basic data types to the compiler, since that is the compiler's job (and allowing that could break a lot of things) and typedef only goes one way.

I'm also not too concerned with getting an answer here, since this is a super-duper edge case that I do not suspect anyone will ever care about when the examples are not horribly contrived (does that mean this should be community wiki?).


Append: The reason why I'm using partial template specialization instead of an easier example like:

void go(int64_t) { }  int main() {     long long int x = 2;     go(x);     return 0; } 

is that said example will still compile, since long long int is implicitly convertible to an int64_t.


Append: The only answer so far assumes that I want to know if a type is 64-bits. I did not want to mislead people into thinking that I care about that and probably should have provided more examples of where this problem manifests itself.

template <typename T> struct some_type_trait : boost::false_type { };  template <> struct some_type_trait<int64_t> : boost::true_type { }; 

In this example, some_type_trait<long int> will be a boost::true_type, but some_type_trait<long long int> will not be. While this makes sense in C++'s idea of types, it is not desirable.

Another example is using a qualifier like same_type (which is pretty common to use in C++0x Concepts):

template <typename T> void same_type(T, T) { }  void foo() {     long int x;     long long int y;     same_type(x, y); } 

That example fails to compile, since C++ (correctly) sees that the types are different. g++ will fail to compile with an error like: no matching function call same_type(long int&, long long int&).

I would like to stress that I understand why this is happening, but I am looking for a workaround that does not force me to repeat code all over the place.

like image 559
Travis Gockel Avatar asked Nov 12 '10 01:11

Travis Gockel


People also ask

Is int64_t same as long long int?

In a 64-bit compile, int64_t is long int , not a long long int (obviously).

Should I use long long or int64_t?

If long long is present, it must have at least 64 bits, so casting from (u)int64_t to (unsigned) long long is value-preserving. If you need a type with exactly 64 bits, use (u)int64_t , if you need at least 64 bits, (unsigned) long long is perfectly fine, as would be (u)int_least64_t .

Is Int64 same as long long?

In C#, long is mapped to Int64. It is a value type and represent System. Int64 struct. It is signed and takes 64 bits.

What is the difference between long int and long long int in C?

Long long takes the double memory as compared to long. But it can also be different on various systems. Its range depends on the type of application.


2 Answers

You don't need to go to 64-bit to see something like this. Consider int32_t on common 32-bit platforms. It might be typedef'ed as int or as a long, but obviously only one of the two at a time. int and long are of course distinct types.

It's not hard to see that there is no workaround which makes int == int32_t == long on 32-bit systems. For the same reason, there's no way to make long == int64_t == long long on 64-bit systems.

If you could, the possible consequences would be rather painful for code that overloaded foo(int), foo(long) and foo(long long) - suddenly they'd have two definitions for the same overload?!

The correct solution is that your template code usually should not be relying on a precise type, but on the properties of that type. The whole same_type logic could still be OK for specific cases:

long foo(long x); std::tr1::disable_if(same_type(int64_t, long), int64_t)::type foo(int64_t); 

I.e., the overload foo(int64_t) is not defined when it's exactly the same as foo(long).

[edit] With C++11, we now have a standard way to write this:

long foo(long x); std::enable_if<!std::is_same<int64_t, long>::value, int64_t>::type foo(int64_t); 

[edit] Or C++20

long foo(long x); int64_t foo(int64_t) requires (!std::is_same_v<int64_t, long>); 
like image 140
MSalters Avatar answered Sep 30 '22 20:09

MSalters


Do you want to know if a type is the same type as int64_t or do you want to know if something is 64 bits? Based on your proposed solution, I think you're asking about the latter. In that case, I would do something like

template<typename T> bool is_64bits() { return sizeof(T) * CHAR_BIT == 64; } // or >= 64 
like image 23
Logan Capaldo Avatar answered Sep 30 '22 20:09

Logan Capaldo