Search code examples
c++boost-variantboost-fusionboost-any

Implement an object directory: boost::any, boost::variant, or?


I'd like to implement a directory object which stores different types of object. Need to be able access the objects by name, get the actual pointer type and serialize it. The object I have in mind looks like this:

struct Object {
    std::string name;
    SomeType ptr;
};
struct Dir {
  std::string name;
  std::set<Object> objects;
};

"SomeType" I was thinking of using Boost::variant. But then it seems like I'd need to add object types into variant list during run-time. Even if I know the object types in a dir ahead, It would become

template <typename Typelist>
struct Object<Typelist> {
    std::string name;
    boost::variant<Typelist> objects;
};

where Typelist is different for different dirs. Then, having a dir of dirs would be the dynamic union of Typelist. It looks complicated. And it will soon hit the limit of 50 variant types. Alternative is Boost::Any to simplify semantics. But I'd like to iterate on the set of objects, and do stuff on it -- each object is a boost::fusion adapt_struct -- I want to fusion::for_each on each member of each object and display them, e.g.. Any alternatives or suggestions?


Solution

  • All depends on how many different types and what kind of performance you need. If the number of object types are limited, then you are better off using a common base class, with specialization for each type.

    The code below makes use of boost::shared_ptr and associated cast functionality. boost::shared_dynamic_cast can be used to cast back-and-forth between the base type and the specialized type.

    #include <boost/shared_ptr.hpp>
    #include <boost/make_shared.hpp>
    #include <iostream>
    #include <list>
    
    namespace stackoverflow
    {
    
    struct base_object
    {
        enum type_t
        {
            directory = 0, file, link,
    
            n_types
        };
    
        const std::string name;
        const type_t type;
    
        base_object(const std::string& name, const type_t& type) :
                name(name), type(type) {
        }
    
        virtual ~base_object()
        {
        }
    };
    
    struct file_object : public base_object
    {
        file_object(const std::string& name) : base_object(name, base_object::file)
        {
        }
    };
    
    struct symlink_object : public base_object
    {
        symlink_object(const std::string& name) : base_object(name, base_object::link)
        {
        }
    };
    
    struct directory_object: public base_object
    {
        std::list<boost::shared_ptr<base_object> > children;
    
        directory_object(const std::string& name) :
                base_object(name, base_object::directory)
        {
        }
    
        template < typename TypeTag >
        boost::shared_ptr< typename TypeTag::object_type > add(const std::string& name);
    };
    
    template < typename ObjectType >
    struct tag
    {
        typedef ObjectType object_type;
    };
    
    typedef tag< directory_object > directory;
    typedef tag< file_object > file;
    typedef tag< symlink_object > symlink;
    
    template < typename TypeTag >
    boost::shared_ptr< typename TypeTag::object_type > directory_object::add(const std::string& name)
    {
        return boost::shared_dynamic_cast< typename TypeTag::object_type , base_object >(
                *children.insert(children.end(),
                        boost::shared_dynamic_cast< base_object, typename TypeTag::object_type >(
                                boost::make_shared< typename TypeTag::object_type >(name))));
    }
    
    }  // namespace stackoverflow
    
    
    int main(void)
    {
        using namespace stackoverflow;
    
        boost::shared_ptr< directory_object > root = boost::make_shared< directory_object >("/");
        root->add<directory>("etc")
                ->add<file>("hosts");
        root->add<directory>("tmp")
                ->add<file>("something.tmp");
        root->add<directory>("var")
                ->add<directory>("lib")
                    ->add<directory>("mysql");
    }