2012-11-15 11 views
5

Ho trovato questo https://gist.github.com/2945472 ma ho bisogno di un'implementazione che non dipenda da C++ 11. Ho provato la mia mano a convertirlo per usare solo boost, ma ho qualche problema.modello visitatore per boost :: any

Ecco quello che mi si avvicinò con:

#include <boost/any.hpp> 
#include <boost/function.hpp> 
#include <boost/bind.hpp> 
#include <boost/lambda/lambda.hpp> 
#include <boost/unordered_map.hpp> 

struct type_info_hash { 
    std::size_t operator()(std::type_info const & t) const { 
     return t.hash_code(); 
    } 
}; 

struct equal_ref { 
    template <typename T> bool operator()(boost::reference_wrapper<T> a,boost::reference_wrapper<T> b) const { 
     return a.get() == b.get(); 
    } 
}; 
struct any_visitor { 
    boost::unordered_map<boost::reference_wrapper<std::type_info const>, boost::function<void(boost::any&)>, type_info_hash, equal_ref> fs; 

    template <typename T> void insert_visitor(boost::function<void(T)> f) { 
     try { 
      fs.insert(std::make_pair(boost::ref(typeid(T)), boost::bind(f, boost::any_cast<T>(boost::lambda::_1)))); 
     } catch (boost::bad_any_cast& e) { 
      std::cout << e.what() << std::endl; 
     } 
    } 

    bool operator()(boost::any & x) { 
     boost::unordered_map<boost::reference_wrapper<std::type_info const>, boost::function<void(boost::any&)>, type_info_hash, equal_ref>::iterator it = fs.find(boost::ref(x.type())); 
     if (it != fs.end()) { 
      it->second(x); 
      return true; 
     } else { 
      return false; 
     } 
    } 
}; 

struct abc {}; 

void fa(int i) { std::cout << "fa(" << i << ")" << std::endl; } 
void fb(abc) { std::cout << "fb(abc())" << std::endl; } 

int main() { 
    any_visitor f; 
    f.insert_visitor<int>(fa); 
    f.insert_visitor<abc>(fb); 

    std::vector<boost::any> xs; 
    xs.push_back(1); 
    xs.push_back(abc()); 
    xs.push_back(1.5); 

    for (auto & x : xs) { 
     if (!f(x)) std::cout << "no visitor registered" << std::endl; 
    } 
} 

sto ottenendo un bad_any_cast quando si inserisce nella mappa. Non dovrebbe any_cast essere chiamato solo da it-> second (x)? Che cosa sto facendo di sbagliato?

+0

Avete considerato l'utilizzo di 'boost :: variant' per il quale i visitatori sono supportati immediatamente? L'uso di 'any' presume che i tipi possano essere * qualsiasi cosa *, cioè * tutti * i tipi nel sistema di tipi. 'variante 'assume che ci sia un sottoinsieme dei tipi che potresti voler usare nell'oggetto. Un visitatore è più vicino a una 'variante 'in quanto le diverse funzioni devono essere definite. –

+0

La mia intenzione è di usarlo per scrivere i file di configurazione da boost :: program_options, che usa boost :: any. – Keith

risposta

3

Non è possibile trasmettere _1 a T (al momento dell'espressione di binding).

Hai bisogno di un cast pigro. È possibile definire una funzione e utilizzare un'espressione di associazione nidificata oppure utilizzare Boost Phoenix con un attore personalizzato any_cast.

Ecco il nidificato approccio bind:

#include <boost/any.hpp> 
#include <boost/function.hpp> 
#include <boost/bind.hpp> 
#include <boost/lambda/lambda.hpp> 
#include <boost/unordered_map.hpp> 

struct type_info_hash { 
    std::size_t operator()(std::type_info const & t) const { 
     return 42; // t.hash_code(); 
    } 
}; 

struct equal_ref { 
    template <typename T> bool operator()(boost::reference_wrapper<T> a,boost::reference_wrapper<T> b) const { 
     return a.get() == b.get(); 
    } 
}; 
struct any_visitor { 
    boost::unordered_map<boost::reference_wrapper<std::type_info const>, boost::function<void(boost::any&)>, type_info_hash, equal_ref> fs; 

    template <typename T> static T any_cast_f(boost::any& any) { return boost::any_cast<T>(any); } 

    template <typename T> void insert_visitor(boost::function<void(T)> f) { 
     try { 
      fs.insert(std::make_pair(boost::ref(typeid(T)), boost::bind(f, boost::bind(any_cast_f<T>, boost::lambda::_1)))); 
     } catch (boost::bad_any_cast& e) { 
      std::cout << e.what() << std::endl; 
     } 
    } 

    bool operator()(boost::any & x) { 
     boost::unordered_map<boost::reference_wrapper<std::type_info const>, boost::function<void(boost::any&)>, type_info_hash, equal_ref>::iterator it = fs.find(boost::ref(x.type())); 
     if (it != fs.end()) { 
      it->second(x); 
      return true; 
     } else { 
      return false; 
     } 
    } 
}; 

struct abc {}; 

void fa(int i) { std::cout << "fa(" << i << ")" << std::endl; } 
void fb(abc) { std::cout << "fb(abc())" << std::endl; } 

int main() { 
    any_visitor f; 
    f.insert_visitor<int>(fa); 
    f.insert_visitor<abc>(fb); 

    std::vector<boost::any> xs; 
    xs.push_back(1); 
    xs.push_back(abc()); 
    xs.push_back(1.5); 

    for (auto it=xs.begin(); it!=xs.end(); ++it) 
     if (!f(*it)) std::cout << "no visitor registered" << std::endl; 
} 

Stampe uscita:

fa(1) 
fb(abc()) 
no visitor registered 
+0

Grazie, questo funziona splendidamente! – Keith

+0

Nota Ho sostituito il corpo del functor type_info_hash per consentirne la compilazione sul mio sistema. Tuttavia, le espressioni di binding nidificate sono rock :) – sehe

+0

Sembra che mi sia dimenticato di rimuovere il flag -std = C++ 11 mentre stavo lavorando a questo. Dovrò scrivere il mio metodo type_info hash_code. Ho anche perso la rimozione del 'auto' nel ciclo principale. – Keith

1

tenta di utilizzare qualsiasi estensibile https://sourceforge.net/projects/extendableany/?source=directory.

struct f_method 
{ 
    typedef void (boost::mpl::_1::* signature)() const; 

    template <typename T> 
    struct wrapper 
     : public T 
    { 
     void f() const 
     { 
      return this->call(f_method()); 
     } 
    }; 

    struct implementation 
    { 
     void operator() (int i) const 
     { 
      std::cout << "fa(" << i << ")" << std::endl; 
     } 

     void operator() (abc) const 
     { 
      std::cout << "fb(abc())" << std::endl; 
     } 

     template <typename T> 
     void operator() (const T& t) const 
     { 
      std::cout << "Errr" << std::endl; 
     } 
    }; 
}; 

typedef xany<boost::mpl::list<f_method> > any; 

int main() { 
    std::vector<any> xs; 
    xs.push_back(1); 
    xs.push_back(abc()); 
    xs.push_back(1.5); 

    for (auto it=xs.begin(); it!=xs.end(); ++it) 
     (*it).f(); 
} 
Problemi correlati