Jump to content

  • Log In with Google      Sign In   
  • Create Account

Banner advertising on our site currently available from just $5!


1. Learn about the promo. 2. Sign up for GDNet+. 3. Set up your advert!


Multiple inheritance and casting


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
3 replies to this topic

#1 Juliean   GDNet+   -  Reputation: 3137

Like
0Likes
Like

Posted 29 May 2014 - 05:41 PM

Hello,

 

I have a very specific problem. I recently refactored my visual scripting system, now I have 4 types of component-classes: Input, Output, Attributes, and Return, for all the possible slots that a command could have. There is also a base EventCommand-class. I know its probably not good style, but in order to form a type of command, a middle-class is constructed by deriving from the EventCommand-class and every component that the command-type should have. Now, most code dealing with those EventCommands is written generically, by dealing with the mentioned components. For that matter, the EventCommand-class has methods for querying each Component, returning nullptr in case it is not found, otherwise it returns a pointer to the component casted from the "this"-pointer of the event-command-class. Thats where my problems begin.

 

I used to wrote this cast like this:

s ACCLIMATE_API EventCommand
{
public:

    InputComponent* QueryInput(void);
    OutputComponent* QueryOutput(void);
    AttributeComponent* QueryAttribute(void);
    ReturnComponent* QueryReturn(void);

private:

    // is called by the above 4 methods
    template<typename ComponentType>
    ComponentType* ComponentCast(SlotType type)
    {
        if(m_slots & type)
        {
#ifdef _DEBUG
            return dynamic_cast<ComponentType*>(this);
#else
            return (ComponentType*)this;
#endif
        }
        else
            return nullptr;
    }
    
    unsigned int slotTypes;
}

Since I aimed for optimal performance and generally try to avoid dynamic_cast where possibly without making my architecture needlessy complicated, I wrote the code so that it would dynamic_cast in debug build to ensure that the component is actually a child, and in release builds would just static-cast it. I've spent the last 2 hours, since I just tried to run it in release, trying to find the cause for a unspecific crash in the application, until I stumbled over this by accident. Via google, I found out that you are actually required to use dynamic_cast with multiple inheritance... so this leads to my questions:

 

- How bad is dynamic_cast in terms of performance anyway? I tried to search e.g. stackoverflow, but most answers are eigther very generic or too old, I'd like to know how the situation looks nowadays. Yeah I know, performance is realitiv and measuring just this one component is not going to say much, also feel free to hit me for optimizing prematurely, but I'd still like to hear what you've found out in your experience.

 

- Since I already have type information myself (I need those for some other parts anyway), is there any better way of handling this without dynamic casting? This is probably depending on the answer to the above questions... Yeah, the type-information its not 100% safe. Its just an enum:

		enum SlotType
		{
			INPUT = 1, 
			OUTPUT = 2, 
			ATTRIBUTE = 4, 
			RETURN = 8
		};

So bogus values could be passed, but it is pretty much a controlled environment, and in debug it will complain anyways. Is there probably even some safer way for handling this kind of type information at compile-time rather than having to perform needless runtime checks? I'm mainly asking because the available child classes are already known and very limitied, so I quess there could at least be an alternative to dynamic_cast which seems to be mostly made for the case where your parent class could be anything. What would you do here?

 



Sponsor:

#2 Hodgman   Moderators   -  Reputation: 37945

Like
1Likes
Like

Posted 29 May 2014 - 06:15 PM

- How bad is dynamic_cast in terms of performance anyway? I tried to search e.g. stackoverflow, but most answers are eigther very generic or too old, I'd like to know how the situation looks nowadays. Yeah I know, performance is realitiv and measuring just this one component is not going to say much, also feel free to hit me for optimizing prematurely, but I'd still like to hear what you've found out in your experience.

It depends on your compiler... but, it can be as bad as traversing a tree of lists of strings and doing a lot of strcmp's... which is why you don't often see it used where performance matters.
 

So bogus values could be passed, but it is pretty much a controlled environment, and in debug it will complain anyways.

It's ugly, but in a small system like this it doesn't matter as long as it works. If it was a general purpose system to be used by a whole game, I'd probably complain because maintenance of the enum would become an annoying problem.
 
Any time where you're going from a base pointer to a derived pointer is a bit of a code-smell though... I don't suppose you can redesign your system so that this kind of casting isn't required in the first place?



#3 SeanMiddleditch   Crossbones+   -  Reputation: 9902

Like
1Likes
Like

Posted 29 May 2014 - 09:20 PM

Don't use an enum for SlotType. That half defeats the purpose of having dynamic components in the first place: the ability to trivially add new components (possibly via a dynamically-loaded DLL or a script).

One option is to just insert the type_info of the component type into the vector as a key (maybe using boost::flat_map). It avoids many of the performance problems of dynamic_cast (though you must register/query the component using the same type always) and the problems with using an enum.

You can avoid RTTI altogether if you wish using some simple tricks to generate unique IDs for any class. A very easy one is to have a static variable in each component; taking the _address_ of this variable (the value itself is irrelevant). The address is guaranteed to be unique since each variable must have a unique address (and you redeclare this variable for each component). You can also do it with CRTP, but macros tend to be a bit more palatable to many C++ novices (and even most intermediates, in my experience).

Something like:
 
// component.h
class Component {
  public:
  virtual const void* GetType() const = 0;
  virtual const char* GetName() const = 0;
};

#define DECLARE_COMPONENT \
  private: \
  struct detail { static const char s_Tag; static const char* const s_Name; }; \
  public: \
  static const void* MyType() { return &detail::s_Tag; } \
  virtual const void* GetType() const override { return &detail::s_Tag; } \
  virtual const char* GetName() const override { return detail::s_Name; } \
  private:

#define DEFINE_COMPONENT(name) \
  const char name::detail::s_Tag = 0; \
  const char* const name::detail::s_Name = #name;
// foocomponent.h
#include "component.h"
class FooComponent final : public Component {
  DECLARE_COMPONENT;

public:
  ... stuff ...
};
// foocomponent.cpp
#include "foocomponent.h"
DEFINE_COMPONENT(FooComponent);
// elsewhere
if (pComponent->GetType() != FooComponent::MyType())
  log_error("pComponent is a '%s' and not a FooComponent", pComponent->GetName());

Should be obvious how to apply that to your templates.

You can take it quite a bit further and offer a fairly comprehensive reflection service in your engine, allowing a component to reference a data structure that uniquely identifies the component's type, name, members, base classes/interfaces, methods, and other properties.
 

Any time where you're going from a base pointer to a derived pointer is a bit of a code-smell though... I don't suppose you can redesign your system so that this kind of casting isn't required in the first place?


It's very difficult to avoid in a fully dynamic component-based system. You need a container of all your components but you need to access concrete classes (or specialized interfaces).

#4 Juliean   GDNet+   -  Reputation: 3137

Like
0Likes
Like

Posted 30 May 2014 - 04:16 AM


Don't use an enum for SlotType. That half defeats the purpose of having dynamic components in the first place: the ability to trivially add new components (possibly via a dynamically-loaded DLL or a script).

 

Ah, no no, you misunderstood, or maybe I didn't make it clear, but its not a fully fetched component system here, maybe components its the wrong word here... There is only ever going to be those 4 "components". Each component stand for a type of interaction within the visual-scripting graph. Take a look at this screenshot from unreal blueprints, my system is similar:

 

https://answers.unrealengine.com/storage/attachments/125-arraybp.jpg

 

Don't know if you are familiar with it, but component is eigther : control flow input or output (with arrows connected with white lines) or attribute/return values (colored dots connected with colored lines). The specific objects get their type by combining those components. A "normal" command will have all of the above, a free function that only calculates say sum of 2 floats only gets attribute and return. That is the point where the extensibility comes into play, and I'm using CRTP to manage type info of the specific kinds of commands (I kind of like templates over macros). But for the components themselfs, there is no point in allowing more than those 4, especially via DLL, since the rest of the system won't be able to deal with them, and there is no need for more anyways.

 


It depends on your compiler... but, it can be as bad as traversing a tree of lists of strings and doing a lot of strcmp's... which is why you don't often see it used where performance matters.

 

Uh, that really sounds bad. I don't know how performance-critical it is really going to be here, I didn't really stresstest the scripting system to see where performance bottlenecks lie, etc...

 


Any time where you're going from a base pointer to a derived pointer is a bit of a code-smell though... I don't suppose you can redesign your system so that this kind of casting isn't required in the first place?

 

Well, thats a bit of a problem. As I said, those 4 split classes only arrived from me refactoring the whole system because I had tons of dublicated code, mostly c&p'd. Now in most code, I could simple have the commands composed instead of multiple-inherited, but there is certain points of communication between the different components that have to be implemented by the composite class, which would be kind of hard using composition, like:

class ACCLIMATE_API OutputComponent
{
public:
	typedef std::unordered_map<unsigned int, unsigned int> OutputMap;

	OutputComponent(void);
	OutputComponent(const OutputComponent& output);

	void SetupOutputs(const OutputMap& mOutput);

	void SetOutput(unsigned int slot, unsigned int id);

	unsigned int GetNumOutputs(void) const;
	unsigned int GetOutputTarget(unsigned int slot) const;
	virtual const OutputVector& GetOutputDeclaration(void) const = 0;

	Signal<unsigned int> SigOutput;

protected:

	void CallOutput(unsigned int slot) const;
	const OutputMap& GetOutputs(void) const;

private:
#pragma warning( disable: 4251 )

	OutputMap m_mOutputs;
#pragma warning( default: 4251 )
};

Those protected methods are there for the commands to call the specific output-slots, and the one virtual method has to be overwritten in order to get information about the number of outputs-lots, etc... now agreed, this could be solved by just adding the component as member of the specific class, and declaring those methods as part of the class, just calling the components methods, but there is times like this:

/**********************************
* InputComponent
**********************************/

class ACCLIMATE_API InputComponent
{
public:

	virtual void Execute(double dt) = 0;
	virtual void Reset(void) = 0;

private:
#pragma warning( disable: 4251 )

	OutputVector m_vOutputs;
#pragma warning( default: 4251 )
};

where both Execute and Reset-methods are being called at specific points in time on the component, and I just don't see how to make that happen via composition.

 

Is it probably safe to do something like this?

class EventCommand
{
public:
	InputComponent* QueryInput(void);
	OutputComponent* QueryOutput(void);
	AttributeComponent* QueryAttribute(void);
	ReturnComponent* QueryReturn(void);
	
protected:
	
	EventCommand(InputComponent* pInput, OutputComponent* pOutput, AttributeComponent* pAttributes, ReturnComponent* pReturn);
	
private:
	
	InputComponent* m_pInput;
	OutputComponent* m_pOutput;
	AttributeComponent* m_pAttribute;
	ReturnComponent* m_pReturn;
}

class EventFunction :
	public EventCommand, public AttributeComponent, public ReturnComponent
{
	EventFunction(void) : EventCommand(nullptr, nullptr, this, this)
	{
	}
}

where I store pointer to the component-classes in the Event-Command-class, which basically should get rid of all need for casting, eliminate the enum and also possibly allow for composition since it doesn't matter where the componet comes from. Is this doable/safe?






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