c++ boost::any to define my own print ,

后端 未结 4 1029
野的像风
野的像风 2021-02-04 22:00

Am struggling a lot to find how to do to use boost::any to create a print function that can print any type using template first.

template 

        
相关标签:
4条回答
  • 2021-02-04 22:15

    I do it like this, which I think is clean and safe:

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

    For any type that you'd like supported, simply ensure that AnyWriter::register() has been called for its type and an operator<< exists for it.

    For example:

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

    output: cluck!

    0 讨论(0)
  • 2021-02-04 22:34

    There is quite easy way to do this, described in "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;
      }
    };
    

    and then you use any_out instead of boost::any.

    0 讨论(0)
  • 2021-02-04 22:34

    Check out this thread on the Boost mailing list: http://lists.boost.org/Archives/boost/2005/01/79232.php

    It has a few ideas, some of which seem sort of OK and some of which don't (to me). Overall, though, this seems like a difficult task to accomplish in a general way, since (as mentioned in that thread), some types will never be ostream'able, yet could be contained in a boost::any object.

    0 讨论(0)
  • 2021-02-04 22:42

    Very nice answer by Pawel Zubrycki (mentioning Björn Karlsson's book).

    But the code has a few errors in the following lines:

    // ...
    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_);
    

    Here's a corrected version of Pawel Zubrycki's answer that works (partially...)

    #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
    

    This test-code works:

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

    However!!!!

    The following does not work:

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

    Here nothing gets printed.

    Why??

    Well the type is messed up, in the following constructor

    any_out(const T& value)
    

    if we then instantiate streamer as

    new streamer_impl<T>()
    

    Removing the reference from the constructor, i.e.

      any_out(const T value)
    

    ... is one solution.

    Another solution is to leave the reference and tweak the template instantiation of streamer_impl. See below

    Which brings as to the following recommened solution is:

    #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
    

    The test-code that gave some trouble above, now works nicely (with the "recommeded solution"):

    int main()
    {
      char str[] = "mystring";
      any_out a = str;
      std::cout << a << std::endl;
    
      a = "myconststring";
      std::cout << a << std::endl;
    }
    
    0 讨论(0)
提交回复
热议问题