Mam proces mieszania zaimplementowany przy użyciu Howard Hinnant's method (ogólny skrót na podstawie przeciążenia hash_append
).Hashowanie typu polimorficznego we właściwy sposób
Celem tej metody jest utworzenie skrótu klas w celu "zapamiętania" wyniku obliczeń (patrz koniec tej odpowiedzi), więc mam do czynienia z pewnym problemem. W szczególności należy wziąć pod uwagę następujące możliwe Input
klasę, która musi być mieszany:
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
};
Teraz, jeśli chcę hash Input
będę mieć coś takiego:
template <class HashAlgorithm>
void hash_append(HashAlgorithm& h, Input const& input) {
hash_append(h, typeid(input));
hash_append(h, typeid(input.a));
}
Więc muszę przeciążenie hash_append
dla A
:
template <class HashAlgorithm>
void hash_append(HashAlgorithm& h, A const& a) {
hash_append(h, typeid(a));
}
tu problem jest to, że w zależności od rodzaju środowiska wykonawczego a
, musiałbym dodać dodatkowe informacje do skrótu, np. dla C
musiałbym dodać u
.
I myślał o następujących rozwiązań (i ujemnych):
dodać metodę wirtualną
A
które zwraca szczególną wartość, która może być dodana dotypeid()
mieszania, ale:- oznacza to dodanie metody wewnątrz
A
, która nie jest związana z celemA
, więc nie podoba mi się ten pomysł (w szczególności dlatego, że mam wiele klas podobnych doA
); - to łamie koncepcję
hash_append
, ponieważ metoda będzie miała unikalny typ zwrotu dla wszystkich dziedziczących klas.
- oznacza to dodanie metody wewnątrz
zrobić kilka
dynamic_cast
wewnątrzhash_append
:- Znalazłem to dość brzydki ... zwłaszcza jeśli mam wiele klas podobne do
A
; - jest to podatne na błędy: jeśli ktoś doda nowe dziecko o numerze
A
i nie dodaje dynamicznej zmiany wewnątrzhash_append
.
- Znalazłem to dość brzydki ... zwłaszcza jeśli mam wiele klas podobne do
Czy istnieje sposób do mieszania typ polimorficzny, bez konieczności zmiany rodzaju lub powoływać się na pęczek dynamic_cast
?
Ostatecznym celem jest to, aby móc memoize wyniki niektórych ciężkich funkcji. Załóżmy szkic podstawowej struktury mojego wniosku:
struct Input { };
struct Result { };
Result solve(Input const&);
Funkcja solve
jest obliczeniowo-ciężki, więc chcę, aby zapisać wyniki poprzedniego obliczenia w pliku przy użyciu skrótu Input
s, na przykładcoś takiego:
// 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;
}
W load
i store
metody będzie ładować i przechowywać wyniki z plików, celem jest, aby memoize rozwiązań między różnymi przebiegów.
Jeśli masz sugestie, jak zapamiętać te wyniki bez zmieniania się z powyższymi problemami, chętnie je przeczytam.
można użyć podwójnego dyspozytorskich w wersji '' hash_append' z A' i wysyła żądanie do odpowiednich wersji dla '' C i B' 'kiedy odzyskałeś typ, ale nie sądzę, że dokładnie to czego szukasz. Czy rozważałeś to? Wadą jest to, że musisz dodać do nich te bloki, aby przyjąć gościa. Jeśli może ci pomóc, mogę umieścić komentarz w bardziej szczegółowej odpowiedzi. – skypjack
@skypjack Przepraszam, nie rozumiem, co masz na myśli - czy możesz napisać mały przykład ilustrujący twoje znaczenie? – Holt
Mam na myśli coś [wzdłuż tej linii] (http://coliru.stacked-crooked.com/a/c1b5c249535f7590). To nie jest działający przykład, ale powinieneś dostać pomysł. Niestety twój przykładowy kod nie zawiera wielu kodów, aby spakować konkretny przykład, przepraszam. Oczywiście, 'HashVisitor' może zostać uproszczony (przynajmniej zaprojektowany tak, aby nie trzeba go modyfikować za każdym razem, gdy definiujesz nowy typ) za pomocą szablonów variadycznych i dziedziczenia bezpośredniego, ale sposób, w jaki go zdefiniowałem powinien być łatwiejszy rozumieć. – skypjack