-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathimplementation.xml
executable file
·117 lines (117 loc) · 7 KB
/
implementation.xml
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
<appendix id="implementation">
<title>Implementation</title>
<section>
<title>Code Generation</title>
<para>The development and maintenance of FXRuby would be almost impossible
without the help of Dave Beazley's excellent
<ulink url="http://www.swig.org">SWIG</ulink>. The complete set of SWIG
interface files used to generate FXRuby is included in the standard
FXRuby source code distribution, and if you'd like you can even
regenerate the FXRuby sources using SWIG. Because FXRuby relies on
functionality in a specific version of SWIG (version 1.3.22), you will need
to <ulink url="http://sourceforge.net/project/showfiles.php?group_id=1645&package_id=1608&release_id=265569">download and install SWIG-1.3.22</ulink>.
<emphasis role="strong">Note that more recent versions of SWIG will not
work</emphasis> (this will be corrected in the future).</para>
<para>To regenerate the FXRuby sources from the SWIG interface files,
change directories to the <filename class="directory">swig-interfaces
</filename> subdirectory of the FXRuby source tree and type <command>make
</command>. Any time that you make a change to the SWIG interface files,
you'll need to repeat this step to update the C++ sources.</para>
</section>
<section>
<title>Object Life Cycles and Garbage Collection</title>
<para>One of the more difficult issues to deal with was understanding
the "life cycle" of FOX objects (that is, the actual C++ objects) and
their relationship to the associated Ruby instances. Understanding this
relationship is critical when dealing with Ruby's garbage collector,
to ensure that objects are disposed of at the right time.</para>
<para>For our purposes, we can divide the set of all objects in an FXRuby
program into two groups, those objects that Ruby "owns" and those that
it doesn't. The first group (the "owned" objects) includes those that you
create explicitly from Ruby code. This is usually done by calling a class'
<methodname>new</methodname> method, e.g.</para>
<programlisting format="linespecific">myIcon = FXPNGIcon.new(myApp, File.open("icon.png", "rb").read)
myButton = FXButton.new(parentWin, "Hello, World!", myIcon)</programlisting>
<para>It's important to keep in mind that when you create an object like
this you're not only creating the Ruby instance part (i.e. whatever
overhead is usually associated with a Ruby instance) but a C++ FOX object
as well. Because we created these objects, we would reasonably expect them
to be destroyed when they are garbage-collected so that our programs
don't leak memory.</para>
<para>The other group of objects (those not owned by Ruby) are those
returned from most class instance methods; they are references to already-
existing objects. For example, <methodname>FXStatusBar#statusline
</methodname> returns a reference to the status bar's enclosed status line
instance.</para>
<section>
<title>GL Objects</title>
<para>A C++ <classname>FXGLGroup</classname> object owns all of the <classname>FXGLObject</classname> objects it "contains". In other words, when that <classname>FXGLGroup</classname> object is destroyed, it will also destroy all of the <classname>FXGLObject</classname> objects for which it holds pointers.</para>
<para>In order to keep track of <emphasis>which</emphasis> GL objects have been added to an <classname>FXGLGroup</classname>, all of the FXRuby C++ classes derived from <classname>FXGLObject</classname> have a boolean member variable <structfield>owned</structfield> that indicates whether this object is "owned" or not. Until an <classname>FXGLObject</classname> object is added to a group, this member variable should stay false.</para>
</section>
</section>
<section>
<title>Virtual Functions</title>
<para>
One of the design requirements for FXRuby was to ensure that any
virtual function call made on a FOX object (from the C++ library
layer) is routed to the proper Ruby instance method, even if that
method has been overridden in a derived class.
</para>
<para>
For example, many of the FXRuby example programs are structured with a
main window class that subclasses <classname>FXMainWindow</classname>
and then overrides its <methodname>create</methodname> instance method:</para>
<programlisting format="linespecific">class MyMainWindow < Fox::FXMainWindow
# overrides the base class version, FXMainWindow#create
def create
end
end</programlisting>
<para>This <methodname>create</methodname> method isn't called directly from Ruby code, however; it's called as a side effect of calling <methodname>FXApp#create</methodname>. Inside the C++ FOX library, the <methodname>FXApp::create()</methodname> member function recursively calls <methodname>create()</methodname> on all of the application's windows and eventually calls the virtual <methodname>FXMainWindow::create()</methodname> member function as well:
</para>
<screenshot>
<mediaobject>
<imageobject>
<imagedata fileref="images/call-chain-example.png" format="PNG" align="center"/>
</imageobject>
</mediaobject>
</screenshot>
<para>
To ensure that our main window's overridden <methodname>create
</methodname> method is invoked instead of the base class implementation,
we need some special machinery in place.
</para>
<para>
For every C++ class that declares virtual member functions, we derive
a new C++ class that overrides all of those virtual functions with special
stubs that know how to make method calls on Ruby instances. The naming
convention for these classes is that the "FX" prefix is replaced with
"FXRb"; for example, our <classname>FXRbButton</classname> C++ class is
derived from FOX's <classname>FXButton</classname>:
</para>
<screenshot>
<mediaobject>
<imageobject>
<imagedata fileref="images/inheritance.png" format="PNG" align="center"/>
</imageobject>
</mediaobject>
</screenshot>
<para>
Although the implementation of these "stubs" varies from function to function, the
basic requirements are always the same:
<orderedlist>
<listitem><para>Look up the Ruby object associated with this C++ object.</para></listitem>
<listitem><para>Convert each of the function's arguments to their Ruby
representation. For example, arguments of type <type>FXint</type> are
converted to Ruby <classname>Fixnum</classname>s.</para></listitem>
<listitem><para>Call the Ruby object's method, using the <function>
rb_funcall()</function> function from Ruby's C API, and capture its
result (which is itself a Ruby object).</para></listitem>
<listitem><para>Convert the method call's result back to the virtual
function's C++ return type, and then return that result from the C++
function. For example, if the C++ function has a <type> FXbool</type>
return type, we would expect the corresponding Ruby method to return
either <constant>Qtrue</constant> or <constant>Qfalse</constant>.</para></listitem>
</orderedlist>
</para>
</section>
</appendix>