【问题标题】:c++ boost::any to define my own print ,c++ boost::any 来定义我自己的 print ,
【发布时间】:2011-02-13 16:31:21
【问题描述】:

我一直在努力寻找如何使用boost::any 创建一个可以先使用模板打印任何类型的打印功能。

template <typename T>
struct printer {
    void print(ostream& os, const boost::any& a);
}; 

我需要先定义print()。 我希望任何人都拥有真正的operator &lt;&lt;,这个想法很简单:为每个任何对象附加一个类的实例 printer&lt;T&gt; 与合适的 T 并在 any 的值类型更改时更改此对象。 第一个技术问题是打印机对象依赖于 T 而 any 不是(也不应该是)类模板。

我真的需要今晚或明天的帮助。我有明天的最后期限,但我希望今晚继续工作。

【问题讨论】:

    标签: c++ boost-any


    【解决方案1】:

    有很简单的方法可以做到这一点,在“Beyond the C++ Standard Library: An Introduction to Boost”中描述:

    struct streamer {
      virtual void print(ostream &o, const boost::any &a) const =0;
      virtual streamer * clone() const = 0;
      virtual ~streamer() {}
    };
    
    template <class T>
    struct streamer_impl: streamer{
      void print(ostream &o, const boost::any &a) const { o << *boost::any_cast<T>(a); }
      streamer *clone() const { return new streamer_impl<T>(); }
    };
    
    class any_out {
      streamer *streamer_;
      boost::any o_;
      void swap(any_out & r){
        std::swap(streamer_, r.streamer_);
        std::swap(o_, r.o_);
      }
    public:
      any_out(): streamer_(0) {}
      template<class T> any_out(const T& value)
        : streamer_(new streamer_impl<T>()), o_(value) {}
      any_out(const any_out& a)
        : streamer_(a.streamer_ ? a.streamer_->clone() : 0), o_(a.o_) {}
    
      template <class T>
      any_out & operator=(const T& r) { 
        any_out(r).swap(*this);
        return *this;
      }
      ~any_out() { delete streamer_; }
    
      friend std::ostream &operator<<(std::ostream& o, const any_out & a) {
        if(a.streamer_)
          a.streamer_->print(o, a);
        return o;
      }
    };
    

    然后你使用any_out 而不是boost::any

    【讨论】:

    • 非常感谢 pawel 给我这个解决方案,你太棒了,
    • 卡尔森先生(本书的作者)非常出色。我只记得他的书中有解决这个问题的方法。我很高兴能帮上忙。
    【解决方案2】:

    我是这样做的,我觉得干净又安全:

    any_extension.hpp:

    namespace cpputil
    {
    
    struct AnyWriter
    {
        /// Register a type with the AnyWriter.
        /// @pre T must have an ostream << operator available somewhere
        template<class T> static bool registerType()
        {
            return registeredTypes().emplace(std::type_index(typeid(T)),
                                             std::bind(&AnyWriter::write<T>,
                                                       std::placeholders::_1,
                                                       std::placeholders::_2)).second;
        }
    
        /// Write any registred object to a stream
        /// @pre Underlying type must have been registered with a call to AnyWriter::registerType<T>
        /// @param os is reference to a std::ostream
        /// @param anyObject is a reference to a boost::any
        static void writeAny(std::ostream& os, const boost::any& anyObject);
    private:
    
        // A function object that converts an any to a type and streams it to an ostream
        using WriteFunction = std::function<void (std::ostream&, const boost::any&)>;
    
        // a map of typeinfo to WriteFunction
        using RegisteredTypes = std::unordered_map<std::type_index, WriteFunction >;
    
        // retrieve the WriteFunction map in a safe way
        static RegisteredTypes& registeredTypes();
    
        // Convert an any to a type, and write it to a stream
        template<class T> static void write(std::ostream& os, const boost::any& anyObject) {
            try {
                const T& typedObject = boost::any_cast<const T&>(anyObject);
                os << typedObject;
            }
            catch(boost::bad_any_cast& e) {
                os << "<exception in conversion: " << e.what() << ">";
            }
        }
    
    };
    }
    
    namespace std {
        ostream& operator<<(ostream& os, const ::boost::any& anyObject);
    }
    

    any_extension.cpp:

    #include "any_extension.h"
    #include <string>
    
    namespace cpputil {
    
    namespace AnyWriterRegistration {
        const bool stringRegistered = AnyWriter::registerType<std::string>();
        const bool intRegistered = AnyWriter::registerType<int>();
        const bool doubleRegistered = AnyWriter::registerType<double>();
    }
    
    
    
    AnyWriter::RegisteredTypes& AnyWriter::registeredTypes()
    {
        static RegisteredTypes _registrationMap;
        return _registrationMap;
    }
    
    void AnyWriter::writeAny(std::ostream &os, const boost::any &anyObject)
    {
        auto registered = registeredTypes();
        auto iFind = registered.find(anyObject.type());
        if(iFind == registered.end()) {
            os << "<unregistered type: " << anyObject.type().name() << ">";
        }
        else {
            iFind->second(os, anyObject);
        }
    }
    
    }
    
    namespace std {
    ostream& operator<<(ostream& os, const ::boost::any& anyObject)
    {
        if(anyObject.empty()) {
            os << "<empty>";
        }
        else {
            cpputil::AnyWriter::writeAny(os, anyObject);
        }
        return os;
    }
    }
    

    对于您希望支持的任何类型,只需确保已为其类型调用 AnyWriter::register() 并且操作符

    例如:

    any_test.cpp:

    struct chicken {};
    std::operator<<(std::ostream& os, const chicken& aChicken) {
        os << "cluck!";
        return os;
    }
    
    namespace {
        const bool chickenRegistered = AnyWriter::register<Chicken>();
    }
    
    void chickenTest() {
        boost::any animal = chicken();
        std::cout << animal << std::endl;
    }
    

    输出: 咯咯!

    【讨论】:

      【解决方案3】:

      Pawel Zubrycki 的回答非常好(提到 Björn Karlsson 的书)。

      但代码在以下几行中有一些错误:

      // ...
      o << *boost::any_cast<T>(a);    // should be:   o << *boost::any_cast<T>(&a);
      // ...
      a.streamer_->print(o, a);       // should be:   a.streamer_->print(o, a.o_);
      

      这是 Pawel Zubrycki 答案的更正版本(部分...)

      #ifndef ANY_OUT_H
      #define ANY_OUT_H
      
      #include <iostream>
      #include <boost/any.hpp>
      
      struct streamer {
        virtual void print(std::ostream &o, const boost::any &a) const =0;
        virtual streamer * clone() const = 0;
        virtual ~streamer() {}
      };
      
      template <class T>
      struct streamer_impl: streamer{
        void print(std::ostream &o, const boost::any &a) const { o << *boost::any_cast<T>(&a); }
        streamer *clone() const { return new streamer_impl<T>(); }
      };
      
      class any_out {
        streamer *streamer_;
        boost::any o_;
        void swap(any_out & r){
          std::swap(streamer_, r.streamer_);
          std::swap(o_, r.o_);
        }
      public:
        any_out(): streamer_(0) {}
        template<class T> any_out(const T& value)
          : streamer_(new streamer_impl<T>()), o_(value) {}
        any_out(const any_out& a)
          : streamer_(a.streamer_ ? a.streamer_->clone() : 0), o_(a.o_) {}
      
        template <class T>
        any_out & operator=(const T& r) {
          any_out(r).swap(*this);
          return *this;
        }
        ~any_out() { delete streamer_; }
      
        friend std::ostream &operator<<(std::ostream& o, const any_out & a);
      };
      
      std::ostream &operator<<(std::ostream& o, const any_out & a) {
        if(a.streamer_)
          a.streamer_->print(o, a.o_);
        return o;
      }
      
      #endif
      

      此测试代码有效:

      {
         any_out a = 5;
         std::cout << a << std::endl;
      }
      

      但是!!!!

      以下操作不起作用

      int main()
      {
        char str[] = "mystring";
        any_out a = str;
        std::cout << a << std::endl;
      
        a = "myconststring";
        std::cout << a << std::endl;
      }
      

      这里什么都没有被打印出来。

      为什么??

      类型搞砸了,在下面的构造函数中

      any_out(const T& value)
      

      如果我们将流媒体实例化为

      new streamer_impl<T>()
      

      从构造函数中移除引用,即

        any_out(const T value)
      

      ... 是一种解决方案。

      另一种解决方案是保留引用并调整streamer_impl 的模板实例化。见下文

      以下推荐的解决方案是:

      #ifndef ANY_OUT_H
      #define ANY_OUT_H
      
      #include <iostream>
      #include <boost/any.hpp>
      
      struct streamer {
        virtual void print(std::ostream &o, const boost::any &a) const =0;
        virtual streamer * clone() const = 0;
        virtual ~streamer() {}
      };
      
      template <class T>
      struct streamer_impl: streamer{
        void print(std::ostream &o, const boost::any &a) const { o << boost::any_cast<T>(a); }
        streamer *clone() const { return new streamer_impl<T>(); }
      };
      
      class any_out {
        boost::any o_;
        streamer *streamer_;
        void swap(any_out & r){
          std::swap(streamer_, r.streamer_);
          std::swap(o_, r.o_);
        }
      public:
        any_out(): streamer_(0) {}
      
        template<class T> any_out(const T& value)
          : o_(value),
      #if 1
            streamer_(new streamer_impl<typename std::decay<decltype(value)>::type>)
      #else
            streamer_((o_.type() == typeid(const char *))
                      ? static_cast<streamer *>(new streamer_impl<const char *>)
                      : static_cast<streamer *>(new streamer_impl<T>))
      #endif
        {
        }
      
        // template<class T> any_out(const T value)
        //   : o_(value),
        //     streamer_(new streamer_impl<T>)
        // {
        // }
      
        any_out(const any_out& a)
          : o_(a.o_), streamer_(a.streamer_ ? a.streamer_->clone() : 0) {}
      
        template <class T>
        any_out & operator=(const T& r) {
          any_out(r).swap(*this);
          return *this;
        }
        ~any_out() { delete streamer_; }
      
        friend std::ostream &operator<<(std::ostream& o, const any_out & a);
      };
      
      std::ostream &operator<<(std::ostream& o, const any_out & a) {
        if(a.streamer_)
          a.streamer_->print(o, a.o_);
        return o;
      }
      
      #endif
      

      上面给一些麻烦的测试代码现在可以很好地工作(使用“推荐的解决方案”):

      int main()
      {
        char str[] = "mystring";
        any_out a = str;
        std::cout << a << std::endl;
      
        a = "myconststring";
        std::cout << a << std::endl;
      }
      

      【讨论】:

        【解决方案4】:

        在 Boost 邮件列表中查看此主题: http://lists.boost.org/Archives/boost/2005/01/79232.php

        它有一些想法,其中一些看起来不错,而另一些则不行(对我来说)。但总的来说,这似乎是一项很难以一般方式完成的任务,因为(如该线程中所述),某些类型永远不会是 ostream'able,但可以包含在 boost::any 对象中。

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 2019-06-26
          • 2022-12-14
          • 1970-01-01
          • 2011-04-09
          • 1970-01-01
          • 2013-04-17
          • 1970-01-01
          • 1970-01-01
          相关资源
          最近更新 更多