source: doc/proposals/virtual.txt @ 5a0735ac

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 5a0735ac was 5a0735ac, checked in by Aaron Moss <a3moss@…>, 7 years ago

Fixed typo in virtual proposal

  • Property mode set to 100644
File size: 4.6 KB
Line 
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;
38            case line : draw(&w->line); break;
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
108Extensibility.
109
110One of the obvious critics of this implementation is that it lacks extensibility for classes
111that cannot be modified (ex: Linux C headers). However this solution can be extended to
112allow more extensibility by adding "Fat pointers".
113
114Indeed, users could already "solve" this issue by writing their own fat pointers as such:
115
116trait MyContext(otype T) {
117      void* get_stack(virtual T*)
118};
119
120void* get_stack(ucontext_t *context);
121
122struct fat_ucontext_t {
123      vtable MyContext;
124      ucontext_t *context;
125}
126
127//Tedious forwarding routine
128void* get_stack(fat_ucontext_t *ptr) {
129      return get_stack(ptr->context);
130}
131
132However, users would have to write all the virtual methods they want to override and make
133them all simply forward to the existing method that takes the corresponding POCO(Plain Old C Object).
134
135The alternative we propose is to use language level fat pointers :
136
137trait MyContext(otype T) {
138      void* get_stack(virtual T*)
139};
140
141void* get_stack(ucontext_t *context);
142
143//The type vptr(ucontext_t) all
144vptr(ucontext_t) context;
145
146These behave exactly as the previous example but all the forwarding routines are automatically generated.
Note: See TracBrowser for help on using the repository browser.