generic dynamic / runtime cast (cast to real runtime type)

67 views
Skip to first unread message

David Feurle

unread,
Oct 16, 2016, 12:10:59 PM10/16/16
to ISO C++ Standard - Future Proposals
Hello,

I like the works of C# dynamic which casts a variable to it's real runtime type and I would like to see a similar feature in C++.

Example in C#:

Object o = new String("hahaha");
dynamic d = o;                                    // the type of d is now string
d += "haha";                                        //append something to the string

As you can see the dynamic variable will be of the runtime type of it's content. The following code uses the operators of string.
If the dynamic where initialized with a different object of a different type (for example int) the code would need to use the operators of the runtime type.
The runtime types are not known at compile time -> the code needs to be "recompiled" at runtime according to the runtime type.

This is not possible in C++. As a workaround the possible types of the runtime_cast could be given as a template parameter.
The code that uses the dynamic variable could be compiled  since all candidates are known at compiletime.
Rtti could be used to determine the type at runtime.

Example in C++:

struct Base {
    virtual Base() = default;
};

struct A : public Base { void fun(); };
struct B : public Base { void fun(); };

fun(A*);
fun(B*);

Base* b = new A();
auto dyn = runtime_cast<A*, B*>(b);   // the type of dyn is either A* or B*
if (!dyn) { return; }                                 //the type of b was neither A* or B*
dyn->fun();                                           // either A::fun is called or B::fun
                                                             //all possible types of dyn are known at compiletime and code could be generated for each one of it.
fun(dyn);                                               //overload resolution is triggered here according to the runtime type of dyn

The code using the variable dyn needs to be compiled with all possible types of it. At runtime the type of the object is used to decide which concrete implementation is called.
Using this feature would enable us to completele obsolete the visitor pattern and trigger overload resolution based on a runtime type.

What do you think about this?

Regards,

David Feurle


Thiago Macieira

unread,
Oct 16, 2016, 1:22:31 PM10/16/16
to std-pr...@isocpp.org
Em domingo, 16 de outubro de 2016, às 09:10:59 PDT, 'David Feurle' via ISO C++
Standard - Future Proposals escreveu:
> Hello,
>
> I like the works of C# dynamic which casts a variable to it's real runtime
> type and I would like to see a similar feature in C++.

You're describing something that C++ has already had since the very first
version: virtual functions.

> Example in C#:
>
> Object o = new String("hahaha");
> dynamic d = o; // the type of d is now
> string
> d += "haha"; //append something to
> the string
>
> As you can see the dynamic variable will be of the runtime type of it's
> content. The following code uses the operators of string.

In C++, we already have that without the need for a special keyword.

Object *o = new DerivedObject("hahaha");
*o += "haha";

Works just fine, provided that operator+= is a virtual function in Object.

> The code that uses the dynamic variable could be compiled since all
> candidates are known at compiletime.

Say what?

> Rtti could be used to determine the type at runtime.

It already can be. In the example I gave above:

Object *o = new DerivedObject("hahaha");

We can assert:
&typeid(*o) == &typeid(DerivedObject)

> Example in C++:
>
> struct Base {
> virtual Base() = default;

You meant ~Base. You also need:

virtual void fun() = 0;

> };
>
> struct A : public Base { void fun(); };
> struct B : public Base { void fun(); };
>
> fun(A*);
> fun(B*);
>
> Base* b = new A();
> auto dyn = runtime_cast<A*, B*>(b); // the type of dyn is either A* or B*
> if (!dyn) { return; } //the type of b was
> neither A* or B*
> dyn->fun(); // either A::fun is
> called or B::fun
> //all possible

Why do we need this? With the pure virtual like I added, you can write:

Base *b = new A;
dyn->fun();

> What do you think about this?

I think it's unnecessary to change what we already have.

--
Thiago Macieira - thiago (AT) macieira.info - thiago (AT) kde.org
Software Architect - Intel Open Source Technology Center

David Feurle

unread,
Oct 16, 2016, 1:44:01 PM10/16/16
to ISO C++ Standard - Future Proposals
Hello Thiago, 

there are things that can not be done with virtual functions. For example double dispatch and the visitor.

It is possible to simulate a dynamic call functionality with virtual functions. But it is a hazzle and several classes / functions need to be implemented to add this functionality.

Imagine the following "standard" example for double dispatch:

class SpaceShip {};
class ApolloSpacecraft : public SpaceShip {};

class Asteroid {
public:
  virtual void CollideWith(SpaceShip&) {
    cout << "Asteroid hit a SpaceShip" << endl;
  }
  virtual void CollideWith(ApolloSpacecraft&) {
    cout << "Asteroid hit an ApolloSpacecraft" << endl;
  }
};

class ExplodingAsteroid : public Asteroid {
public:
  virtual void CollideWith(SpaceShip&) {
    cout << "ExplodingAsteroid hit a SpaceShip" << endl;
  }
  virtual void CollideWith(ApolloSpacecraft&) {
    cout << "ExplodingAsteroid hit an ApolloSpacecraft" << endl;
  }
};

SpaceShip& theSpaceShipReference = theApolloSpacecraft;
theAsteroid.CollideWith(theSpaceShipReference);
theAsteroidReference.CollideWith(theSpaceShipReference);
 
This will not call the correct function even though we have a overload for it. Currently we need to employ a visitor / double dispatch to all classes. In C# it is part of the language. 
The visitor pattern is often used and it would not be neccessary to implement it if we would have a runtime cast. Read more on the motivation section for visitor on wikipedia

Tony V E

unread,
Oct 16, 2016, 1:57:19 PM10/16/16
to 'David Feurle' via ISO C++ Standard - Future Proposals
There are some pattern matching proposals that might be related here.

Sent from my BlackBerry portable Babbage Device
From: 'David Feurle' via ISO C++ Standard - Future Proposals
Sent: Sunday, October 16, 2016 1:44 PM
To: ISO C++ Standard - Future Proposals
Subject: Re: [std-proposals] generic dynamic / runtime cast (cast to real runtime type)

--
You received this message because you are subscribed to the Google Groups "ISO C++ Standard - Future Proposals" group.
To unsubscribe from this group and stop receiving emails from it, send an email to std-proposal...@isocpp.org.
To post to this group, send email to std-pr...@isocpp.org.
To view this discussion on the web visit https://groups.google.com/a/isocpp.org/d/msgid/std-proposals/39dc6003-567c-4506-9fbb-2e0340313960%40isocpp.org.

Nicol Bolas

unread,
Oct 16, 2016, 2:03:14 PM10/16/16
to ISO C++ Standard - Future Proposals


On Sunday, October 16, 2016 at 1:44:01 PM UTC-4, David Feurle wrote:
Hello Thiago, 

there are things that can not be done with virtual functions. For example double dispatch and the visitor.

It is possible to simulate a dynamic call functionality with virtual functions. But it is a hazzle and several classes / functions need to be implemented to add this functionality.

Imagine the following "standard" example for double dispatch:

class SpaceShip {};
class ApolloSpacecraft : public SpaceShip {};

class Asteroid {
public:
  virtual void CollideWith(SpaceShip&) {
    cout << "Asteroid hit a SpaceShip" << endl;
  }
  virtual void CollideWith(ApolloSpacecraft&) {
    cout << "Asteroid hit an ApolloSpacecraft" << endl;
  }
};

class ExplodingAsteroid : public Asteroid {
public:
  virtual void CollideWith(SpaceShip&) {
    cout << "ExplodingAsteroid hit a SpaceShip" << endl;
  }
  virtual void CollideWith(ApolloSpacecraft&) {
    cout << "ExplodingAsteroid hit an ApolloSpacecraft" << endl;
  }
};

SpaceShip& theSpaceShipReference = theApolloSpacecraft;
theAsteroid.CollideWith(theSpaceShipReference);
theAsteroidReference.CollideWith(theSpaceShipReference);
 
This will not call the correct function even though we have a overload for it. Currently we need to employ a visitor / double dispatch to all classes.

Or you could just not poorly design your collision system.
 
In C# it is part of the language. 
The visitor pattern is often used and it would not be neccessary to implement it if we would have a runtime cast.

Alternatively, we could just make visitation more reasonable to implement.

C++ is a statically typed language. What you're talking about requires dynamic dispatching of a kind that C++ as a language is simply not capable of. Not without adding a bunch of hacks to the language.

Take your specific case, for example: `dyn->fun()`, where `dyn` may be `A*` or `B*`. Because C++ is a statically typed language, it cannot have a type which may be one of two things. Therefore, it must be a type which is neither, like a `variant` or somesuch. This type will therefore have to synthesize replicas of all of the operations that could be performed on any of those types. Even "operator-dot" gymnastics don't permit that.

Plus, `A::fun` and `B::fun` are allowed to return different things. Even if they're both virtual functions inherited from the same base class, the derived class versions can differ in terms of return values. So what does `dyn->fun()` return? It would have to return a `variant` of the possible return values. Which makes it hard to use those return values.

This is far more complex than you give it credit for being. C# is a much more malleable language that C++.

David Feurle

unread,
Oct 16, 2016, 2:17:20 PM10/16/16
to ISO C++ Standard - Future Proposals
It is just a standard example but not the one that makes the most sense - I'll give you that. There are situations where visitor makes a lot of sense. Imagine you recieve messages from the network. Different network handlers need to react to the messages received differently. It is not possible to add the behaviour to the message (lots of different handlers) and the handler needs to dispatch all the messages according to it's (unknown) type.
 
In C# it is part of the language. 
The visitor pattern is often used and it would not be neccessary to implement it if we would have a runtime cast.

Alternatively, we could just make visitation more reasonable to implement.

How could that be?
 
C++ is a statically typed language. What you're talking about requires dynamic dispatching of a kind that C++ as a language is simply not capable of. Not without adding a bunch of hacks to the language.

Take your specific case, for example: `dyn->fun()`, where `dyn` may be `A*` or `B*`. Because C++ is a statically typed language, it cannot have a type which may be one of two things. Therefore, it must be a type which is neither, like a `variant` or somesuch. This type will therefore have to synthesize replicas of all of the operations that could be performed on any of those types. Even "operator-dot" gymnastics don't permit that.

Plus, `A::fun` and `B::fun` are allowed to return different things. Even if they're both virtual functions inherited from the same base class, the derived class versions can differ in terms of return values. So what does `dyn->fun()` return? It would have to return a `variant` of the possible return values. Which makes it hard to use those return values.

This is far more complex than you give it credit for being. C# is a much more malleable language that C++.

What about making the scope that the runtime_cast is compiled with all the possible types. The candidates are all there. The variations in the return value does not make any problem than. One could think about it similar to a generic lambda - beeing compiled statically for all types it is used with.
 

D. B.

unread,
Oct 16, 2016, 2:20:01 PM10/16/16
to std-pr...@isocpp.org
It sounds like you basically want a way to automatically implement the old pattern of 'get typeinfo then do a huge switch statement'. This, while not seeming unreasonable initially, usually indicates larger design issues, which can typically be done better than with RTTI and a huge switch statement anyway.

David Feurle

unread,
Oct 16, 2016, 2:37:00 PM10/16/16
to ISO C++ Standard - Future Proposals
A switch statement on the type is always a bad design - agreed. But using a visitor is not. In fact it is one of the most used / important patterns in C++. The runtime cast saves the user of the C++ statement to implement the visitor him/herselve and adds it as a language feature. Shorter and more precise. This of course is only an Opinion

Giovanni Piero Deretta

unread,
Oct 16, 2016, 5:04:23 PM10/16/16
to ISO C++ Standard - Future Proposals
On Sunday, October 16, 2016 at 7:03:14 PM UTC+1, Nicol Bolas wrote:
[...]
C++ is a statically typed language. What you're talking about requires dynamic dispatching of a kind that C++ as a language is simply not capable of. Not without adding a bunch of hacks to the language.


C++ has dynamic dispatching, but it is restricted only on the first parameter of virtual function calls. There was a paper sometimes ago Stroustrup about a possible efficient implementation of multiple dispatch for C++ [1], but there isn't really enough support to get it into the language.

C# dynamic is a great feature, but to perform decently needs a powerful runtime/JIT. I do not think it would work well in C++. Multimethods would be nice though.

[1]  Open Multimethods in C++.

-- gpd
Reply all
Reply to author
Forward
0 new messages