How to print boost::any to a stream?

前端 未结 9 1286
悲哀的现实
悲哀的现实 2020-12-03 07:48

I have a Map std::map, which comes from the boost::program_options package. Now I would like to print the content o

相关标签:
9条回答
  • 2020-12-03 07:54

    Rather than re-writing my class to use boost::spirit::hold_any, I created a way to stream boost::any, similar to what manifest suggested, but just in one place.

    ostream& operator<<(ostream& _os, const boost::any& _any)
    {
      // only define simple type conversions
      if (_any.type() == typeid(int))
        _os << boost::any_cast<int>(_any);
    
       /*any other types you use...*/
    }
    

    Rather cumbersome, but it allows me to stream a boost::any variable anywhere in my code.

    How about being able to construct a boost::spirit::hold_any from a boost:any?

    0 讨论(0)
  • 2020-12-03 07:56

    You could use boost::spirit::hold_any instead. It's defined here:

    #include <boost/spirit/home/support/detail/hold_any.hpp>
    

    and is fully compatible with boost::any. This class has two differences if compared to boost::any:

    • it utilizes the small object optimization idiom and a couple of other optimization tricks, making spirit::hold_any smaller and faster than boost::any
    • it has the streaming operators (operator<<() and operator>>()) defined, allowing to input and output a spirit::hold_any seemlessly.

    The only limitation is that you can't input into an empty spirit::hold_any, but it needs to be holding a (possibly default constructed) instance of the type which is expected from the input.

    0 讨论(0)
  • 2020-12-03 07:58

    Define some aux function to output to stream:

    template<class T>
    bool out_to_stream(std::ostream& os, const boost::any& any_value)
    {
        try {
            T v = boost::any_cast<T>(any_value);
            os << v;
            return true;
        } catch(boost:: bad_any_cast& e) {
            return false;
        }
    }
    

    You can define a special formatting for some types

    template<>
    bool out_to_stream<std::string>(std::ostream& os, const boost::any& any_value)
    {
        try {
            std::string v(std::move(boost::any_cast<std::string>(any_value)));
            os << "'" << v << "'";
            return true;
        } catch(boost:: bad_any_cast& e) {
            return false;
        }
    }
    

    or

    template<>
    bool out_to_stream<bool>(std::ostream& os, const boost::any& any_value)
    {
        try {
            os << ((boost::any_cast<bool>(any_value))? "yes" : "no");
            return true;
        } catch(boost:: bad_any_cast& e) {
            return false;
        }
    }
    

    Then define an output operator for boost::any where you list all types you want to try to cast and output

    std::ostream& operator<<(std::ostream& os, const boost::any& any_value)
    {
        //list all types you want to try
        if(!out_to_stream<int>(os, any_value))
        if(!out_to_stream<double>(os, any_value))
        if(!out_to_stream<bool>(os, any_value))
        if(!out_to_stream<std::string>(os, any_value))
            os<<"{unknown}"; // all cast are failed, an unknown type of any
        return os;
    }
    

    And then for a value_type:

    std::ostream& operator<<(std::ostream& os, const boost::program_options::variable_value& cmdline_val)
    {
        if(cmdline_val.empty()){
            os << "<empty>";
        } else {
            os<<cmdline_val.value();
            if(cmdline_val.defaulted()) 
                os << "(default)";
        }
        return os;
    }
    
    0 讨论(0)
  • 2020-12-03 07:58

    A little late for this party, but anyone that may be interested can also use std::tuple and a std::for_each-like template that iterates over a tuple.

    This is based on the answer from ingomueller.net in this thread.

    I had a recent case where I created a property map (reading configuration values, mainly fundamental types, from an XML file and inserting them into an std::unordered_map, where the value type is of type any. For debugging purposes I wanted to be able to print the entire map with its keys and values along with the type of the value.

    In that project I am not using Boost at all, I used my own any implementation, but its very similar to boost::any.

    The insertion operator basically looks like this:

    template <typename TChar>
    inline std::basic_ostream<TChar>&
    operator<< (std::basic_ostream<TChar>& os, const sl::common::any& v)
    {
        // Types that we support with sl::common::any.
        std::tuple<
            float, double, bool, 
            int8_t, uint8_t, 
            int16_t, uint16_t,
            int32_t, uint32_t, 
            int64_t, uint64_t,
            std::wstring, const wchar_t*,
            StreamInserter::UnsupportedType> t;
    
        // Prepare ostream for printing a value of type any
        StreamInserter si(os, v);
    
        // Iterate over all types in tuple t. If the last type(UnsupportedType) is
        // reached, given v is unsupported.
        for_each(t, si);
        return os;
    }
    

    The for_each template looks like this (C++14):

    template <typename Tuple, typename F, std::size_t ...Indices>
    constexpr void for_each_impl(Tuple&& tuple, F&& f, std::index_sequence<Indices...>) {
        using swallow = int[];
        (void)swallow{1,
            (f(std::get<Indices>(std::forward<Tuple>(tuple))), void(), int{})...
        };
    }
    
    template <typename Tuple, typename F>
    constexpr void for_each(Tuple&& tuple, F&& f) {
        constexpr std::size_t N = std::tuple_size<std::remove_reference_t<Tuple>>::value;
        for_each_impl(std::forward<Tuple>(tuple), std::forward<F>(f),
                      std::make_index_sequence<N>{});
    }
    

    With this just use the StreamInserter class or something similar shown in Ingos answer.

    Hope this helps.

    0 讨论(0)
  • 2020-12-03 08:01

    If you can change boost::any to another type, you can use Boost.TypeErasure. If you ever wanted to create a type that's like any, but only supporting types that support these particular operations at compile time, then this is just for you.

    #include <boost/type_erasure/operators.hpp>
    #include <boost/type_erasure/any.hpp>
    #include <boost/mpl/vector.hpp>
    #include <random>
    #include <iostream>
    
    namespace te = boost::type_erasure;
    
    typedef te::any<boost::mpl::vector<
        te::copy_constructible<>,
        te::destructible<>,
        te::ostreamable<>
    >> streamable_any;
    
    int main()
    {
        streamable_any i(42);
        streamable_any d(23.5);
        std::mt19937 mt;
        streamable_any r(mt);
        std::cout << i << "\n" << d << "\n" << r << "\n";
    }
    

    Live On Coliru

    0 讨论(0)
  • 2020-12-03 08:07

    The list of type switches proposed in other answers can be improved with a loop over a type list using Boost MPL (see documentation of mpl::for_each and mpl::vector). The following code defines an operator<< for any boost::any that is given in the type list SupportedTypes and throws an exception otherwise.

    #include <stdexcept>
    #include <iostream>
    #include <string>
    
    #include <cstdint>
    
    #include <boost/any.hpp>
    #include <boost/mpl/for_each.hpp>
    #include <boost/mpl/vector.hpp>
    
    class StreamInserter
    {
    private:
        std::ostream& os_;
        const boost::any &v_;
        mutable bool has_printed_;
    
    public:
        struct UnsupportedType {};
    
        StreamInserter(std::ostream& os, const boost::any &v)
            : os_(os), v_(v), has_printed_(false) {}
    
        template <typename T>
        void operator()(const T&) const
        {
            if (!has_printed_ && v_.type() == typeid(T))
            {
                os_ << boost::any_cast<T>(v_);
                has_printed_ = true;
            }
        }
    
        void operator()(const UnsupportedType&) const
        {
            if (!has_printed_)
                throw std::runtime_error("unsupported type");
        }
    };
    
    std::ostream& operator<<(std::ostream& os, const boost::any& v)
    {
        typedef boost::mpl::vector<float, double, int8_t, uint8_t, int16_t, uint16_t,
                int32_t, uint32_t, int64_t, uint64_t, std::string, const char*,
                StreamInserter::UnsupportedType> SupportedTypes;
        StreamInserter si(os, v);
        boost::mpl::for_each<SupportedTypes>(si);
        return os;
    }
    
    int main(int, char**)
    {
        std::cout << boost::any(42.0) << std::endl;
        std::cout << boost::any(42) << std::endl;
        std::cout << boost::any(42UL) << std::endl;
        std::cout << boost::any("42") << std::endl;
        std::cout << boost::any(std::string("42")) << std::endl;
        std::cout << boost::any(bool(42)) << std::endl; // throws exception
    }
    
    0 讨论(0)
提交回复
热议问题