source: doc/proposals/virtual.txt @ da81e1d0

ADTaaron-thesisarm-ehast-experimentalcleanup-dtorsdeferred_resndemanglerenumforall-pointer-decayjacob/cs343-translationjenkins-sandboxnew-astnew-ast-unique-exprnew-envno_listpersistent-indexerpthread-emulationqualifiedEnumresolv-newwith_gc
Last change on this file since da81e1d0 was da81e1d0, checked in by Aaron Moss <a3moss@…>, 7 years ago

Added notes on vtable binding and bikeshedding to virtual proposal

  • Property mode set to 100644
File size: 5.7 KB
RevLine 
[63c2bca]1Proposal for virtual functionality
2
3Imagine the following code :
4
5trait drawable(otype T) {
6      void draw(T* );
7};
8
9struct text {
10      char* text;
11};
12
13void draw(text*);
14
15struct line{
16      vec2 start;
17      vec2 end;
18};
19
20void draw(line*);
21
22While all the members of this simple UI support drawing creating a UI that easily
23supports both these UI requires some tedious boiler-plate code :
24
25enum type_t { text, line };
26
27struct widget {
28      type_t type;
29      union {
30            text t;
31            line l;
32      };
33};
34
35void draw(widget* w) {
36      switch(w->type) {
37            case text : draw(&w->text); break;
[5a0735ac]38            case line : draw(&w->line); break;
[63c2bca]39            default : handle_error(); break;
40      }
41}
42
43While this code will work as indented, adding any new widgets or any new widget behaviors
44requires changing existing code to add the desired functionality. To ease this maintenance
45effort required CFA introduces the concept of dynamic types, in a manner similar to C++.
46
47A simple usage of dynamic type with the previous example would look like :
48
49drawable* objects[10];
50fill_objects(objects);
51
52while(running) {
53      for(drawable* object : objects) {
54            draw(object);
55      }
56}
57
58However, this is not currently do-able in the current CFA and furthermore is not
59possible to implement statically. Therefore we need to add a new feature to handle
60having dynamic types like this (That is types that are found dynamically not types
61that change dynamically).
62
63C++ uses inheritance and virtual functions to find the
64desired type dynamically. CFA takes inspiration from this solution.
65
66What we really want to do is express the fact that calling draw() on a object
67should find the dynamic type of the parameter before calling the routine, much like the
68hand written example given above. We can express this by adding the virtual keyword on
69the parameter of the constraints on our trait:
70
71trait drawable(otype T) {
72      void draw(virtual T* );
73};
74
75This expresses the idea that drawable is similar to an abstract base class in C++ and
76also gives meaning to trying to take a pointer of drawable. That is anything that can
77be cast to a drawable pointer has the necessary information to call the draw routine on
78that type. Before that drawable was only a abstract type while now it also points to a
79piece of storage which specify which behavior the object will have at run time.
80
81This storage needs to be allocate somewhere. C++ just adds an invisible pointer at
82the beginning of the struct but we can do something more explicit for users, actually
83have a visible special field :
84
85struct text {
86      char* text;
87      vtable drawable;
88};
89
90struct line{
91      vtable drawable;
92      vec2 start;
93      vec2 end;
94};
95
96With these semantics, adding a "vtable drawable" means that text pointers and line pointers are now
97convertible to drawable pointers. This conversion will not necessarily be a type only change however, indeed,
98the drawable pointer will point to the field "vtable drawable" not the head of the struct. However, since all
99the types are known at compile time, converting pointers becomes a simple offset operations.
100
101The vtable field contains a pointer to a vtable which contains all the information needed for the caller
102to find the function pointer of the desired behavior.
103
104One of the limitations of this design is that it does not support double dispatching, which
105concretely means traits cannot have routines with more than one virtual parameter. This design
106would have many ambiguities if it did support multiple virtual parameter.
107
[da81e1d0]108It is worth noting that the function pointers in these vtables are bound at object construction, rather than
109function call-site, as in Cforall's existing polymorphic functions. As such, it is possible that two objects
110with the same static type would have a different vtable (consider what happens if draw(line*) is overridden
111between the definitions of two line objects). Given that the virtual drawable* erases static types though,
112this should not be confusing in practice. A more distressing possibility is that of creating an object that
113outlives the scope of one of the functions in its vtable. This is certainly a possible bug, but it is of a
114type that C programmers are familiar with, and should be able to avoid by the usual methods.
115
[63c2bca]116Extensibility.
117
118One of the obvious critics of this implementation is that it lacks extensibility for classes
119that cannot be modified (ex: Linux C headers). However this solution can be extended to
120allow more extensibility by adding "Fat pointers".
121
122Indeed, users could already "solve" this issue by writing their own fat pointers as such:
123
124trait MyContext(otype T) {
125      void* get_stack(virtual T*)
126};
127
128void* get_stack(ucontext_t *context);
129
130struct fat_ucontext_t {
131      vtable MyContext;
132      ucontext_t *context;
133}
134
135//Tedious forwarding routine
136void* get_stack(fat_ucontext_t *ptr) {
137      return get_stack(ptr->context);
138}
139
140However, users would have to write all the virtual methods they want to override and make
141them all simply forward to the existing method that takes the corresponding POCO(Plain Old C Object).
142
143The alternative we propose is to use language level fat pointers :
144
145trait MyContext(otype T) {
146      void* get_stack(virtual T*)
147};
148
149void* get_stack(ucontext_t *context);
150
151//The type vptr(ucontext_t) all
152vptr(ucontext_t) context;
153
154These behave exactly as the previous example but all the forwarding routines are automatically generated.
[da81e1d0]155
156Bikeshedding.
157
158It may be desirable to add fewer new keywords than discussed in this proposal; it is possible that "virtual"
159could replace both "vtable" and "vptr" above with unambiguous contextual meaning. However, for purposes of
160clarity in the design discussion it is beneficial to keep the keywords for separate concepts distinct.
161
Note: See TracBrowser for help on using the repository browser.