2012-11-15 74 views
5

我發現這個https://gist.github.com/2945472,但我需要一個不依賴於C++ 11的實現。我試圖將其轉換爲僅使用提升,但我遇到了一些麻煩。訪客模式提升::任何

這裏是我想出了:

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

插入地圖時,我得到一個bad_any_cast。不應該只調用any_cast - > second(x)?我究竟做錯了什麼?

+0

您是否考慮過使用'boost :: variant'來支持訪問者開箱即用? 'any'的使用假設類型可以是* anything *,即類型系統中的* all *類型。 'variant'假設您可能想要在對象中使用的類型的子集。由於必須定義不同的功能,因此訪問者更接近「變體」。 –

+0

我的意圖是使用這個從boost :: program_options寫出配置文件,它使用boost :: any。 – Keith

回答

3

您不能將_1轉換爲T(在綁定表達時)。

你需要一個懶惰的演員。也許定義一個函數並使用一個嵌套的綁定表達式,或者使用Boost Phoenix和一個自定義的any_cast actor。

下面是嵌套綁定的方法:

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

打印輸出:

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

謝謝,這個作品出色! – Keith

+0

注意我替換了type_info_hash函數體,讓它在我的系統上編譯。嵌套的綁定表達式的岩石,雖然:) – sehe

+0

看起來我忘了刪除-std = C++ 11標誌,當我在這個工作。我需要編寫自己的type_info hash_code方法。我也錯過了刪除主循環中的'自動'。 – Keith

1

嘗試使用擴展任何 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(); 
}