any_cast with abstraction

What it says on the tin: a place to discuss proposed new features.
Post Reply
User avatar
syd
Gnome
Posts: 362
Joined: Thu May 01, 2008 1:55 am
Location: Paris, France

any_cast with abstraction

Post by syd »

currently, in order to retrieve the instance hold of an Any, any_cast is used.

Code: Select all

    template<typename ValueType>
    ValueType * any_cast(Any * operand)
    {
        return operand && operand->getType() == typeid(ValueType)
                    ? &static_cast<Any::holder<ValueType> *>(operand->mContent)->held
                    : 0;
    }
the problem is that the cast is possible only if both std::type_info exactly match, if a base class is specified, it won't work. A dynamic_cast would allow to achieve it:

Code: Select all

    template<typename ValueType>
    ValueType * any_cast(Any * operand)
    {
        return &dynamic_cast<Any::holder<ValueType> *>(operand->mContent)->held;
    }
dynamic_cast will return 0 if the cast is not possible, so the behavior is the same, except abstraction can be used.
just my 2 cents ;)
User avatar
nullsquared
Old One
Posts: 3245
Joined: Tue Apr 24, 2007 8:23 pm
Location: NY, NY, USA
x 11

Re: any_cast with abstraction

Post by nullsquared »

I don't quite understand the use of that, can you provide an example? I'm pretty sure you'd want to cast the data itself, not the place holder ... hm.

Also, dynamic cast is a lot slower than static cast, it'd be a bad idea to use it.
User avatar
syd
Gnome
Posts: 362
Joined: Thu May 01, 2008 1:55 am
Location: Paris, France

Re: any_cast with abstraction

Post by syd »

nullsquared wrote:I'm pretty sure you'd want to cast the data itself, not the place holder ... hm.
yeah, I realized the code above is wrong...

To explain better, the problem I was facing with Any was that I cannot retrieve the instance hold if its final type is unknown. I can't write

Code: Select all

 DeviredClass _derived;
Any _anyHoldingDeviredClass(_derived);
BaseClass* _base = any_cast<BaseClass>(_anyHoldingDeviredClass);  //doesn't work because DeviredClass and BaseClass have 2 different std::type_info
I was a bit stuck with this, so I added in Any a method that looks like (don't have the code with me):

Code: Select all

template<typename QueryType> bool Any::Implements(QueryType* output)
{
   output = dynamic_cast<QueryType>(this->mContent->held);
   if(output == 0) return false;
   else return true;
}
which make abstraction working when casting:

Code: Select all

DerivedClass* _derived = new DerivedClass();
Any _any = *_derived;

BaseClass* _base = 0;
if(_any.Implements<BaseClass>(_base)) _base->SomeMethod();
it's useful with plugged-in components in late binding situations, when we don't know its real type.
User avatar
nullsquared
Old One
Posts: 3245
Joined: Tue Apr 24, 2007 8:23 pm
Location: NY, NY, USA
x 11

Re: any_cast with abstraction

Post by nullsquared »

I don't see the point of storing by value. Why not just do this:

Code: Select all

derived *d = new derived();
derived2 *d2 = new derived2();

Ogre::Any any((base*)d);
Ogre::Any any2((base*)d2);

Ogre::any_cast<base*>(any)->doSomeVirtualFunc();
Ogre::any_cast<base*>(any2)->doSomeVirtualFunc();
User avatar
syd
Gnome
Posts: 362
Joined: Thu May 01, 2008 1:55 am
Location: Paris, France

Re: any_cast with abstraction

Post by syd »

this example applies only if you already know those instances are base.
but what if you have a list of Any, filled externally; and you want to find all instances implementing a specific interface; when some them may implement couple interfaces.

here is in what kind of (fictive) situation I see Implements<T> useful:

Code: Select all


void OnSomethingHappens()
{

std::vector<Any>* _components = getComponents();

std::vector<Any>::Iterator i;

for (i = _components->begin(); i != _components->end(); ++i)
{
IWorldListener* _worldListener = 0;
IKeyboardListener* _keyboardListener = 0;
IInteligentEntity* _ia = 0;

if(*(i)->Implements<IWorldListener>(_worldListener)) _worldListener->consumeMsg();
if(*(i)->Implements<IKeyboardListener>(_keyboardListener)) _keyboardListener->SetFocus(true);
if(*(i)->Implements<IInteligentEntity>(_ia)) _ia->RefreshPriorities();
}

}
User avatar
nullsquared
Old One
Posts: 3245
Joined: Tue Apr 24, 2007 8:23 pm
Location: NY, NY, USA
x 11

Re: any_cast with abstraction

Post by nullsquared »

Hm. I see what you're getting at. Still, I think dynamic_cast is a bit too slow, and that there are other ways to do your example with more clarification on the design. For example, getSmartEntites(), getKeyboardListener(), etc. Besides, if you need to know if it matches a type, then why not if (someAny.type() == typeid(IInteligentEntity*)) ...
User avatar
syd
Gnome
Posts: 362
Joined: Thu May 01, 2008 1:55 am
Location: Paris, France

Re: any_cast with abstraction

Post by syd »

I agree the example above looks like a poorly designed architecture, it's just meant to demonstrate technically the capabilities. I'm planning to use it during initializations, not at each frame. here the issue shouldn't be if a dynamic_cast is slower or if there are design alternatives, but if it is useful to be able to query the type an Any holds, to me it is an important need.

I'm working on some programing paradigm, where types need to be self descriptive, in a component oriented environment. that's why Any has an important role here :)
I may post a paper about it one of these days.
User avatar
nullsquared
Old One
Posts: 3245
Joined: Tue Apr 24, 2007 8:23 pm
Location: NY, NY, USA
x 11

Re: any_cast with abstraction

Post by nullsquared »

syd wrote: but if it is useful to be able to query the type an Any holds, to me it is an important need.
Well, that's what I mean:

Code: Select all

boost::any a(new renderer());
assert(a.type() == typeid(renderer*));
assert(a.type() != typeid(entity*));
(never used Ogre::Any, but I'm pretty sure it follows the same implementation)
User avatar
syd
Gnome
Posts: 362
Joined: Thu May 01, 2008 1:55 am
Location: Paris, France

Re: any_cast with abstraction

Post by syd »

the problem of std::type_info is that it doesn't give anything about the super-classes.
to go back to the example above, for a high level class that implements, let's say: ISmartEntity, IPhysicBody and IWorldListener, during intialization each manager classes (WorldMgr, PhysicsMgr and IaMgr) would each of them need to know if the instance implements the required interfaces.
well i made my own any class, if this feature doesn't look useful to anyone, it's fine no need to add it =)
nbeato
Gnome
Posts: 372
Joined: Thu Dec 20, 2007 1:00 am
Location: Florida
x 3
Contact:

Re: any_cast with abstraction

Post by nbeato »

Just to chime in... I've run into similar problems with boost::any (specifically, returning an interface from a factory completely strips any useful type info outside of the interface type). I came to the conclusion that "any" types in C++ are strictly meant to be type safe void pointers so you can separate implementation of modules from the types that users would have. In other words, "any" assumes that user level code has a base "object" or interface type that he can immediately cast back to at compile time. Otherwise, he'd need to roll his own code.
User avatar
nullsquared
Old One
Posts: 3245
Joined: Tue Apr 24, 2007 8:23 pm
Location: NY, NY, USA
x 11

Re: any_cast with abstraction

Post by nullsquared »

Sounds like you're using boost::any wrong. It's meant for passing around any value type (like void*) that is typed at runtime. For example, if you have a GUI update function, you might want a "miscellaneous" parameter that can hold mouse data, button data, etc. Depending on the situation, you're assuming to know what the type is, and you're allowed to cast it. This is such that you can use containers of any's, etc. For example, boost::any is very useful with Lua...

If you're using a factory or something, then there's no point in putting the interface inside an any. Just use the interface itself.
nbeato
Gnome
Posts: 372
Joined: Thu Dec 20, 2007 1:00 am
Location: Florida
x 3
Contact:

Re: any_cast with abstraction

Post by nbeato »

nullsquared wrote:Sounds like you're using boost::any wrong. It's meant for passing around any value type (like void*) that is typed at runtime. For example, if you have a GUI update function, you might want a "miscellaneous" parameter that can hold mouse data, button data, etc. Depending on the situation, you're assuming to know what the type is, and you're allowed to cast it. This is such that you can use containers of any's, etc. For example, boost::any is very useful with Lua...
Yes, after checking, I want it to work in a way that is "wrong". According to the docs: "Discriminated types that contain values of different types but do not attempt conversion between them." For anyone listening in, if you pass IBaseObject * to a boost::any object, the only boost::any_cast that will work is back to IBaseObject *. Granted I never tried to cast back to anything!

But, I disagree that it is "typed at runtime." The any object is typed at compile time. Ya, the any_cast is "run-time" in the sense that it returns a good pointer or NULL using RTTI clone code, but the assignment of the any object is a templated placeholder type that occurs at compile time based on the compile time argument's type to the any constructor.
nullsquared wrote:If you're using a factory or something, then there's no point in putting the interface inside an any. Just use the interface itself.
I think I should fill in the gaps... From a library standpoint, I have no idea what the user's interface is, and I will not extend any library object from a user's class... that would be a bad design move. The only other alternative is to force the user to extend some base library object... which would defeat the point of the any object.

The reason I had problems with this at the library level is because I needed to make decisions in my library based on RTTI information about the user's objects (without knowing anything about the object or forcing the user to inherit my objects). The any object's type_info is taken from a template parameter, which directly ties it to the compile-time type. Since the user simply stored IBase *, all of the type_info provided by any directly exposed IBase * and nothing useful. All of the placeholder's are templated to IBase *, etc. But at compile time, my library doesn't have any useful headers to include to do this. Point is: if you try to use any to do anything outside of storing data for someone who knows the actual type at compile time, even if all you are doing is comparing types of user data at run-time, any objects are not sufficient because they are wired at compile-time.
SigfriedMcWild
Halfling
Posts: 62
Joined: Wed Mar 03, 2004 3:12 am
Location: just a figment of your imagination
Contact:

Re: any_cast with abstraction

Post by SigfriedMcWild »

nbeato wrote: I think I should fill in the gaps... From a library standpoint, I have no idea what the user's interface is, and I will not extend any library object from a user's class... that would be a bad design move. The only other alternative is to force the user to extend some base library object... which would defeat the point of the any object.
You want to make decisions based on the type of the object without knowing anything about the object? Sounds a bit contradictory to me.

If you want to do anything with IBase or derived objects you need to know about IBase. Otherwise IBase might as well be a void*. If IBase is supposed to have a standard interface that you library uses to do things to the UI then IBase (or rather that standard interface) is part for the library and you can do away with the any.

You can use any to allow the UI to store arbitrary stuff in a library provided storage, but the UI will be the only thing capable of using that stuff back out if the libaryr truly knows nothing about IBase
nbeato wrote:Point is: if you try to use any to do anything outside of storing data for someone who knows the actual type at compile time, even if all you are doing is comparing types of user data at run-time, any objects are not sufficient because they are wired at compile-time.
If you want to compare types you can provide a compare method in the IBase interface that is used to do this if type_info is not sufficient.
Post Reply