2012-11-15 2 views
5

이 코드는 https://gist.github.com/2945472입니다.하지만 C++ 11에 의존하지 않는 구현이 필요합니다. 나는 단지 부스트를 사용하기 위해 그것을 변환하는 것에 손을 썼다. 그러나 나는 약간의 문제가있다. 맵에 삽입 할 때 나는 bad_any_cast 받고 있어요방문자 패턴 boost :: any

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

: 여기

내가 생각 해낸 것입니다. any_cast를 it -> second (x)로만 호출하면 안됩니까? 내가 도대체 ​​뭘 잘못하고있는 겁니까?

+0

방문자가 상자에서 지원되는'boost :: variant'를 사용 해본 적이 있습니까? 'any '를 사용하면 타입이 타입 시스템의 * anything *, 즉 * all * 타입이 될 수 있다고 가정합니다. 'variant'는 객체에서 사용하고자하는 타입의 부분 집합이 있다고 가정합니다. 서로 다른 함수를 정의해야하므로 방문자는 '변형'에 더 가깝습니다. –

+0

제 생각은 boost :: any를 사용하는 boost :: program_options에서 설정 파일을 작성하는 것입니다. – Keith

답변

3

_1에서 T (바인드 표현시)을 캐스트 할 수 없습니다.

게으른 캐스트가 필요합니다. 함수를 정의하고 중첩 된 바인드 표현식을 사용하거나 Boost Phoenix를 사용자 정의 any_cast 액터와 함께 사용하십시오.

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

인쇄 출력 : 확장 어떤 https://sourceforge.net/projects/extendableany/?source=directory를 사용하는

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

감사합니다.이 작품은 훌륭합니다! – Keith

+0

참고 type_info_hash functor 본문을 시스템에서 컴파일하도록 바꾸 었습니다. 중첩 된 바인드 표현식은 흔들리지 만 :) – sehe

+0

이 작업을 수행 할 때 -std = C++ 11 플래그를 제거하는 것을 잊은 것처럼 보입니다. 내 자신의 type_info hash_code 메서드를 작성해야합니다. 메인의 루프에서 '자동'을 제거하지 못했습니다. – Keith

1

시도

다음은 중첩 된 바인딩 접근 방식이다.

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