2016-12-20 4 views
2

Der Wert sizeof(std::unordered_map<int, int>) scheint in verschiedenen gcc Versionen um gcc 4,6-4,9 auf Linux anders zu sein:Unterschiedliche Größe von unordered_map über gcc Versionen

  • 4,6: 56
  • 4.7: 64
  • 4.8: 48
  • 4,9 & Newer: 56

(test code on coliru) (das gleiche für andere spezielle)

Dies führt offensichtlich zu Speicherzugriffsproblemen beim Verknüpfen von Bibliotheken, die mit verschiedenen gcc-Versionen kompiliert wurden.

Ein Problem, das ich gerade jetzt konfrontiert bin, ist eine Bibliothek, sagen libfoo, die einige class Foo als seine Schnittstelle darstellt. Das heißt, stellt die Bibliothek die foo.h Header-Datei:

class Foo { 
    std::unordered_map<int, int> map; 
    int val; 
public: 
    Foo(): val(42) {} 
    int getVal(); 
}; 

und stellt auch die kompilierte Bibliothek libfoo.so, die den Binärcode für Foo::getVal() enthält, die Foo::val zugreift. Die Bibliothek wurde mit gcc 4.6 kompiliert und nimmt daher an, dass val einen Offset von 56 Bytes von Foo Start hat.

Jetzt schreibe ich mein Programm, das libfoo verwendet. Ich schreibe

#include <foo.h>  
int bar() { 
    Foo foo; 
    return foo.getVal(); 
} 

Ich baue mein Programm mit gcc 4.8. Es geht davon aus, dass unordered_map eine Größe von 48 Byte hat und daher nur 48+sizeof(int) für das Foo-Objekt zuweist. Als Ergebnis greift auf Daten außerhalb des Objekts zu.


Also, meine Fragen sind:

  1. Gibt es eine Möglichkeit, dieses Problem zu umgehen? Zum Beispiel, gcc 4.8 verwenden Sie die unordered_map von gcc 4.6? In der Tat ist das Problem nur die Zuweisung der richtigen Speichergröße, da alle tatsächlichen Arbeiten mit dem map Feld nur innerhalb libfoo.so passiert. Ich denke, dass ich etwas rohe Zeigermagie machen kann, indem ich explizit mehr Speicher zuweise und die Platzierung neu mache, aber das scheint wirklich unzuverlässig zu sein und anfällig für Speicherlecks.
  2. Bin ich richtig, dass dies eine ungewöhnliche Situation ist, wahrscheinlich, weil unordered_map Implementierung wurde noch als experimentell in GCC < 4.9, und so können keine ähnlichen Probleme mit anderen Standard-Klassen auftreten, die nicht experimentell sind?
  3. Wenn nein, wie sollten solche Probleme im Allgemeinen gemildert werden?

Beachten Sie, dass es kein Problem der Verknüpfung zu einer bestimmten Version von libstdC++ zu sein scheint; Das Problem besteht darin, zu berechnen, wie viel Speicher zugewiesen werden soll.

Antwort

2

Zu Frage 1:

würde ich folgende Wrapper verwenden:

constexpr int DesirableFooSize = 60; 
constexpr bool FooTooSmall = sizeof(Foo) < DesirableFooSize; 

class FooWrapper final : private Foo { 
    std::enable_if<FooTooSmall, std::array<int8_t, DesirableFooSize - sizeof(Foo)>>::type x; 
public: 
    using Foo::getInt; 
    using Foo::Foo; 
    Foo* getFooPointer() { 
     return this; 
    } 
    Foo& getFooReference() { 
     return *this; 
    } 
}; 

und alle Codes in Ihrem Programm soll FooWrapper statt einfach nur Foo verwenden. Dies garantiert, dass Sie immer genau 60 Bytes für jedes Foo Objekt zuweisen. Vergessen Sie nie, diesen zusätzlichen Speicher freizugeben, und der zusätzliche Speicher ist nicht zugänglich. Private Vererbung und eine explizite Casting-Funktionen sind hier absichtlich geschrieben, um Sie zweimal überlegen, wenn Sie FooWrapper zu Foo zu werfen oder etwas mit Zeigern oder Referenzen tun möchten.

Verwandte Themen