Thread: finding derived class type of a pointer to a base class

  1. #1
    #define WORLD "sad place" LinuxCoder's Avatar
    Join Date
    Mar 2006
    Location
    Portugal
    Posts
    89

    finding derived class type of a pointer to a base class

    Hi everyone,

    I'm trying to accomplish some node-based stuff and i'm trying to set some nodes to allow only some kind of nodes for connection. Now i might have somewhat of a design problem as it seems i can only achieve this behavior by using dynamic_cast<> which i have always heard to be a sign of poor coding design. Here's a small test i've made:
    Code:
    #include <iostream>
    #include <stdexcept>
    
    class nodeA
    {
    protected:
            nodeA *m_node;
    
    public:
            virtual void connect(nodeA *node)
            {
                    if (!node) { throw std::runtime_error("nodeA: Null pointer was passed"); }
                    if (node==this) { throw std::runtime_error("nodeA: Can't connect to itself"); }
                    m_node = node;
                    std::cout << "nodeA: Connection to " << node << " performed \n";
            }
    };
    
    class nodeB : public nodeA
    {
            float somevar;   // to make classes different
            double var1;
    public:
            virtual void connect(nodeA *node)
            {
                    if (!node) { throw std::runtime_error("nodeB: Null pointer was passed"); }
                    if (node==this) { throw std::runtime_error("nodeB: Can't connect to itself"); }
    
                    if (!dynamic_cast<nodeB*>(node)) { throw std::runtime_error("nodeB: Wrong nodetype"); }
    
                    m_node = node;
                    std::cout << "nodeB: Connection to " << node << " performed \n";
            }
    
    };
    
    int main()
    {
            nodeA mNodeA;
            nodeA otherA;
            nodeB mNodeB;
            nodeB otherB;
            nodeA *nodePtr = &otherA;   // start with an A node type
    
            std::cout << "\n";
            std::cout << "mNodeA at " << &mNodeA << "\n";
            std::cout << "otherA at " << &otherA << "\n";
            std::cout << "mNodeB at " << &mNodeB << "\n";
            std::cout << "otherB at " << &otherB << "\n\n";
    
            try {
                    mNodeA.connect(nodePtr);
                    std::cout << "A----->A    OK!" << "\n";
    
                    nodePtr = &otherB;
                    mNodeA.connect(nodePtr);
                    std::cout << "B----->A    OK!" << "\n";
    
                    mNodeB.connect(nodePtr);
                    std::cout << "B----->B    OK!" << "\n";
    
                    nodePtr = &mNodeA;
                    mNodeB.connect(nodePtr);
                    std::cout << "A----->B    OK!" << "\n";
    
                    std::cout << "If we got here no node exceptions were thrown so i'll leave while i'm winning...";
                    std::cout << "\n\n";
                    exit(0);
    
            } catch (const std::runtime_error& e) {
                    std::cout << "Runtime error: " << e.what() << "\n\n";
            }
    
            return 0;
    }
    The output i got from running this small test was:
    Code:
    mNodeA at 0xbfb92c64
    otherA at 0xbfb92c5c
    mNodeB at 0xbfb92c48
    otherB at 0xbfb92c34
    
    nodeA: Connection to 0xbfb92c5c performed 
    A----->A    OK!
    nodeA: Connection to 0xbfb92c34 performed 
    B----->A    OK!
    nodeB: Connection to 0xbfb92c34 performed 
    B----->B    OK!
    Runtime error: nodeB: Wrong nodetype
    Which looks exactly like i expected it to and it seems the way i want things to work out. My only doubt here is if using the dynamic_cast<> for this is a poor design and if there's a better way of implementing this behavior of A accepts A, B derives from A but only Accepts A and possibly other nodes derived from A.

    Sorry for the long post and thanks in advance for any help.

    Cheers

  2. #2
    C++ Witch laserlight's Avatar
    Join Date
    Oct 2003
    Location
    Singapore
    Posts
    28,413
    Just to clarify:
    Code:
    node1.connect(&node2);
    means "node 2 connects to node 1", right?
    Now,
    Code:
    // these are allowed
    BaseNode.connect(&BaseNode);
    BaseNode.connect(&DerivedNode);
    DerivedNode.connect(&DerivedNode);
    
    // this is NOT allowed
    DerivedNode.connect(&BaseNode);
    
    // what about?
    DerivedNode.connect(&DerivedNodeSibling);
    Quote Originally Posted by Bjarne Stroustrup (2000-10-14)
    I get maybe two dozen requests for help with some sort of programming or design problem every day. Most have more sense than to send me hundreds of lines of code. If they do, I ask them to find the smallest example that exhibits the problem and send me that. Mostly, they then find the error themselves. "Finding the smallest program that demonstrates the error" is a powerful debugging tool.
    Look up a C++ Reference and learn How To Ask Questions The Smart Way

  3. #3
    #define WORLD "sad place" LinuxCoder's Avatar
    Join Date
    Mar 2006
    Location
    Portugal
    Posts
    89
    means "node 2 connects to node 1", right?
    Yup, thats exactly what it is supposed to do.
    Code:
    // what about?
    DerivedNode.connect(&DerivedNodeSibling);
    I'm sorry but i'm not understanding what you mean by DerivedNodeSibling in there

    What i was really trying to achieve was to have a common interface class (BaseNode which will i think will be pure abstract) for connecting nodes that would allow me to specify on each derived class (DerivedNodeA, DerivedNodeB,etc... all derived from BaseNode) what other type of nodes to accept.

    Some example relationship:
    • DerivedNodeA accepts DerivedNodeB only.
    • DerivedNodeB accepts DerivedNodeC and DerivedNodeB (when not the same node).
    • DerivedNodeC accepts DerivedNodeA and DerivedNodeB but not DerivedNodeC.
    • All DerivedNode_ derive from BaseNode.


    As i said, this is probably a totally wrong way of going about this. just wanted a common interface for managing all the connections if possible.

    Cheers

  4. #4
    C++ Witch laserlight's Avatar
    Join Date
    Oct 2003
    Location
    Singapore
    Posts
    28,413
    I'm sorry but i'm not understanding what you mean by DerivedNodeSibling in there
    DerivedNodeSibling is a class derived from BaseNode, other than DerivedNode. Basically the DerivedNodeA, DerivedNodeB kind of naming that you used.

    Some example relationship:
    hmm... are these relationships arbitrary, or is there some way to reason why a relationship would exist the way it does?
    Quote Originally Posted by Bjarne Stroustrup (2000-10-14)
    I get maybe two dozen requests for help with some sort of programming or design problem every day. Most have more sense than to send me hundreds of lines of code. If they do, I ask them to find the smallest example that exhibits the problem and send me that. Mostly, they then find the error themselves. "Finding the smallest program that demonstrates the error" is a powerful debugging tool.
    Look up a C++ Reference and learn How To Ask Questions The Smart Way

  5. #5
    #define WORLD "sad place" LinuxCoder's Avatar
    Join Date
    Mar 2006
    Location
    Portugal
    Posts
    89
    Basically i want to have ObjectNode and OperationNode.

    ObjectNode will have specific data and methods for Object creation (different type of objects).
    OperationNode will take input either from ObjectNode objects or from some specifc OperationNode objects.

    I was thinking about having both ObjectNode and OperationNode derived from BaseNode possibly(?). I think there must be an easier approach at what i'm trying to achieve but i'm working my head around and this was all i could come up with

    If you know of a better approach at this goal i'll be happy to learn it

  6. #6
    C++ Witch laserlight's Avatar
    Join Date
    Oct 2003
    Location
    Singapore
    Posts
    28,413
    I was thinking about having both ObjectNode and OperationNode derived from BaseNode possibly(?). I think there must be an easier approach at what i'm trying to achieve but i'm working my head around and this was all i could come up with
    What if you overload the member function connect()?
    Quote Originally Posted by Bjarne Stroustrup (2000-10-14)
    I get maybe two dozen requests for help with some sort of programming or design problem every day. Most have more sense than to send me hundreds of lines of code. If they do, I ask them to find the smallest example that exhibits the problem and send me that. Mostly, they then find the error themselves. "Finding the smallest program that demonstrates the error" is a powerful debugging tool.
    Look up a C++ Reference and learn How To Ask Questions The Smart Way

  7. #7
    #define WORLD "sad place" LinuxCoder's Avatar
    Join Date
    Mar 2006
    Location
    Portugal
    Posts
    89
    That makes all the sense, my only problem is that i'll need to have all the nodes in some kind of container (to store the node-network) and as such it's easier to maintain them all as a BaseNode*, this way when i select 2 nodes i must find out if they can connect to each other only through a BaseNode*.

    What i could do is add a NodeId variable to each node and then compare those to check for compatible connections. What do you think of this approach?

  8. #8
    Registered User
    Join Date
    Jan 2005
    Posts
    7,366
    Dynamic dispatch is one instance where dynamic_cast is used. I didn't read this thread thoroughly, so I'm not sure if this is a case where dynamic dispatch is being used or if there are other, better options for you, but it kind of looks like it might be similar. In cases where the dynamic type of two different objects must be checked you sometimes have to use something dynamic_cast.

    I am using a similar tool when sorting two base class pointers. To properly sort them in some cases I need to know the derived type, so I call a virtual sort method on one of them, and then that method uses dynamic_cast to see if it requires a specific type of sort or just the generic base class sort.

    A NodeId is also a solution, and probably better than dynamic_cast in this case. It still has the problem that the base class (or some other code) must be modified every time you add a new node type to the hierarchy. In this case, a new derived type would require a new Id that doesn't conflict with the other derived types. Again, it probably can't be helped in this case, but that is the reason it is usually frowned upon.

  9. #9
    #define WORLD "sad place" LinuxCoder's Avatar
    Join Date
    Mar 2006
    Location
    Portugal
    Posts
    89
    Imagine this situation, i have BaseNode then i have 3 derived classes, DerivedNodeA, DerivedNodeB and DerivedNodeC, they all expand BaseNode with new methods and new data types, now i need DerivedNodeA for instance to allow to be connected only to DerivedNodeA instances, but i need to have this by passing it a BaseNode ptr, since that's what i use to walk along all the nodes in the network. I have cracked my head but the only thing i've come near a solution is really the dynamic_cast<> thing...

  10. #10
    Registered User
    Join Date
    Jan 2005
    Posts
    7,366
    That or the nodeId. I can't think of another solution, and like I said, it sounds like a job for dynamic dispatch (or similar, you aren't exactly dispatching), so I think you're fine with what you're doing. Use of dynamic_cast is often a sign of bad design, but not always. This just is probably one of those exceptions.

  11. #11
    #define WORLD "sad place" LinuxCoder's Avatar
    Join Date
    Mar 2006
    Location
    Portugal
    Posts
    89
    well, i thought about the nodeId but i can't seem to get a working way for it, since in DerivedNodeA i'll need to use that DerivedNodeA's method so although i can use the NodeId for accepting/rejecting the connections i will always need to cast that BaseNode pointer in order to access those methods, unless i make them all in the BaseNode's class as well. am i not right?

  12. #12
    Registered User
    Join Date
    Jan 2005
    Posts
    7,366
    If you need to call DerivedNodeA specific methods from another DerivedNodeA instance that connects to that node, then you will have to cast it. If all you need to do is veryify that the dynamic type of the other node is DerivedNodeA, and you will only be calling virtual base class functions, then you only need the NodeType.

    So, if some of these methods you need to use don't make sense to be a part of the base class interface, then obviously you would make them part of DerivedNodeA's interface only, and once the connection has been established you will need the casted pointer to access that interface. If that's the case, you might as well use the dynamic_cast to check the type and perform the cast at the same time.

    If all the tasks you need to accomplish after the connection make sense as part of the base class interface, then you can have one virtual GetType method in the base class that returns an enum. The enum could go with the base class and would have to be updated every time a new derived class is created. Each derived class would override the method and return their specific enum value. Then in the connect you can just call GetType on the this node and the current node to see if they can actually connect to each other, and continue to use base class interface methods after the connection without ever casting.

  13. #13
    #define WORLD "sad place" LinuxCoder's Avatar
    Join Date
    Mar 2006
    Location
    Portugal
    Posts
    89
    Thanks for the very explanatory post Daved, but i think i must stick to the dynamic_cast because each of the derived nodes will act ask for specific information (some might not ask for any) to the node IF it is accepted. Some will will require a given set of data and methods while the others will not. I can ofcourse add all those methods to the base class, although that seems a bit overwhelming in terms of having to redesign this base class everytime a given node is added.

  14. #14
    Registered User
    Join Date
    Jan 2005
    Posts
    7,366
    It sounds like you are still trying to justify use of dynamic_cast. You don't have to, IMO. You are making the right decision to use dynamic_cast in this case.

    If you feel like doing more research to feel better about your code, lookup dynamic dispatch.

  15. #15
    #define WORLD "sad place" LinuxCoder's Avatar
    Join Date
    Mar 2006
    Location
    Portugal
    Posts
    89
    Sorry Daved, i guess you are right, it's just that i heard and read so much about how bad it was and that by having that it was a sign of really poor design, so i guess i am really trying to justify it. Thank you very much for taking the time and patience to explain all the things you did to me.

Popular pages Recent additions subscribe to a feed

Similar Threads

  1. sorting number
    By Leslie in forum C Programming
    Replies: 8
    Last Post: 05-20-2009, 04:23 AM
  2. Getting an error with OpenGL: collect2: ld returned 1 exit status
    By Lorgon Jortle in forum C++ Programming
    Replies: 6
    Last Post: 05-08-2009, 08:18 PM
  3. Virtual base class
    By George2 in forum C++ Programming
    Replies: 7
    Last Post: 03-14-2008, 07:45 AM
  4. Smart pointer class
    By Elysia in forum C++ Programming
    Replies: 63
    Last Post: 11-03-2007, 07:05 AM
  5. ras.h errors
    By Trent_Easton in forum Windows Programming
    Replies: 8
    Last Post: 07-15-2005, 10:52 PM