2010-12-31 5 views
-2

Ich möchte verschiedene Datentypen in C++ mit eindeutigen deterministischen Namen erzeugen. Zum Beispiel:einzigartiger synthetisierter Name

struct struct_int_double { int mem0; double mem1; }; 

Derzeit mein Compiler synthetisiert Namen unter Verwendung eines Zählers, was bedeutet, die Namen nicht übereinstimmen, wenn die gleichen Datentyp in verschiedenen Übersetzungseinheiten zu kompilieren.

Hier ist, was nicht funktioniert:

  1. Mit dem ABI mangled_name Funktion. Denn es hängt schon von Strukturen ab, die eindeutige Namen haben. Könnte in C++ 11-konformem ABI funktionieren, indem man vorgibt, dass struct anonym ist?

  2. Vorlagen z. B. struct2, da Vorlagen nicht mit rekursiven Typen arbeiten.

  3. Eine komplette Mangling. Denn es gibt Namen, die Art und Weise zu lang sind (Hunderte von Zeichen!)

Neben einem globalen Register (igitt!) Das einzige, was ich ist denken kann, zuerst eine einzigartige lange verstümmelten Namen zu erstellen, und dann Verwenden Sie eine Digest- oder Hash-Funktion, um sie zu verkürzen (und hoffen, dass es keine Konflikte gibt).

Problem: Bibliotheken erzeugen, die aufgerufen werden können, wenn die Typen anonym sind, zB Tupel, Summenarten, Funktionstypen.

Irgendwelche anderen Ideen?

EDIT: Zusätzliche Beschreibung des rekursiven Typs Problem. Erwägen Sie, eine verknüpfte Liste wie folgt zu definieren:

template<class T> 
typedef pair<list<T>*, T> list; 

Dies ist tatsächlich, was erforderlich ist. Es funktioniert nicht aus zwei Gründen: Erstens können Sie keine typedef Vorlage. [NEIN, Sie können KEINE Template-Klasse mit einem typedef verwenden, es funktioniert nicht] Zweitens können Sie die list * nicht als Argument übergeben, weil sie noch nicht definiert ist. In C ohne Polymorphismus können Sie es tun:

struct list_int { struct list_int *next; int value; }; 

Es gibt mehrere Workarounds. Für diese insbesondere Problem können Sie eine Variante des Barton-Nackman-Trick verwenden, aber es verallgemeinert nicht.

Es gibt eine allgemeine Problemumgehung, die mir zuerst von Gabrielle des Rois gezeigt wurde, mit einer Vorlage mit offener Rekursion und dann einer Teilspezialisierung, um sie zu schließen. Aber das ist extrem schwierig zu generieren und wäre wahrscheinlich nicht lesbar, selbst wenn ich herausfinden könnte, wie es geht.

Es gibt ein anderes Problem, Varianten auch richtig zu machen, aber das ist nicht direkt verwandt (es ist nur schlimmer wegen der dummen Einschränkung gegen die Erklärung von Gewerkschaften mit konstruierbaren Typen).

Daher verwendet mein Compiler einfach normale C-Typen. Es muss irgendwie mit Polymorphie umgehen: Einer der Gründe für das Schreiben war, die Probleme des C++ - Systems einschließlich der Templates zu umgehen. Dies führt dann zu dem Namensproblem.

+3

Was ist los mit 'std :: tuple'? (Oder 'std :: tr1 :: tuple') Was wollen Sie eigentlich hier erreichen? –

+1

Was ist los mit '.get ()' anstelle von '.memN'? –

+0

@Billy: Es gibt kein std :: tuple (jedenfalls in C++ 98). Wenn C++ 11 dies hat und es eine Vorlage ist, wird es nicht funktionieren (Vorlagen können nicht mit rekursiven Typen umgehen), und ich würde es ungern benutzen, da mein aktuelles g ++ auf OSX nicht viel Unterstützung von C++ 11 hat (unglücklich). – Yttrill

Antwort

1

Brauchen Sie eigentlich die Namen zuzustimmen?Definieren Sie die Strukturen einfach getrennt mit unterschiedlichen Namen in den verschiedenen Übersetzungseinheiten und reinterpret_cast<> wo notwendig, um den C++ - Compiler glücklich zu halten. Natürlich wäre das in handgeschriebenem Code entsetzlich, aber das ist Code, der von Ihrem Compiler erzeugt wird, also können Sie (und ich nehme an) die notwendigen statischen Typprüfungen durchführen, bevor der C++ Code erzeugt wird.

Wenn ich etwas verpasst habe und Sie wirklich die Typnamen benötigen, um zuzustimmen, dann denke ich, dass Sie bereits Ihre eigene Frage beantwortet haben: Es sei denn, der Compiler kann Informationen zwischen der Übersetzung mehrerer Übersetzungseinheiten teilen (durch ein globales Register) Ich kann keine Möglichkeit sehen, eindeutige, deterministische Namen aus der strukturellen Form des Typs zu generieren, mit Ausnahme des offensichtlichen Namensmangels.

Wie für die Länge der Namen, ich bin mir nicht sicher, warum es wichtig ist? Wenn Sie daran denken, eine Hash-Funktion zu verwenden, um die Namen zu verkürzen, dann brauchen Sie diese offensichtlich nicht für Menschen lesbar zu haben, also warum müssen sie kurz sein?

Persönlich würde ich wahrscheinlich semi-menschenlesbare Namen erzeugen, in einem ähnlichen Stil wie existierende Namen-Mangling-Schemata, und mich nicht mit der Hash-Funktion befassen. Anstatt also struct_int_double zu erzeugen, könnten Sie sid (struct, int, double) oder si32f64 (struct, 32-bit integer, 64-bit float) oder was auch immer erzeugen. Solche Namen haben den Vorteil, dass sie immer noch direkt geparst werden können (was für das Debugging sehr wichtig zu sein scheint).

bearbeiten

Einige weitere Gedanken:

  • Vorlagen: Ich sehe keinen wirklichen Vorteil Template-Code zu generieren, um dieses Problem zu bekommen, auch wenn es möglich wäre. Wenn Sie sich Sorgen machen, dass Sie im Linker Limits für Symbollänge treffen, können Templates Ihnen nicht weiterhelfen, da der Linker kein Konzept von Templates hat: Alle dargestellten Symbole sind entstellte Formen der Templatestruktur, die vom C++ Compiler generiert werden haben genau das selbe Problem wie lange verfälschte Namen, die direkt vom Felix Compiler generiert wurden.
  • Alle Typen, die in Felix-Code benannt wurden, sollten beibehalten und direkt (oder fast direkt) im generierten C++ - Code verwendet werden. Ich würde denken, dass es praktische (leichte) Lesbarkeits-/Wartbarkeitseinschränkungen für die Komplexität von anonymen Typen gibt, die in Felix-Code verwendet werden, welche die einzigen sind, für die Sie Namen generieren müssen. Ich nehme an, dass Ihre "Varianten" diskriminierte Vereinigungen sind, also muss jeder Bestandteil einen Namen haben (das Tag), der im Felix-Code definiert ist, und wieder können diese Namen beibehalten werden. (Ich erwähnte dies in einem Kommentar, aber da ich meine Antwort bearbeite, könnte ich sie genauso gut mit einschließen)
  • Verringerung der Länge des Mangled-Namens: Die Ausführung eines langen Mangled-Namens durch eine Hash-Funktion klingt nach dem einfachsten Weg, und die Wahrscheinlichkeit von Kollisionen sollte akzeptabel sein, solange Sie eine gute Hash-Funktion verwenden und genügend Bits in Ihrem Hash-Namen behalten (und Ihr Alphabet für die Codierung des Hash-Namens hat 37 Zeichen, also könnte ein voller 160-Bit sha1-Hash geschrieben werden etwa 31 Zeichen). Die Idee der Hash-Funktion bedeutet, dass Sie nicht direkt von einem Hash-Namen zu dem ursprünglichen Namen zurückkehren können, aber Sie müssen dies möglicherweise nie tun. Und Sie könnten eine Hilfstabelle für die Namenszuordnung als Teil des Kompilierungsprozesses ausgeben, die ich vermute (oder den Namen vielleicht aus der C-Struct-Definition neu generieren, wo sie verfügbar ist). Wenn Sie Hash-Funktionen immer noch nicht mögen, könnten Sie wahrscheinlich eine relativ kompakte Bit-Level-Codierung definieren (schreiben Sie diese dann in das 37-stellige Bezeichner-Alphabet) oder sogar einen allgemeinen Komprimierungsalgorithmus auf dieser Bit-Ebene ausführen Codierung. Wenn Sie genug Felix-Code zum Analysieren haben, können Sie sogar ein festes Kompressionswörterbuch vorgenerieren. Das ist natürlich ein rabiates Vergnügen: benutze einfach einen Hash.

Edit 2: Sorry, Hirnversagen - sha-1 verdaut, sind 160 Bit, nicht 128.


PS. Nicht sicher, warum diese Frage abgelehnt wurde - es scheint mir vernünftig, obwohl etwas mehr Kontext über diesen Compiler, an dem Sie arbeiten, helfen könnte.

+0

@John: wahrscheinlich abgelehnt, weil die Leute meinen Ton nicht mögen, anstatt den technischen Inhalt :) Wenn Sie einen Zeiger auf den Compiler wollen: http://felix-lang.org. Erzeugt ultraschnellen Code (in vielen Fällen schneller als C oder C++), einfacher zu verwenden als eine Skriptsprache und viel sicherer. – Yttrill

+0

@John: Derzeit "löse" ich dieses Problem für die Funktion, indem ich den Programmierer den Namen angeben lasse. Für Funktionen: export f von (int) als "f"; Anweisung erstellt einen C-Wrapper namens "f" für die native Funktion f. Das ist in Ordnung, aber was ist mit Typen benötigt, um f zu nennen? Also lasse ich den Benutzer sagen, Export-Typ int * double als "int_double" und das macht einen Typedef. Externe Verknüpfungen sind hier nicht wichtig, da externe "C" -Funktionen nicht fehlerhaft sind. Das Problem ist, dass Typen andere Typen enthalten können und Sie den gesamten Baum manuell benennen müssen. – Yttrill

+0

@ John: Länge der Namen ist wichtig, weil (a) Sie müssen den C++ - Code debuggen und damit lesen und (b) weil Linker Grenzen für die Länge des Namens haben. Für einfache Namen ist "sid" in Ordnung. Aber Varianten können oft 100 Fälle mit jeweils einem Tupelargument mit 4 Typen enthalten, also müssten Sie 400 Komponenten benennen, was mehrere tausend Zeichen wäre. – Yttrill

0

Ich verstehe Ihr Problem nicht wirklich.

template<typename T> 
struct SListItem 
{ 
    SListItem* m_prev; 
    SListItem* m_next; 
    T m_value; 
}; 

int main() 
{ 
    SListItem<int> sListItem; 
} 
+0

Richtig, Sie verstehen das Problem nicht. Das Problem besteht darin, Typen nur mit Kombinatoren zu konstruieren. Sie dürfen keine Klassen definieren. Sie dürfen NUR Kombinatoren und eine feste Menge von Primitiven verwenden. Kombinatoren wären: Tupel <>, Variante <>, Zeiger <>, Funktion <>. – Yttrill

+0

Also Liste ist definiert durch: Liste = Variante < tuple<>, Tupel >>>. Es kann in Felix mit "(1 + T * U) als U" geschrieben werden, wobei das "Zeiger" -Bit weggelassen wird und "wie U" eine Rekursion ohne Definition einführt: Dies ist im Grunde die Typ-Theorist-Formel, aber C++ eins I gab ist das gleiche. – Yttrill

+0

Und ja, Sie können das buchstäblich in Felix machen, und nahe bei Ocaml und Haskell. – Yttrill

Verwandte Themen