3

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?

surfcode
  • 445
  • 1
  • 5
  • 20
  • Wouldn't a base class, one that models an arbitrary item in your directory, be an option? Then you can keep a shared_ptr or a normal pointer to specialised objects and then cast them as you access them. – ϹοδεMεδιϲ Oct 22 '13 at 20:58
  • That would be one of the options. But once I store the base class, question is how to know what type I can cast them to, to do something useful with the actual type. Storing typeinfo doesn't seem enough to let me do static_cast( t ), where I need the "T". – surfcode Oct 23 '13 at 02:41

2 Answers2

2

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");
}
ϹοδεMεδιϲ
  • 2,790
  • 3
  • 33
  • 54
  • thank you! Earlier, thought I would use boost::any, then use typeinfo to store a "any" type visitor functor. Using a templated tag type is probably much more efficient. – surfcode Oct 24 '13 at 01:44
1

Th composite design pattern is the classical solution for a such thing

Davidbrcz
  • 2,335
  • 18
  • 27
  • I don't think it is the answer to what the question is asking. I understand some would be leaf and some are composite in the dir. The question is, for leaf, what should be the actual C++ type for "SomeType" above in the Object class. – surfcode Oct 23 '13 at 07:54
  • Composite design pattern; that was new to me. I have done it before. but never thought a design pattern would exist. The most common use of such pattern is in XML / DOM processing. – kingstonian Oct 26 '13 at 20:22