0

I got a good answer to the technical part of my question as to why my current approach to this is not working (assigning derived** to base** is type-unsafe, see also Converting Derived** to Base** and Derived* to Base*). However, I still don't have a good idea of how to implement what I'm thinking of in a C++ manner. I'm starting a new question, since the last title was too specific.

Here's perhaps a clearer explanation of what I am trying to do:

  1. Create a number of objects which are all instances of classes derived from one single class.
  2. Store these objects in some type of master container along with a compile-time human-readable identifier (probably a string?).
  3. Get a list of identifiers from other components, search through the master container, and pass them back (pointers/references to) the corresponding objects so they can read/modify them. I think I need to break type-safety at this point and assume that the components know the derived type that they are asking for by identifier.

I thought this would be relatively simple and elegant to do with maps, vectors, and pointers to objects (I give a simplified example in my my previous question), but it seems I'm going to have to be doing a lot of C-style type casting to allow the components to pass pointers to the locations to store the value from the master container. This indicates to me that I'm not following a C++ paradigm, but what "should" I do?

[Edit] Here's some hypothetical sample code for how I envisioned this, hope this clarifies my thinking:

#include <map>
#include <vector>
#include <string>
using namespace std;

class BaseObj {};
class Der1Obj: public BaseObj {};
class Der2Obj: public BaseObj {};

typedef map<string, BaseObj**> ObjPtrDict;
typedef map<string, BaseObj*> ObjDict;

class BaseComp
{
public:
   ObjPtrDict objs;
};

class DervComp
{
   DervComp(){objs["d1"] = &d1; objs["d2"] = &d2; } // This wouldn't compile
   Der1Obj* d1;
   Der2Obj* d2;
}

typedef vector<BaseComp*> CompList;

void assign_objs(CompList comps, ObjDict objs)
{
   for (auto c = comps.begin(); c != comps.end(); c++)
     for (auto o = c.objs.begin(); o != c.objs.end(); o++)
       *(o->second) = objs[o->first];
}

int main(int argc, char* argv[])
{
    Der1Obj d, d1;
    Der2Obj d2;
    ObjDict objs;
    objs["d"] = &d;
    objs["d1"] = &d1;
    objs["d2"] = &d2;

    DervComp c;
    vector<DervComp*> comps;
    comps.push_back(&c);

    assign_objs(comps, objs);

    return 0;
}
Peter Mortensen
  • 30,738
  • 21
  • 105
  • 131
Bismark
  • 31
  • 1
  • 4
  • 1
    I'm with you right up until 3, why does any of that nessesitate having `Base**` objects anywhere and not just `std::map` (or better yet `std::map>`)? – Collin Jul 12 '12 at 12:05
  • It *can* be elegant, but it requires you to step up the abstraction ladder and actually take advantage of polymorphism by basing your code on `Base` pointers. If necessary, make use of the Visitor pattern for type discrimination. It's impossible to give specific advice without specific use cases. Possibly the whole idea of a master container is getting in your way. – molbdnilo Jul 12 '12 at 12:25
  • @Collin The components define members Derived1*, Derived2*, etc. to point to the object instances. To fill in the values, I need to pass a list of pointers to these members (i.e. Base**) to the top-level search. – Bismark Jul 22 '12 at 10:55

3 Answers3

0

If I got what you want right, you can do it like this:

#include <vector>

class Base
{
public:
    enum eDerived
    {
    //name these whatever you like
        DER1,//for first derived class
        DER2,//for second derived class
        DER3//for third derived class
    };

    virtual eDerived type() = 0;//this will return the class type.
};

class Derived1: public Base
{
public:
    virtual eDerived type() { return DER1; }
};

class Derived2: public Base
{
public:
    virtual eDerived type() { return DER2; }
};

class Derived3: public Base
{
public:
    virtual eDerived type() { return DER3; }
};

int main()
{
    std::vector<Base*> myList;//container for all elements
    //You can store a pointer to any of the derived classes here like this:
    Base * a = new Derived1();
    Base * b = new Derived2();
    Base * c = new Derived3();

    myList.push_back(a);
    myList.push_back(b);
    myList.push_back(c);

    //Iterating through the container
    for( Base * tmp: myList)
    {
        //You can check the type of the item like this:
        if( tmp->type() == Base::DER1 )
        {  
            //and cast to a corresponding type.
            //In this case you are sure that you are casting to the right type, since
            //you've already checked it.
            Derived1 * pointerToDerived1 = static_cast<Derived1 *>(tmp);
        }
    }
}

Ofc you can choose any type of container. If you want to give them an ID, you could either use map, or add it into the class itself.

SingerOfTheFall
  • 29,228
  • 8
  • 68
  • 105
  • 1
    There's very rarely any good reason to implement your own typeid system, C++ has that built in through `dynamic_cast`. – Collin Jul 12 '12 at 12:07
  • @Collin, for some reason I thought OP doesn't want to use dynamic_cast, and wants to make sure of the exact derived type before casting. Let's wait for his input on this. – SingerOfTheFall Jul 12 '12 at 12:09
0

I read your other post, but I think I don´t understand why you would use double pointers. In my understanding you would just use a normal pointer.

E.g.

class Base
{
};

class Deriv : public Base
{
};

std::map< std::string, Base* > ObjectStore;

function Component1( ... )
{
   Base* b = ObjectStore[ "MyObject" ];
   b->DoSomeFancyStuff();
}

function ModifyObjectStore( )
{
   delete ObjectStore[ "MyObject" ];
   ObjectStore[ "MyObject" ] = new Derived(); 

}

I hope this helps.

GeorgT
  • 160
  • 1
  • 9
  • That is interesting...assigning from the map in the class declaration. What if ObjectStore is not populated until run time? – Bismark Jul 24 '12 at 11:58
0

You says, "pass them back the corresponding object". For this why do you want to pass back the base**? You can simply give back the a map from string to pointer back. Please see the code below for explanation.

class Container
{
    void add(const string& aKey_in, Base* b)
    {
        myObjects[aKey_in] = b;
    }
    void getObjs(list<string> aKeys_in, map<string,Base*>& anObjMap_out)
    {
        for(all string s in the aKeys_in)
            anObjMap_out[s] = myObjects[s];
    }
private:
    map<string, base*> myObjects;
};

You conditions meet here: Create a number of objects which are all instances of classes derived from one single class. You could extend the class to have creation logic, factory logic etc.

Store these objects in some type of master container along with a compile-time human-readable identifier (probably a string?). Achieved with the map

Get a list of identifiers from other components, search through the master container, and pass them back (pointers/references to) the corresponding objects so they can read/modify them. I think I need to break type-safety at this point and assume that the components know the derived type that they are asking for by identifier.

You don't need to pass back the pointer to pointer to the client. Just pass back the object pointers.

Additional note: You could implement the pointers with shared_ptr instead of raw pointers. If your client code (whoever is using the getObjs() method) is written properly then you won't need a dynamic cast from base pointer to derived pointer. They should be able to work with the base pointer. Anyway, that is a different question which you haven't asked yet.

PermanentGuest
  • 5,213
  • 2
  • 27
  • 36
  • My verbal description was obviously lacking, so I edited the question to include sample code. The main thing not included in your (quite clear) example is that the "master container" is _outside_ the Component, and the object is a (pointer) member of it. Thus, in my vision, the Component needs to pass out the location of this member (p-to-p) along with the identifier to have an outer process find and fill in the appropriate pointer. – Bismark Jul 24 '12 at 11:57