OneOfAType container — storing one each of a given type in a container — am I off base here?

前端 未结 2 1539
耶瑟儿~
耶瑟儿~ 2020-12-09 23:40

I\'ve got an interesting problem that\'s cropped up in a sort of pass based compiler of mine. Each pass knows nothing of other passes, and a common object is passed down the

2条回答
  •  伪装坚强ぢ
    2020-12-09 23:51

    As ergosys said already, std::map is the obvious solution to your problem. But I can see you concerns with RTTI (and the associated bloat). As a matter of fact, an "any" value container does not need RTTI to work. It is sufficient to provide a mapping between a type and an unique identifier. Here is a simple class that provides this mapping:

    #include 
    #include 
    class typeinfo
    {
        private:
            typeinfo(const typeinfo&); 
            void operator = (const typeinfo&);
        protected:
            typeinfo(){}
        public:
            bool operator != (const typeinfo &o) const { return this != &o; }
            bool operator == (const typeinfo &o) const { return this == &o; }
            template
            static const typeinfo & get()
            {
                static struct _ti : public typeinfo {} _inst;
                return _inst;
            }
    };
    

    typeinfo::get() returns a reference to a simple, stateless singleton which allows comparisions.

    This singleton is created only for types T where typeinfo::get< T >() is issued anywhere in the program.

    Now we are using this to implement a top type we call value. value is a holder for a value_box which actually contains the data:

    class value_box
    {
        public:
            // returns the typeinfo of the most derived object
            virtual const typeinfo& type() const =0;
            virtual ~value_box(){}
    };
    
    template
    class value_box_impl : public value_box
    {
        private:
            friend class value;
            T m_val; 
            value_box_impl(const T &t) : m_val(t) {}
            virtual const typeinfo& type() const
            {
                return typeinfo::get< T >();
            }
    };
    // specialization for void.
    template<>
    class value_box_impl : public value_box
    {
        private:
            friend class value_box;
            virtual const typeinfo& type() const
            {
                return typeinfo::get< void >();
            }
        // This is an optimization to avoid heap pressure for the 
        // allocation of stateless value_box_impl instances:
        void* operator new(size_t) 
        {
            static value_box_impl inst;
            return &inst;
        }
        void operator delete(void* d) 
        {
        }
    
    };
    

    Here's the bad_value_cast exception:

    class bad_value_cast : public std::runtime_error
    {
        public:
            bad_value_cast(const char *w="") : std::runtime_error(w) {}
    };
    

    And here's value:

    class value
    {
        private:
            boost::shared_ptr m_value_box;       
        public:
            // a default value contains 'void'
            value() : m_value_box( new value_box_impl() ) {}          
                // embedd an object of type T.
            template 
            value(const T &t) : m_value_box( new value_box_impl(t) ) {}
            // get the typeinfo of the embedded object
            const typeinfo & type() const {  return m_value_box->type(); }
            // convenience type to simplify overloading on return values
            template struct arg{};
            template
            T convert(arg) const
            {
                if (type() != typeinfo::get())
                    throw bad_value_cast(); 
                // this is safe now
                value_box_impl *impl=
                          static_cast*>(m_value_box.get());
                return impl->m_val;
            }
            void convert(arg) const
            {
                if (type() != typeinfo::get())
                    throw bad_value_cast(); 
            }
    };
    

    The convenient casting syntax:

    template
    T value_cast(const value &v) 
    {
        return v.convert(value::arg());
    }
    

    And that's it. Here is how it looks like:

    #include 
    #include 
    #include 
    int main()
    {
        std::map v;
        v["zero"]=0;
        v["pi"]=3.14159;
        v["password"]=std::string("swordfish");
        std::cout << value_cast(v["zero"]) << std::endl;
        std::cout << value_cast(v["pi"]) << std::endl;
        std::cout << value_cast(v["password"]) << std::endl;   
    }
    

    The nice thing about having you own implementation of any is, that you can very easily tailor it to the features you actually need, which is quite tedious with boost::any. For example, there are few requirements on the types that value can store: they need to be copy-constructible and have a public destructor. What if all types you use have an operator<<(ostream&,T) and you want a way to print your dictionaries? Just add a to_stream method to box and overload operator<< for value and you can write:

    std::cout << v["zero"] << std::endl;
    std::cout << v["pi"] << std::endl;
    std::cout << v["password"] << std::endl;
    

    Here's a pastebin with the above, should compile out of the box with g++/boost: http://pastebin.com/v0nJwVLW

    EDIT: Added an optimization to avoid the allocation of box_impl< void > from the heap: http://pastebin.com/pqA5JXhA

提交回复
热议问题