代码之家  ›  专栏  ›  技术社区  ›  themis

C++模板元编程创建Boo::来自SyrdYPPTR和Boosi::StistaT访问者的变体

  •  1
  • themis  · 技术社区  · 15 年前

    作为个人练习,我想使用shared\u ptr实现访问者模式。我熟悉robertmartin的非循环visitor论文,但发现virtual accept()的侵入性和为每个{X}类创建一个{X}visitor类的必要性令人不快。我喜欢boost::static\u visitor类,因为它在本地封装了所有逻辑,而不需要{X}::accept()和{X}visitor。

    裂开 我在下面提到。我认为应该是这样的:

    template <typename U, typename T1, typename T2, ...>
    boost::variant<T1, T2, ...> rip(U& p, boost::static_visitor<T1, T2, ...> sv)
    {
        if (T1 t1 = dynamic_cast<T1>(p)) return boost::variant<T1, ...>(t1);
        ... and so on, splitting static_visitor
        return 0;  // or throw an exception
    }
    

    任何提示或指南做类似的事情将不胜感激。谢谢。

    #include <algorithm>
    #include <cstdlib>
    #include <iostream>
    #include <memory>
    #include <boost/bind.hpp>
    #include <boost/variant.hpp>
    
    
    struct Base {};
    struct A : Base {};
    struct B : Base {};
    struct C : Base {};
    
    typedef std::shared_ptr<Base> base_ptr;
    typedef boost::variant<A*,B*,C*> base_variant;
    struct variant_visitor : public boost::static_visitor<void> {
        void operator()(A*, base_ptr) const {std::cout << "A*\n";}
        void operator()(B*, base_ptr) const {std::cout << "B*\n";}
        void operator()(C*, base_ptr) const {std::cout << "C*\n";}
    };
    
    
    int main(int, char**)
    {
        // This works, of course.
        base_ptr b(new A());
        base_variant v(new A());
        boost::apply_visitor(boost::bind(variant_visitor(), _1, b), v);
    
        // How could we use a shared_ptr with a variant?  I almost see
        // the template magic, a function to iterate over the template
        // types from the variant_visitor and return an "any<...>".
        // base_variant rip(base_ptr&, variant_visitor) {...}
        // boost::apply_visitor(boost::bind(variant_visitor(), _1, b), rip(b, variant_visitor()));
    
        return EXIT_SUCCESS;
    }
    
    2 回复  |  直到 15 年前
        1
  •  2
  •   visitor    15 年前

    我可能误解了这个问题,但如果你想用同样的方法 variant_visitor 对于包含共享指针而不是普通指针的变量,也许可以通过另一个从 shared_ptr 把它传给另一个客人。

    #include <algorithm>
    #include <cstdlib>
    #include <iostream>
    #include <boost/shared_ptr.hpp>
    #include <boost/bind.hpp>
    #include <boost/variant.hpp>
    
    
    struct Base {};
    struct A : Base {};
    struct B : Base {};
    struct C : Base {};
    
    typedef boost::shared_ptr<Base> base_ptr;
    typedef boost::variant<boost::shared_ptr<A>,boost::shared_ptr<B>,boost::shared_ptr<C> > base_variant;
    
    template <class Visitor>
    struct visit_shared_ptr_get: public boost::static_visitor<typename Visitor::result_type>
    {
        //for unary visitors
        template <class FirstArg>
        typename Visitor::result_type operator()(FirstArg& first) const
        {
            return Visitor()(first.get());
        }  
    
        //for binary visitors, only the first argument is "ripped"
        template <class FirstArg, class SecondArg>
        typename Visitor::result_type operator()(FirstArg& first, SecondArg& second) const
        {
            return Visitor()(first.get(), second);
        }  
    };
    
    struct variant_visitor : public boost::static_visitor<void> {
        void operator()(A*, base_ptr) const {std::cout << "A*\n";}
        void operator()(B*, base_ptr) const {std::cout << "B*\n";}
        void operator()(C*, base_ptr) const {std::cout << "C*\n";}
    };
    
    
    int main(int, char**)
    {
        // This works, of course.
        base_ptr b(new A());
        base_variant v(boost::shared_ptr<A>(new A()));
        boost::apply_visitor(boost::bind(visit_shared_ptr_get<variant_visitor>(), _1, b), v);
        return EXIT_SUCCESS;
    }
    

    一个你想象中的下行者。

    #include <stdexcept>
    #include <boost/type_traits.hpp>
    #include <boost/utility/enable_if.hpp>
    #include <boost/shared_ptr.hpp>
    #include <boost/variant.hpp>
    #include <boost/variant/variant_fwd.hpp>
    #include <boost/preprocessor/repetition.hpp>
    
    //dynamic_cast will only compile if the target type is a pointer
    template <class Derived, class Base, class Variant>
    typename boost::enable_if<boost::is_pointer<Derived>, bool>::type cast_if_pointer( Base* b, Variant& variant)
    {
        if (Derived p = dynamic_cast<Derived>(b)) { variant = p; return true; }
        return false;
    }
    
    //weeds out boost's unused template parameters and other non-pointers
    template <class Derived, class Base, class Variant>
    typename boost::disable_if<boost::is_pointer<Derived>, bool>::type cast_if_pointer( Base*, Variant& )
    {
        return false;
    }
    
    template <class P, BOOST_VARIANT_ENUM_PARAMS(class T)>
    void rip(boost::variant<BOOST_VARIANT_ENUM_PARAMS(T)>& variant, const boost::shared_ptr<P>& smart_ptr)
    {
    #define ATTEMPT_CAST(z, n, type) if (cast_if_pointer<T ## n >(smart_ptr.get(), variant)) return;
        BOOST_PP_REPEAT(BOOST_VARIANT_LIMIT_TYPES, ATTEMPT_CAST, T)
    #undef ATTEMPT_CAST
        throw std::bad_cast();
    }
    
    struct Base 
    {
        virtual ~Base() {}
    };
    
    struct A : Base {};
    struct B : Base {};
    struct C : Base {};
    
    
    typedef boost::shared_ptr<Base> base_ptr;
    typedef boost::variant<A*,B*,C*> base_variant;
    
    
    int main(int, char**)
    {
        base_ptr b(new A());
        base_variant v;
        rip(v, b);
    
        return EXIT_SUCCESS;
    }
    
        2
  •  0
  •   UncleBens    15 年前

    不能将visitor用于类型推断,因为它只有一个模板参数-用于 result_type . 它 operator() boost::variant 可能正在存储。

    我只是猜测以下可能是您想要的(我第一次使用boost的预处理器库):

    #include <algorithm>
    #include <cstdlib>
    #include <iostream>
    #include <memory>
    #include <boost/bind.hpp>
    #include <boost/variant.hpp>
    
    #include <boost/variant/variant_fwd.hpp>
    #include <boost/preprocessor/repetition.hpp>
    
    #define SHARED_PTR_TO_PTR(z, n, text) BOOST_PP_COMMA_IF(n) typename shared_ptr_to_ptr<text ## n>::type
    
    template <class T>
    struct shared_ptr_to_ptr { typedef T type; };
    
    template <class T>
    struct shared_ptr_to_ptr<std::shared_ptr<T> > {typedef T* type; };
    
    template <class T>
    struct unsmartify_variant;
    
    
    template <BOOST_VARIANT_ENUM_PARAMS(typename T)>
    struct unsmartify_variant<boost::variant<BOOST_VARIANT_ENUM_PARAMS(T) > >
    {
        typedef boost::variant<BOOST_PP_REPEAT(BOOST_VARIANT_LIMIT_TYPES, SHARED_PTR_TO_PTR, T)> type;
    };
    
    template <class Variant>
    struct get_visitor: boost::static_visitor<typename unsmartify_variant<Variant>::type >
    {
        template <class SharedPtr>
        typename unsmartify_variant<Variant>::type operator()(SharedPtr& p) const
        {
            return p.get();
        }
    };
    
    template <class Variant>
    typename unsmartify_variant<Variant>::type rip(Variant& variant)
    {
        return boost::apply_visitor(get_visitor<Variant>(), variant);
    }
    
    struct Base {};
    struct A : Base {};
    struct B : Base {};
    struct C : Base {};
    
    typedef std::shared_ptr<Base> base_ptr;
    typedef boost::variant<std::shared_ptr<A>,std::shared_ptr<B>,std::shared_ptr<C> > base_variant;
    
    struct variant_visitor : public boost::static_visitor<void> {
        void operator()(A*, base_ptr) const {std::cout << "A*\n";}
        void operator()(B*, base_ptr) const {std::cout << "B*\n";}
        void operator()(C*, base_ptr) const {std::cout << "C*\n";}
    };
    
    int main(int, char**)
    {
        // This works, of course.
        base_ptr b(new A());
        base_variant v(std::shared_ptr<A>(new A()));
        unsmartify_variant<base_variant>::type k = rip(v);
        boost::apply_visitor(boost::bind(variant_visitor(), _1, b), k);
        return EXIT_SUCCESS;
    }
    

    最大的问题是 boost::变量

    此外,似乎 variant apply_visitor 通过非常量引用,表示 rip

    推荐文章