-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathD1199R0.html
139 lines (129 loc) · 9.91 KB
/
D1199R0.html
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
<!DOCTYPE html>
<html lang="en"><head>
<meta http-equiv="content-type" content="text/html; charset=utf-8">
<meta http-equiv="content-type" content="text/html; charset=utf-8">
<link href="http://alexgorbatchev.com/pub/sh/current/styles/shThemeDefault.css" rel="stylesheet" type="text/css" />
<script src="https://cdn.rawgit.com/google/code-prettify/master/loader/run_prettify.js"></script>
<script src="http://alexgorbatchev.com/pub/sh/current/scripts/shCore.js" type="text/javascript"></script>
<!--<script src="http://alexgorbatchev.com/pub/sh/current/scripts/shAutoloader.js" type="text/javascript"></script>-->
<script src='http://alexgorbatchev.com/pub/sh/current/scripts/shBrushCpp.js' type='text/javascript'></script>
<style>
.syntaxhighlighter div.toolbar span a.toolbar_item{
display: none !important;
}
body .syntaxhighlighter .line {
white-space: pre !important;
}
</style>
<title>A simple proposal for unifying generic and object-oriented programming</title>
</head>
<body>
<p>D1069R0<br>
Mike Spertus, Symantec<br>
<a href="mailto:[email protected]">[email protected]</a><br>
2018-05-20<br>
Audience: Evolution Working Group
</p>
<h1>A simple proposal for unifying generic and object-oriented programming</h1>
<p>In this paper, we propose a simple extension to <a href="http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2018/p1141r0.html">P1141R0</a>
that we believe provides a simple and comprehensive proposal for unifying generic and object-oriented programming.</p>
<h2>The problem</h2>
C++ works best when compile-time and run-time programming are similar. For example, <tt>constexpr</tt> functions allow programs to easily move calculations back and forth between compile-time and run-time as circumstances dictate with no change in code. Likewise, it is well-known that generic programming and object-oriented programming can be applied to similar problems (e.g., Bjarne Stroustrup's <a href="http://www.cs.ox.ac.uk/ralf.hinze/WG2.8/28/slides/bjarne.pdf">Concepts for C++1y: The Challenge</a>), but they do so with very different notations.
Once the design decision is made about whether to use templates
or virtual functions, it is extremely committal and cannot easily be changed.</p>
<p>For example, in designing a drawing library, one would need to decide whether to provide distinct shapes
via a <tt>Shape</tt> base class providing runtime dispatch with virtual
functions or a <tt>Shape</tt> concept providing compile-time dispatch.
If <tt>Shape</tt> is a class, a function might be declared as</p>
<blockquote><pre class="brush:cpp">bool is_convex(Shape const &s);</pre></blockquote>
However, it <tt>Shape</tt> is a concept, a very different declaration must be used:
<blockquote><pre class="brush:cpp">template<typename T> requires Shape<T> bool is_convex(T const &s);</pre></blockquote>
Once this choice is made, the programmer is fully committed to one of two very different programming
paradigms with complex tradeoffs from day one.
<p>Terse notation, as proposed most recently in <a href="http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2018/p1141r0.html">P1141R0</a>, reduces the notational gap between object-oriented and template programming:
<blockquote><pre class="brush:cpp">bool is_convex(Shape auto const &s);</pre></blockquote>
However, switching <tt>Shape</tt> between a class and a concept still requires changing the
declaration of <tt>is_convex</tt>, and even if the declaration were the same (as in the
<a href="http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2015/n4377.pdf">Concepts TS</a>),
many changes would undoubtedly need to be made to the function body to accommodate changing
<tt>Shape</tt> from a class to a concept. E.g., <blockquote><pre class="brush:cpp">// Body is ok if Shape is a class but badly broken if Shape is a concept
bool is_convex(Shape const &s)
{
pair<Shape, int> p(s, 7);
auto x = make_unique<Shape>(convex_hull(s));
/* ... */
}</pre></blockquote>
In the end, the obvious similarity
<h2>Discussion</h2>
<p>It is well-known that generic programming and object-oriented programming can be applied to similar problems (See, for example, the discussion of containers in Bjarne Stroustrup's <a href="http://www.cs.ox.ac.uk/ralf.hinze/WG2.8/28/slides/bjarne.pdf">Concepts for C++1y: The Challenge</a>). While both object-oriented programming and generic programming are incredibly powerful at type-based dispatching, deciding which one to use can be difficult and is extremely
committal once made. To us, the appeal of terse notation is that it has the potential to replace this often premature choice between two very different and poorly-integrated programming models for type-based dispatch with a well-integrated approach that allows the tradeoff between
generic programming and object-oriented programming to be flexibly tuned and evolved over time as circumstances dictate.</p>
Let us consider the following declaration:
<blockquote><pre class="brush:cpp">bool is_convex(Shape const &s) { /* ... */ }</pre></blockquote>
In C++17, this object-oriented code will use virtual methods in the <tt>Shape</tt> class to determine whether a shape is <a href="https://en.wikipedia.org/wiki/Convex_set">convex</a>, but in the language of the <a href="http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2017/n4674.pdf">Concepts TS</a>, this might equally well declare a function template
performing compile-time dispatch between types satisfying the <tt>Shape</tt> concept. This suggests that it might
be possible to tune/evolve a program between object-oriented dispatch and maintaining the flexibility to <tt>Shape</tt> to a Concept. If such a unification could be achieved, we believe it would be fantastically powerful, but of course the devil is in the details, so let us look more closely.
<h3>Challenges</h3>
One obvious challenge is that changing <tt>Shape</tt> from a
concept to a class is very likely to break the function body for <tt>is_convex</tt> because, even though usage of classes and concepts are in some ways similar, there are many difference in their precise usage, both major and subtle, as illustrated below:
<blockquote><pre class="brush:cpp">// The following is ok if Shape is a class but has numerous problems if Shape is a concept
bool is_convex(Shape const &s)
{
pair<Shape, int> p(s, 7);
auto x = make_unique<optional<Shape>>(convex_hull(s));
/* ... */
}
</pre></blockquote>
Likewise, even though the behavior of functions and function templates are also in some ways similar, their interpretation can change depending on
whether <tt>Shape</tt> is a class or a concept:
<blockquote><pre class="brush:cpp">// Pass by rvalue ref if Shape is a class, forwarding ref is Shape is a concept
bool smooth(Shape &&s);
auto f = &smooth; // OK if Shape is a class, but ill-formed if it is a concept
</pre></blockquote>
<h2>Proposal</h2>
So, how do we enable flexible code in the face of such distinctions without introducing breaking changes? Fortunately,
the recent approach of P1141R0 gives us a sigil that can be used when we desire to say that type inference could take
place.
<blockquote><pre class="brush:cpp">// is_convex is a function template
bool is_convex(Shape auto const &s) { /* ... */ }</pre></blockquote>
P1141R0 defines what this means if <tt>Shape</tt> is a concept. To retain
flexibility as to whether <tt>Shape</tt> is a class or a concept, we merely
need to provide a concepts-based interpretation of the above when <tt>Shape</tt>
is a class, which is of course the concept that the type is simply <tt>Shape</tt>.
In other words, if <tt>Shape</tt> is a class, the above declaration should be equivalent
to
<blockquote><pre class="brush:cpp">template<typename T> concept is_Shape = Same<Shape, T>;
bool is_convex(is_Shape auto const &s);</pre></blockquote>
To further ensure that it is
easy to change <tt>Shape</tt> back and forth between being a class and a concept, occurrences of <tt>Shape</tt> within the function body of <tt>is_convex</tt>
should also be replaced with <tt>is_Shape</tt>, ensuring that only code that is compatible with concepts is accepted. The unsung
hero in making this really work well is that, as pointed out in P1168R0, constrained function template argument deduction
and class template argument deduction greatly facilitate safely and expressively writing function bodies. While P1168R0
focuses on how this helps concept code in general, it also makes it easier to write code for classes that wish to maintain
the flexibility to change to concepts in the future.
<blockquote><pre class="brush:cpp">// The following is ok if Shape is a class but has numerous problems if Shape is a concept
bool is_convex(Shape const &s)
{
pair p(s, 7);
auto x = make_unique<optional>(convex_hull(s)); // See P1069R0
/* ... */
}
</pre></blockquote>
Summing up the above, our proposal is simply
<ul><li>In a declaration using the <tt>auto</tt>-qualified
class <tt>C</tt>, the concept <tt>Same<C, _ ></tt> representing type
equality with the class <tt>C</tt>.</li>
<li>If a function template has a <tt>C auto</tt> in its parameter list, occurrences of <tt>C</tt> within the function body are
replaced by the concept for type equality with <tt>C</tt></li>
</ul>
Of course, code working with classes in the normal way independent of concepts is
unaffected, avoiding any breaking changes to existing programs and continuing to provide the full
functionality expected from classes when flexibility is not desired, but now, writing code that flexibly works with both classes and concepts is as
easy as writing concepts code alone, providing a simple unification between the two major programming styles in C++, without the need to
get mired in the myriad subtle differences between generic
and “normal” programming.
<script type="text/javascript">
SyntaxHighlighter.defaults['gutter'] = false;
SyntaxHighlighter.all()
</script>
</body></html>