Domanda

I sperimentato qualche strano comportamento durante l'utilizzo di C tipo tratti ++ e hanno ridotto il mio problema fino a questo piccolo problema eccentrico per la quale io darò un sacco di spiegazione dal momento che non voglio lasciare nulla aperto per errori di interpretazione.

Diciamo che avete un programma in questo modo:

#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 entrambi compilazione a 32 bit con GCC (e con 32 e 64 bit MSVC), l'uscita del programma sarà:

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

Tuttavia, il programma risultante da un'uscita GCC compilazione volontà 64 bit:

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

Questo è curioso, poiché long long int è un intero a 64 bit con segno ed è, a tutti gli effetti, identici ai tipi long int e int64_t, così logicamente, int64_t, long int e long long int sarebbero tipi equivalenti - l'assembly generato quando utilizzando questi tipi è identico. Uno sguardo a stdint.h mi dice perché:

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

In un 64-bit compilazione, int64_t è long int, non un long long int (ovviamente).

La correzione di questa situazione è piuttosto semplice:

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

Ma questo è terribilmente hacker e non scala bene (funzioni reali di sostanza, uint64_t, ecc). Quindi la mia domanda è:? C'è un modo per dire al compilatore che un long long int è anche una int64_t, proprio come long int è


I miei pensieri iniziali sono che questo non è possibile, a causa del modo in C / C ++ definizioni del tipo di lavoro. Non c'è un modo per specificare il tipo di equivalenza dei tipi di dati di base per il compilatore, dato che questo è il lavoro del compilatore (e permettendo che potrebbe rompere un sacco di cose) e typedef va un solo modo.

Sono anche non troppo preoccupato con ottenere una risposta qui, dal momento che questo è un super-mega bordo caso che io non sospetto chiunque sarà mai preoccuparsi quando gli esempi non sono terribilmente artificiosa (vuol dire che questo dovrebbe essere comunità wiki?).


Append : Il motivo per cui sto utilizzando il modello di specializzazione parziale, invece di un esempio più semplice del tipo:

void go(int64_t) { }

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

è Detto questo esempio sarà ancora la compilazione, dal momento che long long int è implicitamente convertibile in una int64_t.


Append : l'unica risposta finora presuppone che voglio sapere se un tipo è 64-bit. Non volevo che la gente per fuorviare nel pensare che mi preoccupo che e, probabilmente, avrebbe dovuto fornire altri esempi di dove questo problema si manifesta.

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

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

In questo esempio, some_type_trait<long int> sarà un boost::true_type, ma some_type_trait<long long int> non sarà. Anche se questo ha un senso nell'idea C ++ s 'di tipo, non è auspicabile.

Un altro esempio sta usando un qualificatore come same_type (che è abbastanza comune per l'uso in C ++ 0x Concetti):

template <typename T>
void same_type(T, T) { }

void foo()
{
    long int x;
    long long int y;
    same_type(x, y);
}

Questo esempio non riesce a compilare, in quanto C ++ (correttamente) vede che i tipi sono diversi. g ++ non riuscirà a compilare con un errore del tipo:. nessuna funzione di corrispondenza di chiamata same_type(long int&, long long int&)

Vorrei sottolineare che ho capito perché questo sta accadendo, ma io sto cercando una soluzione che non mi costringono a ripetere il codice in tutto il luogo.

È stato utile?

Soluzione

Non c'è bisogno di andare a 64-bit per vedere qualcosa di simile. Considerare int32_t su piattaforme comuni a 32-bit. Potrebbe essere typedef'ed come int o come long, ma solo uno dei due ovviamente alla volta. int e long sono di tipi distinti naturalmente.

Non è difficile vedere che non v'è alcuna soluzione che rende int == int32_t == long su sistemi a 32 bit. Per lo stesso motivo, non c'è modo di fare long == int64_t == long long su sistemi a 64-bit.

Se si potesse, le possibili conseguenze sarebbe piuttosto doloroso per il codice che sovraccarica foo(int), foo(long) e foo(long long) -?! All'improvviso che avrebbero avuto due definizioni per la stessa di sovraccarico

La soluzione corretta è che il codice del modello di solito non dovrebbe essere affidamento su un tipo preciso, ma sulle proprietà di quel tipo. La logica intera same_type potrebbe ancora essere OK per casi specifici:

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

vale a dire., Il foo(int64_t) di sovraccarico non è definita quando è esattamente lo stesso foo(long).

[modifica] Con C ++ 11, ora abbiamo un modo standard per scrivere questo:

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

Altri suggerimenti

Vuoi sapere se un tipo è dello stesso tipo di int64_t o vuoi sapere se qualcosa è di 64 bit? Sulla base della sua soluzione proposta, credo che tu stai chiedendo quest'ultimo. In tal caso, vorrei fare qualcosa di simile

template<typename T>
bool is_64bits() { return sizeof(T) * CHAR_BIT == 64; } // or >= 64
  

Quindi la mia domanda è: C'è un modo per dire al compilatore che una lunga long int è il anche un'int64_t, proprio come long int è

Questa è una domanda buona o un problema, ma ho il sospetto che la risposta è NO.

Inoltre, un long int non può essere un long long int.


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

Credo che questo sia libc. Ho il sospetto che si vuole andare più in profondità.

  

In entrambi compilazione a 32 bit con GCC (e con 32 e 64 bit MSVC), la   output del programma sarà:

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

Linux a 32 bit utilizza il modello di dati ILP32. Interi, long e puntatori sono a 32-bit. Il tipo a 64-bit è un long long.

documenti Microsoft gamme a Tipo dati Ranges . Il dicono che il long long è equivalente a __int64.

  

Tuttavia, il programma risultante da un'uscita GCC compilazione volontà 64 bit:

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

Linux a 64 bit utilizza il modello di dati LP64. Longs sono a 64 bit e long long sono a 64-bit. Come nel caso di 32 bit, documenti Microsoft gli intervalli a Tipo di dati Ranges lunga lunga è ancora __int64.

C'è un modello di dati ILP64 dove tutto è a 64 bit. Devi fare qualche lavoro extra per ottenere una definizione per il vostro tipo word32. Vedere anche giornali come 64 bit modelli di programmazione: Perché LP64

?
  

Ma questo è terribilmente hacker e non scala bene (funzioni reali di sostanza, uint64_t, ecc) ...

Si, è ancora meglio. miscele GCC e le partite le dichiarazioni che dovrebbero prendere 64 tipi bit, quindi è facile finire nei guai, anche se si segue un particolare modello di dati. Ad esempio, il seguente causa un errore di compilazione e ti dice di uso -fpermissive:

#if __LP64__
typedef unsigned long word64;
#else
typedef unsigned long long word64;
#endif

// intel definition of rdrand64_step (http://software.intel.com/en-us/node/523864)
// extern int _rdrand64_step(unsigned __int64 *random_val);

// Try it:
word64 val;
int res = rdrand64_step(&val);

Essa si traduce in:

error: invalid conversion from `word64* {aka long unsigned int*}' to `long long unsigned int*'

Quindi, ignorare LP64 e modificarlo:

typedef unsigned long long word64;

Poi, vagare ad un braccio degli oggetti gadget a 64 bit che definisce LP64 e l'uso NEON:

error: invalid conversion from `word64* {aka long long unsigned int*}' to `uint64_t*'
Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top