简体   繁体   中英

Unique ID Defined by Most-Derived Class accessible through Base Class

Okay, so, the idea is that I have a map of "components", which inherit from componentBase, and are keyed on an ID unique to the most-derived*.

Only, I can't think of a good way to get this to work. I tried it with the constructor, but that doesn't work (Maybe I did it wrong). The problem with any virtual, etc, inheritance tricks are that the user has to impliment them at the bottom, which can be forgotten and makes it less... clean.

*Right phrase? If -> is inheritance; foo is most-derived: foo->foo1->foo2->componentBase

Here's some code showing the problem, and why CRTP can't cut it: (No, it's not legit code, but I'm trying to get my thoughts down)

#include<map>

class componentBase
{ public: virtual static char idFunction() = 0; };

template <class T>
class component
    : public virtual componentBase
{
public:
    static char idFunction(){ return reinterpret_cast<char>(&idFunction); }
};

class intermediateDerivations1
    : public virtual component<intermediateDerivations1>
{
};

class intermediateDerivations2
    : public virtual component<intermediateDerivations2>
{   };

class derived1
    : public intermediateDerivations1
{   };

class derived2
    : public intermediateDerivations1
{   };


//How the unique ID gets used (more or less)
std::map<char, componentBase*> TheMap;

template<class T>
void addToMap(componentBase * c)
{
    TheMap[T::idFunction()] = c;
}

template<class T>
T * getFromMap()
{
    return TheMap[T::idFunction()];
}

int main()
{

    //In each case, the key needs to be different.

    //For these, the CRTP should do it:
    getFromMap<intermediateDerivations1>();
    getFromMap<intermediateDerivations2>();

    //But not for these.
    getFromMap<derived1>();
    getFromMap<derived2>();

    return 0;
}

More or less, I need something that is always there, no matter what the user does, and has a sortable value that's unique to the most-derived class.

Also, I realize this isn't the best-asked question, I'm actually having some unexpected difficultly wrapping my head around it in words, so ask questions if/when you need clarification.

Edit: Using Beta's phrasing; The class derived2 has an ID number, unique among all classes derived from ComponentBase, and from which no other classes are derived - except that there should be no usage cases in which we're dealing with an instance when we don't know the most-derived type. That is, we should never have to deal with a foo1* that is actually pointing to a ``foo`.

Any time that I need to access this ID, I have type information about the most-derived class; via the templated nature of addComponent, getComponent and removeComponent.

Hmm, to put it another way; I need to "convert" type into a unique number while I know the type, so that I can later distinguish between two things when I don't have the type information.

I don't understand why you are using reinterpret_cast in class component .

As far as having unique IDs, you should have some kind of process to validate that the ID is not used by any derived instance.

On the other hand, each class should implement a static clone or create method. The factory would have a map of . The function pointer points to the specific class' create or clone method. Since the std::map cannot be created as a const static entity during compilation time, I generally use constant static arrays to hold the IDs and function pointers. If the array is small, it is insignificant in performance to a map .

Example:

class Base
{;};

// Declare a synonym for a pointer to the creation function.
typedef Base *    (*P_Creation_Function)(unsigned int id);

struct Creation_Entry
{
    unsigned int         class_id;
    P_Creation_Function  p_creator;
};

class Child1 : public Base
{
  public:
    static Base * create(unsigned int id);
};

Creation_Entry  creation_table[] =
{
    {1, Child1::create},
};

static const unsigned int NUM_CREATORS =
    sizeof(creation_table) / sizeof(creation_table[0]);

//  Process 1:  search table for ID
for (unsigned int i = 0; i < NUM_CREATORS; ++i)
{
    if (creation_table[i].class_id == new_id)
    {
        return (*creation_table[i].p_creator)(new_id);
    }
}

//  Process 2:  Execute each creation function in the table.
//  Creation functions will return NULL if the ID is not a match
Base * p_new_object;
for (unsigned int j = 0; j < NUM_CREATORS; ++j)
{
    p_new_object = (*creation_table[j].p_creator)(new_id);
    if (p_new_object)
    {
        return p_new_object;
    }
}

For small projects, the overhead of a creation function return NULL is not significant compared to other bottlenecks (such as disk i/o). The second process does not require the factory to know the class ID; the class ID remains encapsulated in the class.

I've used both processes and implement them depending on my mood and the project size. :-)

You could make use of the typeid operator.

If you have a ComponentBase* component, then

typeid(*component)

will return a type_info& object that uniquely identifies the class of the object that the component pointer points at. (If component points at a Derived object, then this will return the type_info object that belongs to the Derived class. Note though that typeid(component) would return a type_info& that represents the type ComponentBase *, so dereferencing the pointer is important.)

Then you can use eg. the address of this type_info object, or the result of type_info::name() as the key for your map.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM