root/trunk/docs/derelictify.html

Revision 251, 10.5 kB (checked in by aldacron, 2 years ago)

[Docs]
* build.html and derelictify.html were out of date

  • Property svn:mime-type set to text/html
  • Property svn:mim-type set to text/html
Line 
1 <html lang="en">
2 <head>
3     <title>Aldacron's Guide to Derelictification</title>
4     <link rel="stylesheet" type="text/css" href="styles.css">
5 </head>
6 <body>
7 <hr>
8 <hr>
9 <h2 align="center">Aldacron's Guide to Derelictification</h2>
10 <hr>
11 <hr>
12
13 <h3>Introduction</h3>
14 Because Derelict is open source, the project is open to contributions from
15 users. You are also free to modify your copy of the source under the terms
16 of the BSD-style license agreement you'll find at the top of every Derelict
17 source module. Still, even though contributions are welcome, not all are accepted.
18 <p>
19 Bugfixes and enhancements to existing code or documentation will almost always
20 be folded in. New packages, however, often will not be. If we were to accept
21 any and all packages submitted, Derelict would quickly become bloated, unwieldy,
22 and difficult to maintain. New packages will most definitely be added over time,
23 but they must meet some loose criteria, as explained on the
24 <a href="index_a.html#criteria">front page</a>.
25 </p><p>
26 Whether you are making a package for inclusion in the trunk, distributing it
27 separately, or using it privately, there are a few guidelines that should be
28 followed when creating the package.
29 </p>
30
31 <h3>The Bindings</h3>
32 At a very minimum, the bindings you create must be designed to allow manual loading
33 of a shared library. If the library to which you are binding cannot be compiled
34 in shared library form, then the binding cannot be Derelictified. Creating a
35 shared library binding involves several steps, which this document attempts to
36 clarify.
37
38 <h4>typedefs, constants and #defined values</h4>
39 C typedefs can translate to either alias or typedef statements. Derelict
40 modules should use the alias form. Constants should be declared in anonymous
41 enums. #defined values, depending on the intention, should either
42 be declared as aliases or anonymous enums.
43
44 <div class="note">Derelict packages originally declared constants as publicly
45 available constant values. In order to help reduce the size of the resultant
46 binaries, a process was begun to convert all such values to anonymous enums.
47 Currently, the conversion is not complete so you will see examples of both usages
48 in the packages. New packages should all use the enum form. Example:
49
50 <pre>
51 enum
52 {
53     CONSTANT_ONE    = 10,
54     CONSTANT_TWO    = 22,
55     CONSTANT_THREE  = 25,
56 }
57 </pre>
58 </div>
59
60 <p>
61 Where to put these declarations depends entirely upon the size of the library
62 being bound. Using DerelictSDL as an example, you can look at the source to see
63 that all types are declared in context-specific modules along with function
64 declarations and converted macros. Because of the number of headers in SDL, this
65 approach makes sense. DerelictAL (and most of the other Derelict packages), on
66 the other hand, declares all types in a special types module, while functions
67 are declared separately. Generally, this is the approach you should take when
68 the size of the binding is not so large to make this approach difficult to
69 maintain.
70 </p>
71
72 <h4>Macros</h4>
73 The D Programming Language has no means of implementing macros, so C macros
74 must be converted to a format D understands. This means the macros should be
75 converted to D functions. The functions should be declared and implemented
76 publicly at the global module scope. They should not be declared as part of
77 a class or struct definition. And while it may be tempting to implement
78 some macros as templates or mixins, <span class="important">don't</span>. The
79 goal is to stay as close to the C version as possible.
80
81 <h4>Function Declarations</h4>
82 All Derelict packages must provide function pointer declarations. When a shared
83 library is manually loaded, the function pointers will be initialized to point
84 to the proper memory locations in the shared library's address space. The
85 declarations of the function pointers should be typedefed and should have the
86 following syntax:
87 </p>
88
89 <span class="bold">return_type function(param_list) pfFunctionName</span>
90
91 <p>
92 In the above, <span class="bold">pf</span> is prepended to the C function name
93 without changing the case of the name. For example, the function name
94 <span class="bold">glColor</span> would become <span class="bold">pfglColor</span>.
95 More examples: </p>
96
97 <pre>
98 typedef void function(int) pfMyFunction;            // typedefed pointer to C function named MyFunction
99 typedef int function() pfMyOtherFunction;           // typedefed pointer to C function named MyOtherFunction
100 typedef int function(float, char*, void*) pfyetAnotherMyFunction; // typedefed pointer to C function named yetAnotherMyFunction.
101 </pre>
102
103 <p>
104 Following the function declarations should be global variables of the type
105 <span class="bold">pfFunctionName</span> (for each function), and named
106 <span class="bold">FunctionName</span>. Expanding the above example:
107 </p>
108
109 <pre>
110 typedef void function(int) pfMyFunction;
111 typedef int function() pfMyOtherFunction;
112 typedef int function(float, char*, void*) pfyetAnotherMyFunction;
113 pfMyFunction            MyFunction;                 // pointer to C function named MyFunction
114 pfMyOtherFunction       MyOtherFunction;            // pointer to C function named MyOtherFunction
115 pfyetAnotherMyFunction  yetAnotherMyFunction;       // pointer to C function named yetAnotherMyFunction.
116 </pre>
117
118 <p>
119 Finally, you should keep related function delcarations and their associated
120 variable declarations grouped according to purpose. That is, rather than
121 making a long list of function declarations followed by a long list of variable
122 declarations, break them up. This really helps to ease maintenance. One or two
123 of the Derelict packages currently do not do this, but those will be restructured
124 in the future. Expanding on the above examples:
125 </p>
126
127 <pre>
128 typedef void function(int) pfMyFunction;
129 typedef int function() prMyOtherFunction;
130 typedef int function(float, char*, void*) pfyetAnotherMyFunction;
131 pfMyFunction            MyFunction;
132 pfMyOtherFunction       MyOtherFunction;
133 pfyetAnotherMyFunction  yetAnotherMyFunction;
134
135 typedef float function() pfFooFunction;
136 typedef double function(int) pfAnotherFooFunction;
137 pfFooFunction           FooFunction;
138 pfAnotherFooFunction    AnotherFooFunction;
139 </pre>
140
141 <h4>Loading the Shared Library</h4>
142 The last step is to implement the actual loading of the shared library. This
143 involves binding the function pointers to the function names and aliasing
144 a GenericLoader template. Both require templates that are defined in
145 derelict.util.loader, so you must import that module at the top of your
146 module:
147
148 <pre>
149 private import derelict.util.loader;
150 </pre>
151
152 <p>
153 The binding of a function is done via the <span class="bold">bindFunc</span>
154 template. First, you must implement a function that will handle all of the
155 loading. This function should be private to your module. The function should
156 accept a <span class="bold">SharedLib</span> as the only argument.
157 </p>
158
159 <pre>
160 private void loadFoo(SharedLib lib)
161 {
162
163 }
164 </pre>
165
166 <p>
167 If you are unfamiliar with the mechanics of D templates, the call syntax of
168 <span class="bold">bindFunc</span> may be quite strange to you. In this case,
169 there are two sets of parantheses. In the first set of parentheses, you need to
170 pass the function pointer to which you are binding the function. The second set of
171 parentheses is where, in this particluar case, you provide the arguments to an
172 <span class="bold">opCall</span> implementation internal to
173 <span class="bold">bindFunc</span>. Here, you should pass the name of the
174 function as a string and the <span class="bold">SharedLib</span> reference
175 passed to your load function. It's easier to understand seeing it in action than
176 reading a description of it. The following example expands on the previous
177 examples:
178 </p>
179
180 <pre>
181 private import derelict.util.loader;
182
183 typedef void function(int) pfMyFunction;
184 typedef int function() prMyOtherFunction;
185 typedef int function(float, char*, void*) pfyetAnotherMyFunction;
186 pfMyFunction            MyFunction;
187 pfMyOtherFunction       MyOtherFunction;
188 pfyetAnotherMyFunction  yetAnotherMyFunction;
189
190 typedef float function() pfFooFunction;
191 typedef double function(int) pfAnotherFooFunction;
192 pfFooFunction           FooFunction;
193 pfAnotherFooFunction    AnotherFooFunction;
194
195 private void loadFoo(SharedLib lib)
196 {
197     bindFunc(MyFunction)("MyFunction", lib);
198     bindFunc(MyOtherFunction)("MyOtherFunction", lib);
199     bindFunc(yetAnotherFunction)("yetAnotherFunction", lib);
200     bindFunc(FooFunction)("FooFunction", lib);
201     bindFunc(AnotherFooFunction)("AnotherFooFunction", lib);
202 }
203 </pre>
204
205 <p>
206 The last thing to do is to instantiate an instance of the GenericLoader struct and
207 configure it to load your library by calling its <span class="bold">setup</span>
208 method:
209
210 <p class="bold">GenericLoader.setup(char[] windowsNames, char[] linuxName,
211 char[] macNames, void function(SharedLib) loadFunc)</p>
212
213 <p>Each of the name parameters are either a single library name or a comma-separated
214 list of library names, that the loader will attempt to find on the user's system
215 when the <span class="bold">load</span> method is called with no arguments. When
216 multiple names are supplied, they will be loaded in the order given. As an example,
217 here is the implementation for DerelictAL:
218 </p>
219 <pre>
220 GenericLoader DerelictAL;
221 static this()
222 {
223     DerelictAL.setup(
224         "OpenAL32.dll",
225         "libal.so, libAL.so, libopenal.so, libopenal.so.0",
226         "",
227         &loadAL
228     );
229 }
230 </pre>
231
232 <p>So continuing the foo example, assuming there is a foo.dll on Windows and
233 a libFoo.so on Linux:
234
235 <pre>
236 GenericLoader DerelictFoo
237 static this()
238 {
239     DerelictFoo.setup(  "foo.dll",      // the Windows shared library name(s)
240                         "libFoo.so",    // the Linux shared library name(s)
241                         "",             // the Mac shared library name(s)
242                         loadFoo         // the load function where your bindFunc calls are
243     );
244 }
245 </pre>
246
247 <p>
248 The use of multiple shared library names is important on Linux. There are many
249 possible names for the same shared library, so it is best to provide as many of
250 them as you know of to the loader. The loader will attempt to load the shared
251 library using each of the names given until it is successful and will not throw
252 an exception until the last name is attempted and fails.
253
254
255 <h4>Use the Source, Luke</h4>
256 If you find this documentation confusing (I do, and I wrote it), then the source
257 is your best recourse. It's fairly self-explanatory. I recommend that you use
258 DerelictAL and DerelictSDL as examples, for consistency.
259 </body>
260 </html>
Note: See TracBrowser for help on using the browser.