Ich habe einen Hash-Prozess implementiert mit Howard Hinnant's method (generische Hash basierend auf hash_append
Überladungen).Hashing polymorphen Typ der richtige Weg
Der Zweck dieser Methode besteht darin, einen Hash der Klassen zu erstellen, um das Ergebnis der Berechnungen zu "memoisieren" (siehe Ende dieser Antwort), so dass mir ein Problem bevorsteht. Insbesondere sollten Sie die folgenden möglichen Input
Klasse, die gehasht werden muss:
struct A {
virtual int do_stuff() const = 0;
virtual ~A();
};
struct B: A {
int do_stuff() const override { return 0; }
};
struct C: A {
const int u;
int do_stuff() const override { return u; }
};
struct Input {
A const& a; // store a reference to an instance of B or C
};
Nun, wenn ich Input
Hash will, muss ich so etwas wie:
template <class HashAlgorithm>
void hash_append(HashAlgorithm& h, Input const& input) {
hash_append(h, typeid(input));
hash_append(h, typeid(input.a));
}
Also muss ich eine Überlastung von hash_append
für A
:
template <class HashAlgorithm>
void hash_append(HashAlgorithm& h, A const& a) {
hash_append(h, typeid(a));
}
Das hier Problem ist, dass auf dem Laufzeittyp vonje, würde ich zusätzliche Informationen zu dem Hash hinzufügen müssen, z. für C
würde ich u
hinzufügen müssen.
Ich dachte an die folgenden Lösungen (und Nachteile):
eine virtuelle Methode zu
A
hinzufügen, die einen bestimmten Wert zurückgibt, der zu demtypeid()
Hash hinzugefügt werden können, aber:- das bedeutet, eine Methode innerhalb von
A
hinzuzufügen, die nicht mit dem Zweck vonA
zusammenhängt, daher mag ich diese Idee nicht wirklich (insbesondere weil ich mehrereA
-ähnliche Klassen habe); - dies bricht das Konzept von
hash_append
, da die Methode einen eindeutigen Rückgabetyp für alle erbenden Klassen haben wird.
- das bedeutet, eine Methode innerhalb von
tun ein Bündel von
dynamic_cast
innenhash_append
:- Das fand ich ziemlich hässlich ... insbesondere, wenn ich mehrere Klassen ähnlich wie
A
haben; - Dies ist fehleranfällig: wenn jemand eine neue Kinder von
A
hinzufügt und keine dynamische_cast innerhalbhash_append
hinzufügen.
- Das fand ich ziemlich hässlich ... insbesondere, wenn ich mehrere Klassen ähnlich wie
Gibt es eine Möglichkeit, einen polymorphen Typ Hash, ohne dass der Typ selbst oder stützen sich auf eine Reihe von dynamic_cast
ändern?
Das Endziel dieser ist in der Lage sein, die Ergebnisse einiger Schwer Funktionen memoize. Lassen Sie uns die Grundstruktur meiner Anwendung skizzieren: mit Hash Input
s
struct Input { };
struct Result { };
Result solve(Input const&);
Die solve
Funktion ist rechenlastig, so möchte ich die Ergebnisse der vorherigen Berechnung in der Datei speichern, z.B.so etwas wie:
// depends on hash_append
std::string hash(Input const&);
Result load_or_solve(Input const& input) {
auto h = hash(input);
Result result;
if (exists(h)) { // if result exists, load it
result = load(h);
}
else { // otherwize, solve + store
result = solve(input);
store(h, result);
}
return result;
}
Die load
und store
Methoden würden laden und zu speichern Ergebnisse von Dateien ist das Ziel, Lösungen zwischen verschiedenen Läufen memoize.
Wenn Sie einen Vorschlag haben, wie Sie diese Ergebnisse notieren können, ohne sich mit den oben genannten Problemen zu befassen, werde ich mich freuen, sie zu lesen.
Sie doppelte Dispatching können innerhalb der 'hash_append' Version von' A' und versenden die Anfrage an die entsprechenden Versionen für 'B' und 'C 'wenn du den Typ zurückbekommst, aber ich denke nicht, dass du genau das suchst. Hast du darüber nachgedacht? Der Nachteil ist, dass Sie diesen Klassen einen Text hinzufügen müssen, um einen Besucher zu akzeptieren. Wenn es für Sie arbeiten kann, kann ich den Kommentar in eine detailliertere Antwort setzen. – skypjack
@skypjack Es tut mir leid, ich verstehe nicht ganz, was du meinst - könntest du ein kleines Beispiel schreiben, um deine Bedeutung zu verdeutlichen? – Holt
Ich meine etwas [in dieser Zeile] (http://coliru.stacked-crooked.com/a/c1b5c249535f7590). Das ist kein funktionierendes Beispiel, aber Sie sollten auf die Idee kommen. Leider fehlt Ihrem Beispielcode viel Code, um ein konkretes Beispiel zu packen, tut mir leid. Und natürlich kann 'HashVisitor' _simplified_ sein (zumindest so entworfen, dass Sie es nicht jedes Mal ändern müssen, wenn Sie einen neuen Typ definieren), indem Sie variadische Vorlagen und direkte Vererbung verwenden, aber die Art, wie ich es definiert habe, sollte einfacher sein verstehen. – skypjack