Jump to content

  • Log In with Google      Sign In   
  • Create Account

How to dereference to the derivied class?/How bad is it of my currently way of doing it?


Old topic!
Guest, the last post of this topic is over 60 days old and at this point you may not reply in this topic. If you wish to continue this conversation start a new topic.

  • You cannot reply to this topic
8 replies to this topic

#1 Conny14156   Members   -  Reputation: 267

Like
0Likes
Like

Posted 28 May 2014 - 07:56 AM

Hi,

I have a map like this were I store deriviedclass pointers inside a basepointer

std::map<std::string,BaseComponent*> componentMap;

I was wondering if there were any "smart" way to dereference it to the derivied class?

my current way is

template<class T_S>
T_S* GetComponent()
{
	std::string test = typeid(T_S).name();
	std::map<std::string, BaseComponent*>::iterator it;
	for (int i = 0; i < 6; i++)
	{
		test.erase(test.begin());
	}
	it = componentMap.find(test);
	if (it != componentMap.end())
	{
		T_S* ptTComponent;
		ptTComponent = (T_S*)it->second;

		return ptTComponent;
	}
	else
		return NULL;
}

I use typeid to get the class "name", and the string is ctrl + c and ctrl + v, but I want some more "safe?" way to get the class name. 



Sponsor:

#2 Juliean   GDNet+   -  Reputation: 2606

Like
2Likes
Like

Posted 28 May 2014 - 08:25 AM

One way is the so called CRTP (curiously recurring template pattern), where you seperate your component class in one normal base class, and one templates class, like this:

class BaseComponent
{
public:

    virtual ~BaseComponent(void) = 0 {};
    
    static unsigned int GenerateId(void)
    {
        return m_typeCounter++;
    }
    
private:

    static unsigned int m_typeCounter; // define & init this in cpp-file to 0
}

template<typename>
class Component :
    public BaseComponent
{
public:
    static unsigned int Type(void)
    {
        static const type = GenerateId();
        return Type;
    }
}

Now you declare your component-classes so:

class MyComponent :
	public Component<MyComponent>
{
}

and then you can write your function to call "::Type()" on the templates component type, which will return the runtime-fixed type-id of the component-type, which you can use for safe map lookup of the component. Note that things get a little more compicated once you want to use components of the same types across dlls, but I doubt that thats what you want to do right now.

 

EDIT: also note that this pattern is extremly useful as what you can already define in the "Component"-class, e.g. you can add a virtual Clone-method to the BaseComponent and override it in the Component-class, so that you don't have to define it for every component-child you declare.


Edited by Juliean, 28 May 2014 - 08:27 AM.


#3 frob   Moderators   -  Reputation: 21224

Like
2Likes
Like

Posted 28 May 2014 - 05:43 PM

When you are properly using inheritance you very rarely need to cast that way. The entire point of deriving from a base class is that code that uses it should never know what class is involved.

See also: Dependency Inversion Principle.

As described in that link, many game engines will provide and implement abstract interfaces. You program against the abstract interface. If you need something that does something, you search based on the abstract interface.

For an example, one major game I worked on, the concrete GameObject class derived from the IGameObject interface. You would not search for a GameObject, you would search for an IGameObject.

Doors and bridges and other portals implemented IHasPortal. It would be a very bad idea to try to search for a concrete instance because there were many classes of things that implemented IHasPortal. There were bi-directional doors and one-way gates and assorted kinds of bridges, drawbridges that could be opened and closed, and downloaded content meant new classes could be added that you would never know about. But if you always searched for objects implementing IHasPortal, you would get anything and everything.

We had quite a lot of them in the code base. IDetonatable, IStoreFront, INotCloneable, and so on.

Use a dynamic_cast and if it returns NULL, it doesn't implement the type. Be a tiny bit wary of using dynamic_cast. On many systems it is very nearly zero-cost, on other systems it has quite a high cost, plus it also depends on if you are searching up the inheritance tree or down it. If you are on one of those systems where dynamic_cast has a high cost, there are tricks you can use with type_id instead. GCC implements dynamic_cast so casting to the base class is optimized to basically the cost of a single pointer indirection, and since the interfaces are base classes, it is practically free.
Check out my personal indie blog at bryanwagstaff.com.

#4 Juliean   GDNet+   -  Reputation: 2606

Like
1Likes
Like

Posted 28 May 2014 - 05:54 PM


When you are properly using inheritance you very rarely need to cast that way. The entire point of deriving from a base class is that code that uses it should never know what class is involved.

 

What you are talking about is inheritance, but when you are going a more composition-oriented approach, then I would slightly argue against you. In a data-oriented component-system, where each entity is composed of multiple components that are all POD, and there are systems that implement behaviour, there isn't even a think like IHasPortal - instead, the entity contains a "Portal"-component. In that case, you would want to cast in the way the OP does - its not casting a specify class, its casting a composite of the class, which is more than ok IMHO. (I'm of course skipping over the part that you should rather have all components of the same type in one array/vector anways, that would be bad for explanation purposes...)



#5 SeanMiddleditch   Members   -  Reputation: 5794

Like
2Likes
Like

Posted 28 May 2014 - 07:13 PM

I use typeid to get the class "name", and the string is ctrl + c and ctrl + v, but I want some more "safe?" way to get the class name.


Why do you need a name at all? Just use the typeinfo value itself. You can use either a map<typeinfo, Component*> or a map<const typeinfo*, Component*> (I don't remember if the former is legal). Don't do this across DLL boundaries, though.

You can use Juliean's approach, too, with a further trick to make it even safer (and to actually work in C++, which does not have static type constructors):

class Base {
public:
  virtual ~Base() = defualt;
  virtual const void* MyTypeId() const = 0;
};

template <typename T>
class DerivedHelper : public Base
{
public:
  virtual const void* MyTypeId() const override { static typeTag = 0; return &typeTag; }
};

class MyComponent : public DerivedHelper<MyComponent>
{
};
You never actually dereference the value returned by MyTypeId, but just use it as an opaque handle to a unique type. The ODR rules require that there be only a single copy of static typeTag so the address returned will always be the same. Again, barring cross-DLL boundaries. You can make this DLL safe by using some macros instead of templates and making sure you define the static value in a .cpp file and not in a .h file.

#6 Servant of the Lord   Crossbones+   -  Reputation: 19520

Like
0Likes
Like

Posted 28 May 2014 - 09:30 PM

The standard way of getting the derived pointer from the base is dynamic_cast:

Derived *derived = dynamic_cast<Derived*>(base); //Returns null on failure, just like your function.

.

Compilable example: [ideone.com]

 

[Edit:] frob mentioned dynamic_cast, I didn't notice that.


Edited by Servant of the Lord, 28 May 2014 - 09:33 PM.

It's perfectly fine to abbreviate my username to 'Servant' rather than copy+pasting it all the time.
All glory be to the Man at the right hand... On David's throne the King will reign, and the Government will rest upon His shoulders. All the earth will see the salvation of God.
Of Stranger Flames - [indie turn-based rpg set in a para-historical French colony] | Indie RPG development journal

[Fly with me on Twitter] [Google+] [My broken website]

[Need web hosting? I personally like A Small Orange]


#7 Juliean   GDNet+   -  Reputation: 2606

Like
0Likes
Like

Posted 29 May 2014 - 03:43 AM


The standard way of getting the derived pointer from the base is dynamic_cast:

Derived *derived = dynamic_cast(base); //Returns null on failure, just like your function.

.

Compilable example: [ideone.com]



[Edit:] frob mentioned dynamic_cast, I didn't notice that.
 

 

dynamic_cast is not an option here, at least not a good one. I wanted to write this already yesterday to frobs post, but didn't realize why (it was too late already). In this example the OP has a container of all the same classes, say there is 12 objects in them, each of them unqiue in type. Performing dynamic_cast on all of them until you found the right one is quite wastefull, in comparison to just getting the typeid (eigther via RTTI or some customized system), getting the value with the entry and static-casting.

 

 

 


You can use Juliean's approach, too, with a further trick to make it even safer (and to actually work in C++, which does not have static type constructors):

 

Why wouldn't my approach work in C++? I'm curious, as I'm actually using it in my c++ game engine, and that quite often biggrin.png I mean, the typeid is not safe across different program starts, so it could happen that one time component "transform" has id 2 and the other time it is 4, but it still is relatively safe in the same execution. I find your approach interesting nevertheless, though for my components, I prefer my way, because due to the linear nature of the id-values I can use a vector for storing the components instead of a map


Edited by Juliean, 29 May 2014 - 03:44 AM.


#8 Servant of the Lord   Crossbones+   -  Reputation: 19520

Like
0Likes
Like

Posted 29 May 2014 - 09:16 AM

dynamic_cast is not an option here, at least not a good one. I wanted to write this already yesterday to frobs post, but didn't realize why (it was too late already). In this example the OP has a container of all the same classes, say there is 12 objects in them, each of them unqiue in type. Performing dynamic_cast on all of them until you found the right one is quite wastefull, in comparison to just getting the typeid (eigther via RTTI or some customized system), getting the value with the entry and static-casting.

 

Good point - I hadn't realizing he was using the type to search for or index the components! I mistakenly thought he already knew which Base* was the correct one. mellow.png

 

In that case, I'd also use a static ID - either linearly, or else a compile-time hash (using constexpr) of the component's type name, to keep the IDs constant from compiler to compiler, and from build to build.

static const uint32_t ID = compile_time_hash(#ComponentName);

The components would then be in a std::unordered_map, not a sorted map.


Edited by Servant of the Lord, 29 May 2014 - 09:45 AM.

It's perfectly fine to abbreviate my username to 'Servant' rather than copy+pasting it all the time.
All glory be to the Man at the right hand... On David's throne the King will reign, and the Government will rest upon His shoulders. All the earth will see the salvation of God.
Of Stranger Flames - [indie turn-based rpg set in a para-historical French colony] | Indie RPG development journal

[Fly with me on Twitter] [Google+] [My broken website]

[Need web hosting? I personally like A Small Orange]


#9 Conny14156   Members   -  Reputation: 267

Like
0Likes
Like

Posted 29 May 2014 - 11:39 AM

mind-blown.jpeg

So many complex word and method!

But will try to improve it from all the feedback!

I think I will go towards Juliean way with the CRTP method, it seems kind of fun. if I make it to work, I will try to improve it with SeanMiddleditch suggestion.

 

And I will remake my std::map to a std::unordered_map as Servant of the Lord suggested. if I got all the above to work 






Old topic!
Guest, the last post of this topic is over 60 days old and at this point you may not reply in this topic. If you wish to continue this conversation start a new topic.



PARTNERS