2012-05-10 15 views
17

So habe ich eine schöne hartnäckige Allokator-Klasse , die ermöglicht mir C++ Container-Objekte und Zeichenfolgen im persistenten Speicher, die von einer Mmap-Datei unterstützt wird, die von einem Lauf von meinem Programm auf die beibehalten werden kann Nächster.Kopieren von Objekten mit verschiedenen Zuweisern in C++

Mein Problem kommt, wenn ich etwas tun möchte, das persistente und nicht persistente Objekte mischt. Zum Beispiel habe ich

typedef std::basic_string<char, std::char_traits<char>, persistent_alloc<char>> pstring; 

pstring a, b, c; 
std::string x, y, z; 

Ich will die Dinge zu tun, wie in der Lage sein:

if (a == x) 
    a = y; 
c = z + b; 

und so weiter, aber standardmäßig ist es nicht funktioniert, wie pstring und std::string nicht verwandten Arten sind. Nun, was den Vergleich betrifft, kann ich definieren:

template<typename Alloc1, typename Alloc2> inline bool 
operator==(const std::basic_string<char, std::char_traits<char>, Alloc1> &a, 
      const std::basic_string<char, std::char_traits<char>, Alloc2> &b) 
{ 
    return strcmp(a.c_str(), b.c_str()) == 0; 
} 

... und jetzt kann ich Zeichenfolgen für die Gleichheit vergleichen. Aber diese für jede Operation hinzuzufügen scheint wie ein Schmerz - es scheint, als ob sie von der Standardbibliothek bereitgestellt werden sollten. Schlimmer noch, Zuweisungsoperatoren und Kopierkonstruktoren müssen Mitglieder sein und können nicht als globale Inline-Funktionen wie diese definiert werden.

Gibt es einen vernünftigen Weg, dies zu tun? Oder muss ich die gesamte Standardbibliothek neu schreiben, um Zuweiser sinnvoll zu unterstützen?

+6

Dies wurde in der Vergangenheit zum Standardausschuss gebracht. Diese Beschränkung des aktuellen Zuweisungsmodells und andere werden in [n1850] (http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2005/n1850.pdf) gezeigt. Das Zuweisungsmodell in C++ ist nicht sehr gut definiert, und es ist tatsächlich problematisch (die Alternative in n1850 ist auch aus verschiedenen Gründen problematisch) –

+2

Nicht zusammenhängende Anmerkung: Denken Sie daran, dass Ihr Programm UB wäre, wenn die Typen "std :: is_trivialy_copyable" erfüllen :: value == true', es sei denn, Sie stellen den persistenten Speicher an genau der gleichen absoluten Stelle oder etwas Ähnlichem wieder her. –

+0

Sie möchten, dass sich einige Operationen gleich verhalten und einige Operationen sich anders verhalten. Wie würde der Compiler wissen, wenn du es nicht buchstabierst? –

Antwort

7

Es gibt einen Weg, damit umzugehen, aber Sie müssen etwas außerhalb der Box denken. Was Sie brauchen, ist ein Zwischentyp, der implizit sowohl von std::string als auch von Ihrem Allokator-String konstruiert werden kann.

Es gibt eine proposal for such a thing before the C++ committee currently. Es basiert auf einem Google-built Apache-licensed implementation that already exists. Es heißt basic_string_ref; Es ist eine Vorlagenklasse, die im Grunde genommen ein Zeiger auf das erste Zeichen in einer Zeichenfolge und eine Größe ist, die die Länge der Zeichenfolge darstellt. Es ist kein echter Container in dem Sinne, dass es nicht die Speicherverwaltung übernimmt.

Welches ist genau das, was Sie brauchen.

basic_string_ref für einen bestimmten Zeichentyp und Eigenschaften Typ ist implizit konstruierbar aus einem std::basic_stringunabhängig von Allocator.

Alle Vergleichsoperatoren können in basic_string_ref definiert werden. Da es implizit von std::basic_string (und praktisch frei zu konstruieren) konstruiert werden kann, würde es transparent für Vergleiche zwischen unterschiedlich zugewiesenen Strings funktionieren.

Doing Zuordnung ist eher knifflig, aber machbar. Es erfordert eine Reihe von Konvertierungen:

a = pstring{basic_string_ref{y}}; 

Nicht der hübscheste Code, natürlich. Wir würden es vorziehen, einfach den Kopierkonstruktor und den Zuweisungsoperator std::basic_string als Zuweisungsagenten zu ändern. Aber da das nicht machbar ist, ist das wirklich das nächstbeste.Man könnte sogar in einer Template-Funktion wickeln:

template<typename DestAllocator, typename SourceAllocator, typename charT, typename traits> 
std::basic_string<charT, traits, DestAllocator> conv_str(const std::basic_string<charT, traits, SourceAllocator> &input) 
{ 
    return std::basic_string<charT, traits, DestAllocator>{basic_string_ref<charT, traits>{y}}; 
} 

Natürlich, wenn Sie das tun können, können Sie einfach tun:

template<typename DestAllocator, typename SourceAllocator, typename charT, typename traits> 
std::basic_string<charT, traits, DestAllocator> conv_str(const std::basic_string<charT, traits, SourceAllocator> &input) 
{ 
    return std::basic_string<charT, traits, DestAllocator>{y.begin(), y.end()}; 
} 

Es wäre toll, wenn dies nur war Teil std::basic_string, damit Sie die Problemumgehungen nicht benötigen. Aber es ist nicht.

+0

Dies macht das Problem etwas einfacher, löst es aber nicht ganz. Ich sehe immer noch keine Möglichkeit, Zuweisungsoperatoren zu arbeiten, ohne die Standardbibliothek zu ändern. –

+0

@ChrisDodd: Ja, ich weiß; Ich sagte, dass. Sie können entweder 80% von dem bekommen, was Sie wollen, und mit ein paar kleineren API-Problemen fertig werden, oder Sie können 'std :: basic_string' neu schreiben. Persönlich würde ich das erstere vorschlagen. –

Verwandte Themen