0

After storing objects of different types in the same container using common parent class I need to extract them back.

[Tests/test0.c++]:

int main()
{
    element wrapper;
    wrapper.name = "div";
    wrapper.attributes["id"] = "wrapper";

    cargo<string> text("Learn from yesterday, live for today, hope for tomorrow.");

    wrapper.children.push_back(&text);

    cout << "Name:\t"  << wrapper.name << endl;

         /* I have an explicit cast here,
          * but it can't be used this way
          * since children may have different types
          */
    cout << "Cargo:\t" << ((cargo< string >*) wrapper.children[0])->value << endl;

    return 0;
}

[Source/element.h]

struct element
{
    std::string name;
    std::map< std::string, std::string > attributes;
    std::vector< node* > children;
};

[Source/node.h]

struct node
{ };

[Source/cargo.h]

template <typename Type>
struct cargo
    : public node
{
    Type value;

    cargo(Type value)
        : value(value)
    { }
};

I need to have some kind of type holder to be associated with real node type and use it in farther casting-extracting operations... Instead of that hard-coded one in my test.

UPDATE:

What I'm trying to do is a simple Document Object Model Data structure to use it as symbol table entry for my xml-like language parser. I don't want to use any existing XML library as they are very large. I think the idea of DOM is simple, so I can easily adopt it for some more complex operations, for example, by allowing generic types for the nodes in DOM tree using cargo<Type>. I recognize that the design I adopted may not be the most adequate! So I'm open to suggestions!

I would be thankful for any help!

Rizo
  • 3,003
  • 5
  • 34
  • 49
  • Do you expect to be doing lots of operations on `cargo<>`? – Nim Dec 02 '10 at 18:19
  • You are probably aware that node should have a virtual dctor. – Marcin Dec 02 '10 at 18:28
  • @Nim: The only need for cargo is to store the value. No extra operations will be added. The `Type` passed to to cargo as a template parameter will have all the business included. – Rizo Dec 02 '10 at 18:34
  • @Marcian: Yes, I do. I simplified the code for clearness... – Rizo Dec 02 '10 at 18:34
  • The streaming aside, does the `Type` implement methods which you will call? If so, implement them in `node`, and override in `cargo` which delegates to the instance of `Type`. Will there be the same set of methods on all `Type`s? These are important considerations... – Nim Dec 02 '10 at 18:41
  • 1
    It would help a great deal if you would describe what problem are you trying to solve. Chances are that the solution is much easier :) – Marcin Dec 02 '10 at 18:53
  • 1
    This is my proposal: divide the problem into three parts. A simple document structure, (de)serialization and classes which will contain the data deserialized/serialized from a document. Please look at Boost.Serialization library for some ideas. – Marcin Dec 02 '10 at 19:07
  • If you are parsing XML, and you want a very small XML parser that builds a dom, then TinyXML will do the trick for you. Else you can use simple inheritance - I would avoid the conversion of the types till the last possible moment. So in that sense, all you need is effectively a `Node` and `TextNode` derived from that (at most). Then in each `Node` have a set of `Node` objects etc. It's quite easy to build a tree such as you need... – Nim Dec 02 '10 at 21:30

5 Answers5

1

if you are simply streaming, you could implement the stream operators in the base class and then delegate to a method in the derived class, else look at the visitor pattern. Without having a real grasp of what kind of operations you are likely to be doing on cargo, it's difficult to make further suggestions...

Nim
  • 33,299
  • 2
  • 62
  • 101
1

If you don't plan on treating the container members polymorphically on retrieval, Boost.Variant might be useful to wrap the container members in a deterministic way.

The variant class template is a safe, generic, stack-based discriminated union container, offering a simple solution for manipulating an object from a heterogeneous set of types in a uniform manner. Whereas standard containers such as std::vector may be thought of as "multi-value, single type," variant is "multi-type, single value."

There's some example code in this prior question.

Community
  • 1
  • 1
Steve Townsend
  • 53,498
  • 9
  • 91
  • 140
1


This question is probably more about the design than implementation.
Although Boost.Variant and Boost.Any will work, they will be only a workaround. The real problem may be that variable part of responsibility of classes, derived from node class, is not encapsulated.
You could try to use composition instead. One host class used for common interface and appropriate amount of components/delegates/whatever (those are to be born from a solution design :) ).

Or... a totally different solution may fit you. You may want to venture to meta programing word and ditch the common interface. Instead entities like tuples (type lists) may be of help.

Best Regards,
Marcin

Marcin
  • 897
  • 1
  • 7
  • 19
  • Yes, you're right! This is more about the design than implementation... I have no _real_ experience with related data structures and techniques (algorithms, patterns, tricks...) used for this purpose. I'm just trying to keep things clear and generic, but it leads me to implementation problems like this. I'll search for the solutions you mentioned. Thanks! :) – Rizo Dec 02 '10 at 18:53
0

You won't get along something like this without a cast.

But most importantly, this often means that you're going the wrong way. As long as you decided cargo would inherit publicly from node, you provided a very strong relationship between the two classes, and 'being a node' has a much stronger meaning than :

I can be inserted in a container along with other node derived types

We need to know what is a node and what can be done with it to help you further. However if you really need to stick with your initial solution, boost.variant could help you.

icecrime
  • 74,451
  • 13
  • 99
  • 111
  • The only purpose for `node` class is to be a container stored in `vector`. I could have created separate classes like `TextNode` and `ElementNode`, but I'd like to keep things generic in terms of content... – Rizo Dec 02 '10 at 18:45
0

You should design so the code doesn't care about the base class type. Provide an interface that is the same for all. Or add the pure virtual methods you need to the base class and implement in derived class.

Assuming that is some how not possible, have you tried dynamic_cast? It returns null if the cast fails, rather than throwing as your static_cast above will do.

Hope this helps, Beezler

beezler
  • 646
  • 6
  • 18
  • I did considered using `dynamic_cast`, but I see no use for the requested purpose. `dynamic_cast` also requires knowing _a priori_ the type. Unless I use it to check for non-null castings... (?) – Rizo Dec 02 '10 at 18:40
  • I would leave casting as the last resort. Add a behavior or abstract method that you can implement to get the functionality you are casting to get. – beezler Dec 02 '10 at 19:43