2014-08-29 16 views
8

Sto sviluppando una libreria (C++) che utilizza contenitori non ordinati. Questi richiedono un hasher (di solito una specializzazione della struttura modello std::hash) per i tipi di elementi che memorizzano. Nel mio caso, quegli elementi sono classi che racchiudono stringhe letterali, simili a conststr dell'esempio allo the bottom of this page. STL offre una specializzazione per i puntatori char costanti, che, tuttavia, calcola solo puntatori, come spiegato here, in the 'Notes' section:Verificare se i letterali di stringa uguale sono memorizzati allo stesso indirizzo

Non c'è specializzazione per stringhe C. std::hash<const char*> produce un hash del valore del puntatore (l'indirizzo di memoria), esso non esamina il contenuto di alcun array di caratteri.

Anche se questo è molto veloce (o almeno così credo), non è garantito dalla norma se diversi letterali uguali stringa sono memorizzati nello stesso indirizzo C++, come spiegato in this question. Se non lo sono, la prima condizione di hashers non sarebbe soddisfatto:

Per due parametri k1 e k2 che sono uguali, std::hash<Key>()(k1) == std::hash<Key>()(k2)

desidero calcolare selettivamente l'hash utilizzando la fornito specializzazione, se la garanzia di cui sopra è data, o qualche altro algoritmo in caso contrario. Sebbene ricorrere alla richiesta di coloro che includono le mie intestazioni o di costruire la mia libreria per definire una particolare macro sia fattibile, una implementazione definita sarebbe preferibile.

C'è qualche macro, in qualsiasi implementazione C++, ma principalmente g ++ e clang, la cui definizione garantisce che diversi valori letterali di stringa uguale siano memorizzati nello stesso indirizzo?

Un esempio:

#ifdef __GXX_SAME_STRING_LITERALS_SAME_ADDRESS__ 
const char str1[] = "abc"; 
const char str2[] = "abc"; 
assert(str1 == str2); 
#endif 
+2

Certamente no, perché non si tratta solo di "* stringhe uguali memorizzate nello stesso indirizzo *", ma più stringhe memorizzate come sottostringhe di una stringa più grande, ecc. Ad esempio, dati due letterali '" mondo "' e '" ciao mondo "', il compilatore può generare codice come '.data: byte STR {h, e, l, l, o,, w, o, r, l, d}' facendo riferimento al primo come 'STR + 6' e il secondo come 'STR'. – Manu343726

+6

Anche se i valori letterali di stringa sono coalizzati, due variabili 'char []' non saranno. 'st1 == str2' non sarà mai vero. –

+1

Non puoi usare 'std :: string' per il tuo contenitore?O matrici di carbone? – quantdev

risposta

5

Esiste una macro, in qualsiasi implementazione C++, ma soprattutto g ++ e clang, le cui garanzie definizione che diversi letterali uguali stringa vengono memorizzati allo stesso indirizzo?

tentativo unire costanti identici (costanti stringa e costanti a virgola mobile) attraverso compilazione unità.

Questa opzione è l'impostazione predefinita per la compilazione ottimizzata se l'assemblatore e il linker lo supportano. Utilizzare le costanti -fno-merge per inibire questo comportamento.

Abilitato ai livelli -O, -O2, -O3, -Os.

  • Visual Studio ha String Pooling (/GF opzione "Elimina stringhe duplicate")

String messa in comune permette che erano inteso come più puntatori a buffer multipli per essere molteplici puntatori a un singolo buffer. Nel seguente codice, s e t sono inizializzati con la stessa stringa. String pooling li induce a puntare alla stessa memoria:

char *s = "This is a character buffer"; 
char *t = "This is a character buffer"; 

Nota: anche se MSDN utilizza char* stringhe letterali, const char* dovrebbe essere usato

  • clang ha a quanto pare anche la possibilità -fmerge-constants, ma Non riesco a trovare molto a riguardo, tranne nella sezione --help, quindi non sono sicuro se sia davvero l'equivalente di quello di gcc:

Vieta fusione delle costanti


Comunque, come letterali stringa sono immagazzinate dipende dall'implementazione (molti non memorizzarli nella porzione di sola lettura del programma).

Invece di costruire la vostra libreria su possibili hack implementazione-dipendente, posso solo suggerire l'utilizzo di std::string invece di stringhe in stile C: si comporteranno esattamente come ci si aspetta.

È possibile costruire il vostro std::string sul posto nei vostri contenitori con i emplace() metodi:

std::unordered_set<std::string> my_set; 
    my_set.emplace("Hello"); 
+0

'std :: string' porta con sé allocazione dinamica ed eccezione delle eccezioni, anche se in effetti sembra essere l'unica soluzione standard, oltre a reinventare la ruota, cioè l'algoritmo di hashing, per le mie classi. – Kalrish

+0

@Kalrish: sì, ma l'impatto dell'allocazione dinamica sarà limitato poiché le stringhe sono note al momento della compilazione (ad esempio, è possibile allocare tutte le stringhe all'avvio dell'applicazione). Sills sembra l'unico modo pulito e portatile. Spero che aiuti. – quantdev

+1

char * s = "Questo è un buffer di caratteri"; non è più valido in C++ (rottura del cambiamento in C++ 11). Deve essere const char * s = "Questo è un buffer di caratteri"; . – user515430

2

Sebbene C++ non sembra consentire qualsiasi modo che funziona con le stringhe, v'è una brutta ma un po 'praticabile aggirare il problema se non ti dispiace riscrivere i tuoi letterali stringa come sequenze di caratteri.

template <typename T, T...values> 
struct static_array { 
    static constexpr T array[sizeof...(values)] { values... }; 
}; 

template <typename T, T...values> 
constexpr T static_array<T, values...>::array[]; 

template <char...values> 
using str = static_array<char, values..., '\0'>; 

int main() { 
    return str<'a','b','c'>::array != str<'a','b','c'>::array; 
} 

Questo è necessario per restituire zero. Il compilatore deve garantire che anche se più unità di traduzione istanziano str<'a','b','c'>, queste definizioni vengono unite e si finisce con un'unica matrice.

Tuttavia, è necessario assicurarsi di non mescolare questo valore con stringhe letterali. Qualsiasi stringa letterale è garantita non per confrontare uguale a qualsiasi matrice di istanze di modello.

+0

Grazie! Sfortunatamente, le stringhe letterali sarebbero _way_ più comode da usare nella mia libreria. Mi chiedo, tuttavia, se potessero essere convertiti in fase di compilazione in sequenze di caratteri ... – Kalrish

+0

@Istruzioni letterali stringa Kalrish non possono essere utilizzate come argomenti del modello e sebbene i valori letterali stringa possano essere passati alle funzioni 'constexpr' e l'indicizzazione dell'array sia consentita su stringhe letterali in espressioni costanti, un'operazione di indicizzazione su un parametro di funzione 'constexpr' non si qualifica come espressione costante. Il meglio che riesco a fare è di abusare orribilmente del preprocessore e di forzare molte istanze di template non necessarie: '#define CHAR_AT (s, i) ((i) :: array: sizeof (s) == 2? str :: array: .. .) ' – hvd

+0

Dovresti estendere la macro' STR' per supportare la stringa più lunga che stai effettivamente usando, e quindi usare 'STR (" abc ")'. Penso che sia una pessima idea, ma è l'unica cosa che posso inventare che consenta di ottenere stringhe letterali. – hvd

Problemi correlati