boost::any replacement for the code below

前端 未结 2 1623
终归单人心
终归单人心 2020-12-22 01:01

I wish get rid of boost dependency on my code. I have the following struct construct. When calling and using this struct at another place in the code boost::any_cast

相关标签:
2条回答
  • 2020-12-22 01:20

    Just for fun, I thought I'd create a minimalist any implementation:

    //////////////////////////////////////////
    // my_any.hpp
    #include <memory>
    #include <stdexcept>
    
    struct my_any
    {
        my_any() = default;
        template <typename T> my_any(T const& v) : _storage(new storage<T>(v)) { }
        my_any(my_any const& other)              : _storage(other._storage? std::move(other._storage->clone()) : nullptr) {}
    
        void swap(my_any& other)               { _storage.swap(other._storage); }
        friend void swap(my_any& a, my_any& b) { a.swap(b); };
        my_any& operator=(my_any other)        { swap(other); return *this; }
    
        // todo move semantics
    private:
        struct storage_base { 
            virtual std::unique_ptr<storage_base> clone() = 0;
            virtual ~storage_base() = default; 
        };
        template <typename T>
        struct storage : storage_base {
            T value;
            explicit storage(T const& v) : value(v) {}
            std::unique_ptr<storage_base> clone() { return std::unique_ptr<storage_base>(new storage<T>(value)); }
        };
        std::unique_ptr<storage_base> _storage;
        template<typename T> friend T      & any_cast(my_any      &);
        template<typename T> friend T const& any_cast(my_any const&);
    };
    
    template <typename T> T& any_cast(my_any& a) { 
        if (auto p = dynamic_cast<my_any::storage<T>*>(a._storage.get()))
            return p->value;
        else
            throw std::bad_cast();
    }
    
    template <typename T> T const& any_cast(my_any const& a) { 
        if (auto p = dynamic_cast<my_any::storage<T> const*>(a._storage.get()))
            return p->value;
        else
            throw std::bad_cast();
    }
    

    You can then use it precisely the same fashion as your use-cases showed:

    struct Properties {
        public:
            Properties(const std::string &s="", const my_any& p={}) 
                : name(s), value(p) {}
    
            template <typename T> Properties(T n) { value = n; }
    
            std::string name;
            my_any value;
    };
    
    #include <vector>
    #include <iostream>
    
    typedef std::vector<Properties> Props;
    
    int main()
    {
        Props v;
        v.emplace_back("bye", 42);
        v.emplace_back("vector", v);
    
        std::cout << "v.size(): "          << v.size()                           << "\n";
        std::cout << "v[0].value: "        << any_cast<int>(v[0].value)          << "\n";
        std::cout << "v[1].value.size(): " << any_cast<Props>(v[1].value).size() << "\n";
    
        v[0].value = v;
    
        try {
            std::cout << "v[0].value: " << any_cast<int>(v[0].value) << "\n";
        } catch(std::exception const& e)
        {
            std::cout << e.what() << " exception caught, ok!\n";
        }
    
        std::cout << "v[0].value.size(): " << any_cast<Props>(v[0].value).size() << "\n";
    }
    

    See the output Live On Coliru

    0 讨论(0)
  • 2020-12-22 01:44

    boost::any uses type erasure to store objects of any type, and you can assign it values of different types at runtime. The any_cast is used to retrieve the original value, with the correct type, that was stored in the any object. For instance, your current class allows you to do this

    Properties p("int", 42);
    std::cout << boost::any_cast<int>(p.value) << '\n';
    p = Properties("string", std::string("hello"));
    std::cout << boost::any_cast<std::string>(p.value) << '\n';
    

    You cannot just convert the class above into a template and get identical functionality. If you do that, you'll only be able to store a single type of value. And you must change the entire struct into a template, not just the constructor.

    template<typename T>
    struct Properties {
     public:
     Properties() {}
     Properties(std::string s, T p)
     : name(std::move(s))   // should use initialization list instead
     , value(std::move(p))  // of assignment within the body
     {}
    
     Properties(T n)
     : value(std::move(n))
     {}
    
     std::string name;
     T value;
    };
    

    However, the code I posted above is now illegal.

    Properties<int> p("int", 42);
    std::cout << p.value << '\n';
    // p = Properties<std::string>("string", std::string("hello"));
    // will not compile because Properties<int> and Properties<std::string> are
    // distinct types 
    

    If these restrictions are OK, then the modified definition should work for you.

    0 讨论(0)
提交回复
热议问题