Static and Dynamic Polymorphism Harmony

79 views
Skip to first unread message

talz...@gmail.com

unread,
Apr 15, 2013, 6:48:04 PM4/15/13
to std-pr...@isocpp.org
There is a big problem in C++ and I want to bring it into awareness and *maybe* propose a solution to it.

The problem:
Today there is a war going on between static polymorphism and dynamic polymorphism.
For example, consider the following piece of code:

void print_str(std::ofstream & os, std::string const& str) {
    os << str;
}

(I'm not good at giving concrete examples)
It simply writes a std::string to an std::ofstream.
Why is it that this operation involves a virtual function call? I'm using concrete types, and I didn't even ask for polymorphism of any kind.
Why is it that I'm not given a choice of whether or not I want dynamic polymorphism, but when using std::string for example I can only use
static polymorphism? Why is it chosen for me by the class writer. Surely, he/she doesn't know what my specific needs are.

C++ is about giving the user as much choice as possible to do what suits him/her.
I feel like the choice has been made for me by someone who doesn't know what suits me in my specific situation.

Let's look at it from a class writer's point of view:
I'm writing a new class and I want some sort of polymorphism. Do I need static or dynamic polymorphism? And for which method/s?
No one can answer this question because it depends on the case.
That is exactly why you should be able to choose when the case arrives.

What I think is the solution to the problem:
In my opinion the solution is to have some sort of std::any class that receives a concept as its 
template parameter and then provides us with type erasure based on that concept.
Much like std::function can provide a single interface to all functor types based on signature, 
std::any<std::Ostream> would be able to provide a single dynamic interface to all std::Ostream models.

So I have a choice between the code example above and this:

template <std::ForwardRange<char> Str>
void print_name(std::any<std::Ostream> & os, Str const& str) {
    os << str;
}

The first code example would use static polymorphism whereas this one would use dynamic polymorphism for os and static for str.
Just as I asked from the compiler.
This way we can get dynamic/static polymorphism when we explicitly ask for it.
By the way, std::any<std::Ostream> would also be a model of std::Ostream which makes this solution very flexible and
makes this a whole lot more exciting in my opinion.

This solution is very similar to a proposed Boost library called Boost.TypeErasure.
It is a very interesting and exciting library in my opinion, but I think that it needs to be able to work with core-language concepts in order
to change the way we write code for the better.
Here's a link to Boost.TypeErasure's docs.

My vision:
C++ will be full of types and concepts. Types would merely be implementations of generic concepts.
C++ would be totally polymorphic, either by static or dynamic polymorphism based on the case at hand.
Concepts would not be limited to templates anymore. They would be the best uncompromising way for creating interfaces.

I'm very excited for the future of C++ and I think this might be it.
Please let me know what you think.

Tal Zion

Nicol Bolas

unread,
Apr 15, 2013, 8:38:04 PM4/15/13
to std-pr...@isocpp.org, talz...@gmail.com
On Monday, April 15, 2013 3:48:04 PM UTC-7, talz...@gmail.com wrote:
There is a big problem in C++ and I want to bring it into awareness and *maybe* propose a solution to it.

The problem:
Today there is a war going on between static polymorphism and dynamic polymorphism.
For example, consider the following piece of code:

void print_str(std::ofstream & os, std::string const& str) {
    os << str;
}

(I'm not good at giving concrete examples)
It simply writes a std::string to an std::ofstream.
Why is it that this operation involves a virtual function call? I'm using concrete types, and I didn't even ask for polymorphism of any kind.

You used a `std::ofstream &`, which is very much a polymorphic type. The compiler does not know that you will not derive from `ofstream` and that a user will not pass such a derived class to your function.

Also, remember that `std::ofstream` forwards all of its stuff to a `streambuf`, which may not be an `filebuf`.
 
Why is it that I'm not given a choice of whether or not I want dynamic polymorphism, but when using std::string for example I can only use
static polymorphism? Why is it chosen for me by the class writer. Surely, he/she doesn't know what my specific needs are.

If you have specific needs that the class writer does not provide... get a new class writer. That's generally how things are done. If Tool X doesn't do what you need, you move on to Tool Y.


What you want would require a combination of a full concepts proposal as well as a full reflection proposal (by "full", I mean integration between reflection and concepts, as well as the ability to define new members and types based on reflection). After all, something is going to have to build these `any` objects with an interface based on a concept. That would have to be done via reflection over the concept and building a type based on that.

Maybe for C++22.

talz...@gmail.com

unread,
Apr 15, 2013, 8:46:45 PM4/15/13
to std-pr...@isocpp.org, talz...@gmail.com
You are correct.
I think that a library implementation of std::any would be best and that would require reflection and concepts.
Reflection and concepts are currently being explored by people much more experienced than me.
Nonetheless I thought this issue would be worth bringing up to help with shaping those features.

This is how I see C++'s future and I think many people would gain from C++ being this way.

minchul park

unread,
Apr 15, 2013, 9:46:29 PM4/15/13
to std-pr...@isocpp.org
I think structural type system is very desirable feature and I believe it's the C++'s way to go. I don't think ostream is good example - component based system in game industry would be practical example that shows its demand.


But there are several difficulties about its implementation. Since compiler doesn't know about every relation to interface-implementation on interface compilation, dispatch table should be lazily constructed (affects performance negatively), or compilation model change might to be required. (Though link time code gen can build necessary dispatch table, dynamic linking should be considered.)

IMO, it's very hard to implement it correctly/efficiently with library approach - it should be tightly integrated into type system. Moreover, since there are some more proposals related to type system (multi-method, type switch ...), I expect some more extra consideration for them.

Below is a good article about implementation of interface in go lang. I hope this helpful to interested people.



--
 
---
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.
Visit this group at http://groups.google.com/a/isocpp.org/group/std-proposals/?hl=en.
 
 



--

talz...@gmail.com

unread,
Apr 15, 2013, 10:07:20 PM4/15/13
to std-pr...@isocpp.org, talz...@gmail.com
On Tuesday, April 16, 2013 3:38:04 AM UTC+3, Nicol Bolas wrote:
On Monday, April 15, 2013 3:48:04 PM UTC-7, talz...@gmail.com wrote:
There is a big problem in C++ and I want to bring it into awareness and *maybe* propose a solution to it.

The problem:
Today there is a war going on between static polymorphism and dynamic polymorphism.
For example, consider the following piece of code:

void print_str(std::ofstream & os, std::string const& str) {
    os << str;
}

(I'm not good at giving concrete examples)
It simply writes a std::string to an std::ofstream.
Why is it that this operation involves a virtual function call? I'm using concrete types, and I didn't even ask for polymorphism of any kind.

You used a `std::ofstream &`, which is very much a polymorphic type. The compiler does not know that you will not derive from `ofstream` and that a user will not pass such a derived class to your function.

Also, remember that `std::ofstream` forwards all of its stuff to a `streambuf`, which may not be an `filebuf`.

I know that std::ofstream& is a polymorphic type and that is the point.
It shouldn't be a polymorphic type on it's own. We received polymorphic behavior from it although we didn't ask for it.
I'm not saying that it should be changed because of deprecation issues, but if everything wasn't virtual and you only got polymorphism through
std::any and templates then the user would have a choice whether or not he/she wants polymorphism and also what kind of polymorphism. 

 
Why is it that I'm not given a choice of whether or not I want dynamic polymorphism, but when using std::string for example I can only use
static polymorphism? Why is it chosen for me by the class writer. Surely, he/she doesn't know what my specific needs are.

If you have specific needs that the class writer does not provide... get a new class writer. That's generally how things are done. If Tool X doesn't do what you need, you move on to Tool Y.

What I mean is that tool X shouldn't contain whether it uses virtual methods or not.
Tool X does what it does. If you want to refer to a generic tool that goes by the same concept, you are free to choose if you want static or dynamic polymorphism.
You decide it when you use it.

For example, if iostream wasn't polymorphic by default, with this proposal you would be able to use it in both dynamic and static polymorphic ways.
With this proposal, if you want a dynamically polymorphic std::string you don't have to write a whole new std::string class.
You can just use the current one with an std::any.
Why is std::string not a polymorphic type and std::ofstream is? std::string is just an implementation of a general purpose string, not all string implementations.
Reply all
Reply to author
Forward
0 new messages