Thread: How would you do this (object interlinking)

  1. #1
    *******argv[] - hu? darksaidin's Avatar
    Join Date
    Jul 2003
    Posts
    314

    How would you do this (object interlinking)

    I have a parent object and quite a lot of child objects (that are not fields of the parent).
    The parent doesn't rely 100% on them, but it sometimes needs their support.
    Sometimes a child object needs help by another child object.
    Therefore the parent object needs to know all child objects, each child object needs to know (almost) every other child object but none of them needs to know the parental object.

    Until that point, normal pointers do the job quite well. However, sometimes child objects die (the parent is immortal) and need to be replaced. In that case I would have to copy their new pointer target to all the other child objects which is possible but probably not very clever style.

    So I thought about using references in the child objects. The parent still keeps pointers to valid child objects and each child object keeps a reference of the pointers of interest for it. So updating a pointer in the parent object would at the same time update all of the child objects.
    Unfortunatly, I can't use references in objects because they need to be initialized and thats not possible in a class (or am I wrong?).

    Is there any other way without the use of a pointer to a pointer (which would result in pretty messy code) ? Or maybe a trick to use references ? Should I use a different concept (ouch) ?

    Any comments would help!
    [code]

    your code here....

    [/code]

  2. #2
    Guest Sebastiani's Avatar
    Join Date
    Aug 2001
    Location
    Waterloo, Texas
    Posts
    5,708
    Are these the same *types* of objects, or are the child objects derived from the parent. Would you mind giving a code example of your problem?

    One of the most common approaches actually uses a feature you thought unnecessary: To have the children simply retain a pointer to the parent. This actually solves the data integrity issue quite well. Here's an example:

    Code:
    #include <list>
    #include <iostream>
    
    struct child;
    
    struct parent {
    list <child*> children;
    child * lookup(child * sibling)
    {
     for(list<child*>::iterator 
          it = children.begin(); it != children.end(); ++it)
            if(*it == sibling)
             return *it;
     return 0;
    }
    void disown(child * bad_seed)
    {
      for(list<child*>::iterator 
          it = children.begin(); it != children.end(); ++it)
            if(*it == bad_seed)
           {
            children.erase(it);
            break;
           }
    }
    }; 
    
    struct child {
    parent & father;
    list <child*> siblings;
    child(parent& _father):father(_father)
    {
     father.children.push_back(this);
    }
    child * lookup(child * sibling)
    {
     return father.lookup(sibling);
    }
    void on_notify(const char * message)
    {
     cout << "Recieved: " << message << endl;
    }
    void attach(child * sibling)
    {
     siblings.push_back(sibling);
    }
    void notify(const char * message)
    {
     child * valid;
     
     for(list<child*>::iterator 
          it = siblings.begin(); it != siblings.end(); ++it)
         {
           valid = lookup(*it);
             if(!valid) 
            {
              it = siblings.erase(it);
              if(it == siblings.end()) break;
            }
             else valid->on_notify(message);
          }
    }
    };
    
    
    
    
    
    
    
    
    
    int main()
    {
     parent dad;
     child one(dad), two(dad), three(dad);
     one.attach(&two);
     two.attach(&three);
     three.attach(&one);
    
     //dad.disown(&one); // uncomment this and one less message is printed!
     //dad.disown(&two); // uncomment this and one less message is printed!
     //dad.disown(&three); // uncomment this and one less message is printed!
    
    
     char * message = "Notification";
    
    
     one.notify(message);
     two.notify(message);
     three.notify(message);
     return cin.get();
    }
    Code:
    #include <cmath>
    #include <complex>
    bool euler_flip(bool value)
    {
        return std::pow
        (
            std::complex<float>(std::exp(1.0)), 
            std::complex<float>(0, 1) 
            * std::complex<float>(std::atan(1.0)
            *(1 << (value + 2)))
        ).real() < 0;
    }

  3. #3
    *******argv[] - hu? darksaidin's Avatar
    Join Date
    Jul 2003
    Posts
    314
    I'm not sure about how I could give a code example that is not too long.

    The objects maintained by the engine(parent) are of rather different nature: a renderer, a windows message handler, a macro association engine that resolves function name-strings to C++-function pointers, an array of BSPs, a camera.... etc... so all kinds of different stuff. Some of them require support by others, i.e. the camera needs access to the renderers modelview matrix.

    I thought about what you said: Giving all child-objects a pointer to the parent object. That might work, but wouldn't it require all of them to include the header of the engine class? How could I do this if at the same time the engine requires headers of all of those classes.
    [code]

    your code here....

    [/code]

  4. #4
    Guest Sebastiani's Avatar
    Join Date
    Aug 2001
    Location
    Waterloo, Texas
    Posts
    5,708
    Ah, the circular #include problem.

    The easiest way to work out all of the semantics is to create a 'dummy' project that mimics some of the problematic declarations.

    Perhaps you could post a symbolic interactive description of the inter-relationship of these classes?
    Code:
    #include <cmath>
    #include <complex>
    bool euler_flip(bool value)
    {
        return std::pow
        (
            std::complex<float>(std::exp(1.0)), 
            std::complex<float>(0, 1) 
            * std::complex<float>(std::atan(1.0)
            *(1 << (value + 2)))
        ).real() < 0;
    }

  5. #5
    Registered User
    Join Date
    May 2003
    Posts
    1,619
    Hmm, I think you have way too much interdependancy on your part. Try using some version of the Controller-Model-View scheme. There are a number of variations of this, I prefer my own, which I call ICMV (Interface - Controller - Model - View). I have four main classes, which form a single "item":

    Interface : This is the main class; to construct the item, you construct this class. It exposes different functions, but internally it just forwards all function calls to Controller. The reason that I make this class, not Controller, the main class is because I want to be able to swap Controllers and use them polymorphically.

    Interface has, as private data members, a pointer to Controller, and an instance of Model.

    Controller: A base class from which all controllers of this type are derived. The controller is the brains -- all input goes here, and it makes all the choices about what to do.

    Controller has, as a private data member, a reference to Model.

    Model: This class encapsulates the state of the item. Most of the item's variables are stored here. The state is updated by the Controller and read by the Views. Model is also responsible for owning Views.

    View: This is an ABC, its child classes implement ways of looking at the model. Graphics, sound, disk, etc. outputs. It has a reference to Model, but in general, Views should never modify Model (I usually make it a reference to const).

    Essentially, the Controller is the brains of the item, making all the decisions as to what to do next. The Model is the state of the item, which should contain all information about the item itself. The Views are ways of representing and displaying the data of the Model.

    The Controller makes choices and updates the Model. It also tells the Views about the changes, and the Views decide if they have to do anything as a result. Interface just wraps everything up and allows polymorphism from Controller.

  6. #6
    *******argv[] - hu? darksaidin's Avatar
    Join Date
    Jul 2003
    Posts
    314
    Thanks for your help. I just had an idea how I could resolve all those dependencies.

    CEngine (parent) does not use any membersfunctions of it's child objects in the public scope. So I used a PIMPL to hide the private scope and forward declared all child objects. The engines public scope only requires pointers to child objects (in the various registerXXX(xxx_type*) functions), so I don't need to include their header anymore.

    Thanks for your help, I got the idea while trying to write down the symbolic description of the classes inter-relationship you suggested!

    edit: I just read you post Cat. I'm not sure if I understand your basic concept at all. You use some kind of "hub" that forwards all the information between the objects. Is that right? That sounds great, but also like a lot of overhead (to type). I guess I'll print that and try to grasp it
    Last edited by darksaidin; 08-29-2003 at 11:09 AM.
    [code]

    your code here....

    [/code]

  7. #7
    *******argv[] - hu? darksaidin's Avatar
    Join Date
    Jul 2003
    Posts
    314
    I got another question. Hopefully this won't be seen as a bump. I just didn't want to open another thread as it is closely related to this topic.

    In the parent class' (CEngine) public scope I've added the following functions:

    Code:
        CRenderer 		&renderer();
        CCamera		&camera();
        COverlay		&overlay();
        CMacroHandler 	&macrohandler();
    In the implementation, renderer() looks like this:

    Code:
    // return reference to selected renderer
    CRenderer &CEngine::renderer() {
        return *p->poRenderer;
    }
    This worked fine until I tried to inline the function. I get linker errors for each functioncall to renderer() .

    I'm fairly new to C++. Maybe I can't inline references ? How would this affect execution speed ?

    ../_obj/camera.o(.text+0x1ed):camera.cpp: undefined reference to `CEngine::renderer()'
    [code]

    your code here....

    [/code]

  8. #8
    Registered User
    Join Date
    May 2003
    Posts
    1,619
    Inline functions must live in the .h file, because they must be visible to all compilation units that use them.

Popular pages Recent additions subscribe to a feed

Similar Threads

  1. using this as synchronization object
    By George2 in forum C# Programming
    Replies: 0
    Last Post: 03-22-2008, 07:49 AM
  2. circular doubly linked list help
    By gunnerz in forum C++ Programming
    Replies: 5
    Last Post: 04-28-2007, 08:38 PM
  3. Replies: 60
    Last Post: 12-20-2005, 11:36 PM
  4. Question on l-values.
    By Hulag in forum C++ Programming
    Replies: 6
    Last Post: 10-13-2005, 04:33 PM
  5. A question about constructors...
    By Wolve in forum C++ Programming
    Replies: 9
    Last Post: 05-04-2005, 04:24 PM