2017-08-22 103 views
7

Howard Hinnant's method (hash_append aşırı yüklere dayalı genel karma) kullanılarak gerçekleştirilen bir karma işlemim var.Karma polimorfik tip doğru yol

Bu yöntemin amacı, hesaplamaların sonucunu "hatırlamak" için sınıfların karmaşasını yaratmaktır (bu cevabın sonuna bakın), bu yüzden bazı sorunlarla karşılaşıyorum. Ben Input karma istersem gibi bir şey olacak,

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 
}; 

Şimdi:: Ben bir aşırı ihtiyaç

template <class HashAlgorithm> 
void hash_append(HashAlgorithm& h, Input const& input) { 
    hash_append(h, typeid(input)); 
    hash_append(h, typeid(input.a)); 
} 

Özellikle, karma gereken aşağıdaki olası Input sınıfını dikkate hash_appendA için: buraya

template <class HashAlgorithm> 
void hash_append(HashAlgorithm& h, A const& a) { 
    hash_append(h, typeid(a)); 
} 

sorun olduğunuçalışma zamanı türüne bağlı, hash'a ek bilgi eklemem gerekecek, ör. C için u'u eklemeliyim.

Aşağıdaki çözeltiler (ve sakıncaları) düşündü:

  1. typeid() karma eklenebilir, belirli bir değer verir A için sanal bir yöntem eklemek ama:

    • Bu, A'un içinde A'un amacı ile ilgili olmayan bir yöntemin eklenmesi anlamına gelir, bu yüzden bu fikri gerçekten sevmiyorum (özellikle birden fazla A benzeri sınıfları olduğu için); Bu yöntem, tüm devralma sınıfları için benzersiz bir döndürme türüne sahip olacağından, hash_append kavramını bozar.
  2. hash_append içindeki dynamic_cast bir grup var: Ben A benzer birden sınıfları varsa

    • ben özellikle ... Bu oldukça çirkin buldum;
    • Bu hataya açık: Birisi A'un yeni bir çocuğunu eklerse ve hash_append'un içine bir dynamic_cast eklemezse.

kendini türünü değiştirmek veya dynamic_cast bir grup güvenmek zorunda kalmadan, bir polimorfik tür karma bir yolu var mı?


bu nihai hedefi bazı ağır fonksiyonların sonuçlarını memoize muktedir olduğunu. benim uygulamasının temel yapısını kabaca anlatmama izin verin:

struct Input { }; 
struct Result { }; 

Result solve(Input const&); 

solve fonksiyon-hesaplama ağırdır, bu yüzden örneğin Input s karmasını kullanarak dosyada önceki hesaplama sonuçlarını kaydetmek istediğinizgibi bir şey:

// 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; 
} 

load ve store yöntemleri yüklemek ve dosyaları mağaza sonuçları olur, hedef Değişik değerler arasındaki çözümler memoize etmektir.

Yukarıdaki sonuçlarla uğraşmak zorunda kalmadan bu sonuçların nasıl okunacağı konusunda öneriniz varsa, bunları okumaktan memnuniyet duyarız.

+1

ve B' ve 'C' için uygun versiyonları isteği gönderme:
İşte fikir göstermek gerekir kod demet 'türünü geri aldığınızda, ama tam olarak aradığınızı düşünmüyorum. Bunu düşündün mü? Bunun dezavantajı, bir ziyaretçi kabul etmek için bu sınıflara katmanı eklemeniz gerektiğidir. Sizin için işe yarayabilirse, yorumu daha ayrıntılı bir cevaba koyabilirim. – skypjack

+0

@skypjack Üzgünüm Ne demek istediğini tam olarak anlamadım - anlamını göstermek için küçük bir örnek yazabilir misin? – Holt

+1

[Bu hat boyunca] bir şey demek istiyorum (http://coliru.stacked-crooked.com/a/c1b5c249535f7590). Bu çalışan bir örnek değil ama siz bu fikri almalısınız. Maalesef, örnek kodunuz somut bir örnek alabilmek için çok fazla kod içermiyor, üzgünüm. Ve elbette, 'HashVisitor', variadic şablonlar ve doğrudan miras kullanarak, (en azından, yeni bir tip tanımladığınızda her zaman değiştirmeniz gerekmeyecek şekilde tasarlanmış) _simplified_ olabilir, ancak tanımladığım yol daha kolay olmalıydı. anlamak. – skypjack

cevap

4

Sen (bu da B veya C için biridir) A ait hash_append sürümündeki çift gönderilmesini kullanmak ve uygun sürüme isteği iletebilirsiniz. Bunun dezavantajı, bir ziyaretçi kabul etmek için bu sınıflara katmanı eklemeniz ve sizin için kabul edilebilir olup olmadığını söyleyemem. Sen A` `arasında` hash_append` sürümündeki çift gönderilmesini kullanabilirsiniz

struct B; 
struct C; 

struct Visitor { 
    virtual void visit(const B &) = 0; 
    virtual void visit(const C &) = 0; 
}; 

template<typename T, typename... O> 
struct HashVisitor: T, HashVisitor<O...> { 
    template<typename U> 
    std::enable_if_t<std::is_same<T, U>::value> tryVisit(const U &u) { 
     T::operator()(u); 
    } 

    template<typename U> 
    std::enable_if_t<not std::is_same<T, U>::value> tryVisit(const U &u) { 
     HashVisitor<O...>::visit(u); 
    } 

    void visit(const B &b) override { tryVisit<B>(b); } 
    void visit(const C &c) override { tryVisit<C>(c); } 
}; 

template<> 
struct HashVisitor<>: Visitor {}; 

template<typename... F 
auto factory(F&&... f) { 
    return HashVisitor<std::decay_t<F>>{std::forward<F>(f)...}; 
} 

struct A { 
    virtual void accept(Visitor &) = 0; 
    virtual int do_stuff() const = 0; 
    virtual ~A(); 
}; 

struct B: A { 
    void accept(Visitor &v) override { v.visit(*this); } 
    int do_stuff() const override { return 0; } 
}; 

struct C: A { 
    const int u; 
    void accept(Visitor &v) override { v.visit(*this); } 
    int do_stuff() const override { return u; } 
}; 

template <class HashAlgorithm> 
void hash_append(HashAlgorithm &, const B &) { 
    // do something 
} 

template <class HashAlgorithm> 
void hash_append(HashAlgorithm &, const C &) { 
    // do something 
} 

template <class HashAlgorithm> 
void hash_append(HashAlgorithm &h, const A &a) { 
    auto vis = factory(
     [&h](const B &b){ hash_append(h, b); }, 
     [&h](const C &c){ hash_append(h, c); } 
    ); 

    a.accept(vis); 
} 
+0

Cevabınız için teşekkürler - Sağlamlık istiyorsanız, yani uygulanmayan bir karma yöntemiyle bir türün olmaması durumunda bunun en iyi yanıt olduğunu düşünüyorum. Ne yazık ki, ters yöne, yani daha az sağlam bir yönteme (derleme zamanında), ancak daha az kısıtlamaya sahip olmam gerekir: tipik olarak, ziyaretçilerin önüne önce sınıfları bildiremeyebilirim ve hala karma sürecini ayırmak istiyorum. kendilerini sınıflar. Sınıfları ilan ettikten sonra özel çalışma zamanı gönderimi eklememe izin verecek bir şey uygulamaya çalışıyorum (biraz şaşırdım, yakında başka bir soru açabilirim ...). – Holt

+0

Bana bildirin ve mümkünse size bir yanıt vermeye çalışacağım. – skypjack

+0

Burada - https://stackoverflow.com/questions/45837117/storing-various-tuples-and-applying-templated-functor-on-them – Holt