Sign in Join
Wiki copytemporary

Creating bindings for C libraries: what changed

Revision 1 to 2. Removed lines have a minus sign; added lines have a plus sign.

OldNewLine
11# Introduction
22
33This page describes how to create **Pascal bindings** for **C libraries**. In order to use C libraries in Pascal, you have to create a pascal translation for every C function, type and variable. To do that one can either utilized an automated tool, like h2pas or do a manual headers conversion
44
55# H2PAS
66
77## Overview
88
99The [[H2Pas]] tool that automatically translates many common C things. A GUI for Lazarus using h2pas and other tools additionally automates the creation. It helps to create a rule set, which can be used to update the bindings. So the next version of the c library can be converted far more easier. A nice feature of the h2pas wizard is that it uses temporary files, so the c header files remain unchanged.
1010
1111## Work flow
1212
1313- Fetch the C header files you want to translate.
1414- Create a working directory and give your bindings a name
1515- Create a new h2pas project with the h2pas wizard.
1616- Add the .h files to the project.
1717- Setup the h2pas options.
1818- Run the wizard.
1919- Fix errors by adding text tools and run the wizard again.
2020- When h2pas runs without errors, test compilation and add optional tools to beautify the output.
2121- Write some test programs to test your bindings.
2222- Publish your bindings on lazarus-ccr or the FreePascal website.
2323
2424## Install tools
2525
2626The h2pas tool comes with every normal fpc installation.
2727
2828Install the h2paswizard package in the Lazarus IDE. Go to "Package-\>Install/Uninstall Packages", select from the right list the H2PasWizard package and click 'Install selection', then 'Save and rebuild IDE'. Restart the IDE and you get a new menu entry: Tools -\> h2pas
2929
3030## Fetch C header files
3131
3232The C header files .h describes the interface of a C library. They are normally not provided with the library. You have to get the sources or the development package of the library. For example the C header files of the gtk libraries are in the package gtk+-devel.
3333
3434### Example: MPICH2
3535
3636Download the mpich2-1.0.3.tar.gz from <http://www-unix.mcs.anl.gov/mpi/mpich2/> and unpack them. The .h files are under mpich2-1.0.3/src/include.
3737
3838You can find more information about MPICH for FPC [[MPICH|here]].
3939
4040## Create a working directory and name your bindings
4141
4242Create a directory with a useful name. A name should not contain special characters, like spaces, umlaute, points or commas. Copy the .h files.
4343
4444### Example: MPICH2
4545
4646The h2p directory will be used for the pascal files. The h2p/c_sources directory will be used for the .h files.
4747
4848``` bash
4949mkdir -p h2p/c_sources
5050cp mpich2-1.0.3/src/include/*.h h2p/c_sources/
5151```
5252
5353## Create a new h2pas project with the h2pas wizard
5454
5555Open the h2pas wizard: "Tools -\> h2pas". This will open a window. You can switch between this window and the other IDE windows. Your last h2pas project will be loaded automatically. To create a new project click on "Settings -\> New/Clear settings". Then click on the bottom button "Save settings" and choose a filename.
5656
5757Note: If this is the first time you have used the h2pas wizard, you need to use the wizard's "Settings" tab to tell the wizard where to find the h2pas.exe converter program. The h2pas.exe converter lives in the same directory as the FreePascal compiler fpc.exe - if you downloaded FPC as part of Lazarus it will be in ...\Lazarus\fpc\*version*\bin. Then save the settings so you don't have to do this again.
5858
5959### Example: MPICH2
6060
6161Click on "Settings -\> New/Clear settings". Then click on the bottom button "Save settings" and save it as h2p/mpi2.h2p.
6262
6363## Add the .h files to the h2pas-project
6464
6565In the "C header files" page you can add/delete the .h files. You can enable/disable .h files to convert only part of the files.
6666
6767### Example: MPICH2
6868
6969Click on "C header files -\> Add .h files ..." and select "mpi.h" and "mpio.h". They will be enabled automatically. Select the mpi.h file and click "Merge all but this", so that the wizard combines all header files into one unit (mpi.pas).
7070
7171## Setup the h2pas options
7272
7373Under "h2pas Options" you can set the parameters for the h2pas program.
7474
7575### Example: MPICH2
7676
7777- Enable -e, -D, -p, -w and disable all others.
7878- The -l library path is "mpich".
7979- Output extension is ".pas"
8080- Output directory is h2pas/, which is the default, so leave it empty
8181
8282## Run the wizard
8383
8484Click on the bottom button "Run h2pas". This will copy the \<example\>.h file to a temporary \<example\>.tmp.h file and run the tools listed under "Before h2pas". Then it runs h2pas to convert the \<example\>.tmp.h into \<example\>.inc or \<example\>.pas or whatever output extension you setup on the h2pas page. Then it runs the tools listed under "After h2pas" on the output files.
8585
8686If h2pas finds a syntax error, the IDE will open the example.tmp.h file and jumps to the error line. h2pas often only reports 'syntax error', which is very unspecific. See [[Common problems when converting C header files]].
8787
8888### Example: MPICH2
8989
9090The h2pas wizard already contains all tools to convert all the specials of this header file, so h2pas runs without errors. But the created unit is not yet ready. Read further.
9191
9292Go to the *After H2Pas* tab and add the *Reduce compiler directives in pascal file* tool.
9393
9494Add the following to the *Undefines* property:
9595
9696` MPI_INCLUDED`\
9797` MPIO_INCLUDE`\
9898` NEEDS_MPI_FINT`\
9999` MPI_BUILD_PROFILING`\
100100` MPICH_SUPPRESS_PROTOTYPES`\
101101` MPI_Wtime`\
102102` HAVE_MPICH2G2`\
103103` FOO`\
104104` HAVE_PRAGMA_HP_SEC_DEF`
105105
106106Add the following to the *Defines* property:
107107
108108` MPICH2`\
109109` MPICH_NEW_MPIIO`
110110
111111## Writing your own converter tools
112112
113113### Using the tool "Search and replace"
114114
115115Many things like renaming a variable can be done by the **Search and replace** tool. Add the tool via the **Add new tool** button on either the 'Before h2pas' or the 'After h2pas' page. Then set the **SearchFor**, **ReplaceWith**, **Options** and **Caption** property.
116116
117117#### Option: trtRegExpr
118118
119119If you set this option to **True**, it means that you want to use [[IDE regular expressions|regular expressions]]. This allows for plenty of possibilities to adapt and/or beautify the source code by **metacharacters** and **quantifiers**. A list of the **metacharacters** and **quantifiers** and examples are found in this article: [[IDE regular expressions]].
120120
121121### Example: rename an identifier Tguint to guint
122122
123123| Property | Value |
124124|-------------|-------------------------------|
125125| Caption | Rename Tguint to guint |
126126| SearchFor | Tguint |
127127| ReplaceWith | guint |
128128| Options | \[trtMatchCase,trtWholeWord\] |
129129
130130### Example: rename several identifiers
131131
132132Rename Tguint to guint, Tgulong to gulong, Tgint to gint:
133133
134-<table>
135-<thead>
136-<tr>
137-<th><p>Property</p></th>
138-<th><p>Value</p></th>
139-</tr>
140-</thead>
141-<tbody>
142-<tr>
143-<td><p>Caption</p></td>
144-<td><p>Rename Tguint to guint</p></td>
145-</tr>
146-<tr>
147-<td><p>SearchFor</p></td>
148-<td><pre><code>T(guint|gint|gulong)</code></pre></td>
149-</tr>
150-<tr>
151-<td><p>ReplaceWith</p></td>
152-<td><p>$1</p></td>
153-</tr>
154-<tr>
155-<td><p>Options</p></td>
156-<td><p>[trtMatchCase,trtWholeWord,trtRegExpr]</p></td>
157-</tr>
158-</tbody>
159-</table>
134+**Property**
160135
136+**Value**
137+
138+Caption
139+
140+Rename Tguint to guint
141+
142+SearchFor
143+
144+ T(guint|gint|gulong)
145+
146+ReplaceWith
147+
148+\$1
149+
150+Options
151+
152+\[trtMatchCase,trtWholeWord,trtRegExpr\]
153+
161154### Example: MPICH2
162155
163156#### Removing Makro MPI_DUP_FN
164157
165158The *MPI_DUP_FN* is defined two times in the mpi C headers. Once as a macro to something that does not exist, and later to a real function. So, the first should be deleted.
166159
167160Add a 'Search and replace' tool to the before H2Pas tools (at the end) with the following settings:
168161
169-<table>
170-<thead>
171-<tr>
172-<th><p>Property</p></th>
173-<th><p>Value</p></th>
174-</tr>
175-</thead>
176-<tbody>
177-<tr>
178-<td><p>Caption</p></td>
179-<td><p>Remove dummy define MPI_DUP_FN</p></td>
180-</tr>
181-<tr>
182-<td><p>Name</p></td>
183-<td><p>RemoveDummyDefineMPI_DUP_FN</p></td>
184-</tr>
185-<tr>
186-<td><p>SearchFor</p></td>
187-<td><pre><code>^#define\s+MPI_DUP_FN\s+MPIR_Dup_fn</code></pre></td>
188-</tr>
189-<tr>
190-<td><p>ReplaceWith</p></td>
191-<td></td>
192-</tr>
193-<tr>
194-<td><p>Options</p></td>
195-<td><p>[trtMatchCase,trtRegExpr]</p></td>
196-</tr>
197-</tbody>
198-</table>
162+**Property**
199163
164+**Value**
165+
166+Caption
167+
168+Remove dummy define MPI_DUP_FN
169+
170+Name
171+
172+RemoveDummyDefineMPI_DUP_FN
173+
174+SearchFor
175+
176+ ^#define\s+MPI_DUP_FN\s+MPIR_Dup_fn
177+
178+ReplaceWith
179+
180+Options
181+
182+\[trtMatchCase,trtRegExpr\]
183+
200184#### Removing typecasts to MPI_DUP_FN
201185
202186Add a 'Search and replace' tool to the before H2Pas tools (at the end) with the following settings:
203187
204-<table>
205-<thead>
206-<tr>
207-<th><p>Property</p></th>
208-<th><p>Value</p></th>
209-</tr>
210-</thead>
211-<tbody>
212-<tr>
213-<td><p>Caption</p></td>
214-<td><p>Remove MPI_DUP_FN Makros</p></td>
215-</tr>
216-<tr>
217-<td><p>Name</p></td>
218-<td><p>Remove_MPI_DUP_Func</p></td>
219-</tr>
220-<tr>
221-<td><p>SearchFor</p></td>
222-<td><pre><code>^#define .*MPI_DUP_FN\).*$</code></pre></td>
223-</tr>
224-<tr>
225-<td><p>ReplaceWith</p></td>
226-<td></td>
227-</tr>
228-<tr>
229-<td><p>Options</p></td>
230-<td><p>[trtMatchCase,trtRegExpr]</p></td>
231-</tr>
232-</tbody>
233-</table>
188+**Property**
234189
190+**Value**
191+
192+Caption
193+
194+Remove MPI_DUP_FN Makros
195+
196+Name
197+
198+Remove_MPI_DUP_Func
199+
200+SearchFor
201+
202+ ^#define .*MPI_DUP_FN\).*$
203+
204+ReplaceWith
205+
206+Options
207+
208+\[trtMatchCase,trtRegExpr\]
209+
235210#### Removing functions MPIO_Test, PMPIO_Test, MPIO_Wait and PMPIO_Wait
236211
237212The MPIO_Test, PMPIO_Test, MPIO_Wait and PMPIO_Wait functions are defined twice and redefined via macro. So, the functions can be removed:
238213
239214Add a 'Search and replace' tool to the before H2Pas tools (at the end) with the following settings:
240215
241-<table>
242-<thead>
243-<tr>
244-<th><p>Property</p></th>
245-<th><p>Value</p></th>
246-</tr>
247-</thead>
248-<tbody>
249-<tr>
250-<td><p>Caption</p></td>
251-<td><pre><code>Remove Func P?MPIO_(Test|Wait)</code></pre></td>
252-</tr>
253-<tr>
254-<td><p>Name</p></td>
255-<td><p>RemoveFuncP_MPIO_Test_Wait</p></td>
256-</tr>
257-<tr>
258-<td><p>SearchFor</p></td>
259-<td><pre><code>^int P?MPIO_(Test|Wait).*$</code></pre></td>
260-</tr>
261-<tr>
262-<td><p>ReplaceWith</p></td>
263-<td></td>
264-</tr>
265-<tr>
266-<td><p>Options</p></td>
267-<td><p>[trtMatchCase,trtRegExpr]</p></td>
268-</tr>
269-</tbody>
270-</table>
216+**Property**
217+
218+**Value**
219+
220+Caption
221+
222+ Remove Func P?MPIO_(Test|Wait)
223+
224+Name
225+
226+RemoveFuncP_MPIO_Test_Wait
227+
228+SearchFor
229+
230+ ^int P?MPIO_(Test|Wait).*$
231+
232+ReplaceWith
233+
234+Options
235+
236+\[trtMatchCase,trtRegExpr\]
271237
272238## Improving an existing tool
273239
274240You found a bug and want to fix it or you want to extend one of the above tools. Great!
275241
276242Most of the above tools are defined in the h2pasconvert unit, which is part of the h2paswizard package. Basically a tool needs a classname, a description and an **Execute** method. To test/debug a tool outside the IDE and save a lot of compilation time, see the project components/simpleideintf/examples/testh2pastool.lpi. Compile it and start it on a console/terminal with the filename of a .h file as first command parameter. For example:
277243
278244``` bash
279245./testh2pastool files/h2pastest.pas
280246```
281247
282248## Writing a custom tool
283249
284250You can write your own conversion tools and register them in the IDE easily. Start a package and a add a new unit (say unit1) for your new class. See the existing tools as example and read the prior section. When you wrote a new tool and tested it with the above simpleideintf project, then register it in the IDE: Add a **register** procedure to your unit (unit1), like the following (pseudo code):
285251
286252``` pascal
287253uses
288254 Classes, ..., IDETextConverter;
289255
290256type
291257 TYourToolClass = class(TCustomTextConverterTool)
292258 public
293259 class function ClassDescription: string; override;
294260 function Execute(aText: TIDETextConverter): TModalResult; override;
295261 end;
296262
297263procedure Register;
298264
299265implementation
300266
301267procedure Register;
302268begin
303269 TextConverterToolClasses.RegisterClass(TYourToolClass);
304270end;
305271```
306272
307273Do not forget to enable the **register** checkbox of the unit in the package editor, otherwise the Register procedure will not be called by the IDE. Then install your package in the IDE.
308274
309275## Future work / Missing stuff
310276
311277- A tool to convert static linked procedures to dynamic linked procedure variables
312278- A tool to fix functions without parameter names by searching the .c files. This could also add a comment, in which c file it found the function.
313279- Find missing identifiers and let the user check which one to comment or replace with base types.
314280- Create a list of macro functions, which were half translated.
315281
316282C:
317283
318284``` c
319285typedef struct page
320286{
321287 u32int present : 1; // Page present in memory
322288 u32int rw : 1; // Read-only if clear, readwrite if set
323289 u32int user : 1; // Supervisor level only if clear
324290 u32int accessed : 1; // Has the page been accessed since last refresh?
325291 u32int dirty : 1; // Has the page been written to since last refresh?
326292 u32int unused : 7; // Amalgamation of unused and reserved bits
327293 u32int frame : 20; // Frame address (shifted right 12 bits)
328294} page_t;
329295```
330296
331297Pascal:
332298
333299``` pascal
334300type
335301 Unsigned_7 = 0 .. (1 shl 7) - 1;
336302 Unsigned_20 = 0 .. (1 shl 20) - 1;
337303
338304type
339305 page_t = bitpacked record
340306 present : boolean;
341307 rw : boolean;
342308 user : boolean;
343309 accessed : boolean;
344310 dirty : boolean;
345311 unused : Unsigned_7;
346312 frame : Unsigned_20;
347313 end;
348314```
349315
350316# Convert C to Pascal with lua script
351317
352318I'm impressed by the power of the lua scripting language and how easy it is to do funny things with it.
353319
354320``` lua
355321--
356322-- Please send any improvement to mingodadATgmailDOTcom.
357323--
358324
359325inFileName = arg[1] or 'g:\\tmp\\plua\\clua\\lvm.c'
360326
361327fh = assert(io.open(inFileName))
362328cbody = fh:read('*a')
363329fh:close()
364330
365331pasbody = cbody
366332
367333function split(str, pat)
368334 local t = {} -- NOTE: use {n = 0} in Lua-5.0
369335 local fpat = '(.-)' .. pat
370336 local last_end = 1
371337 local s, e, cap = str:find(fpat, 1)
372338 while s do
373339 if s ~= 1 or cap ~= '' then
374340 table.insert(t, cap)
375341 end
376342 last_end = e+1
377343 s, e, cap = str:find(fpat, last_end)
378344 end
379345 if last_end <= #str then
380346 cap = str:sub(last_end)
381347 table.insert(t, cap)
382348 end
383349 return t
384350end
385351
386352function trim(s)
387353 -- from PiL2 20.4
388354 return (s:gsub('^%s*(.-)%s*$', '%1'))
389355end
390356
391357function trimChars(str, c1, c2)
392358 return str:gsub('^%' .. c1 .. '*(.-)%' .. c2 .. '*$', '%1')
393359end
394360
395361function getPascalType(cType)
396362 if cType == 'void' then
397363 return 'pointer'
398364 elseif cType == 'int' then
399365 return 'integer'
400366 elseif cType == 'short' then
401367 return 'Shortint'
402368 elseif cType == 'define' then
403369 return 'is_define'
404370 else
405371 return cType
406372 end
407373end
408374
409375--check var name and type
410376--if var starts with a capital letter
411377-- add 'T' informt of it
412378--if var is preceded by * then add a 'P' for type
413379
414380function checkVarType(aType, aVar)
415381 aType = getPascalType(aType)
416382 aType = aType:gsub('^(%u)', 'T%1')
417383 aVar = aVar:gsub('^(%*)', function(m)
418384 aType = 'P' .. aType
419385 return ''
420386 end)
421387 aVar = aVar:gsub('(%[[^]]+%])', function(m)
422388 aType = 'array' .. m .. ' of ' .. aType
423389 return ''
424390 end)
425391 return aType, aVar
426392end
427393
428394function varDeclaration(aType, aVar)
429395 aType, aVar = checkVarType(aType, aVar)
430396 return aVar .. ' : ' .. aType
431397end
432398
433399function checkDeclarationOrGoto(m1, m2, m3)
434400 if m2 ~= 'goto' then
435401 return '\n' .. m1 .. varDeclaration(m2, m3) .. ';'
436402 else
437403 return '\n' .. m1 .. m2 .. ' ' .. m3 .. ';'
438404 end
439405end
440406
441407function addProcFunc(mtype, mname, mparams, mbody)
442408 local str, isFunc, rType, k, v
443409 if mtype == 'void' then
444410 str = '\nprocedure '
445411 isFunc = false
446412 else
447413 str = '\nfunction '
448414 isFunc = true
449415 end
450416 mtype, mname = checkVarType(mtype, mname)
451417 local vparams = split(trimChars(mparams, '(',')'), ',')
452418 str = str .. mname .. '('
453419
454420 local tparams = {}
455421 for k, v in pairs(vparams) do
456422 local vparam = split(trim(v), ' ')
457423 if #vparam > 2 then
458424 table.insert(tparams, vparam[1] .. ' ' .. varDeclaration(vparam[2], vparam[3]))
459425 else
460426 --print(vparam[1], vparam[2])
461427 table.insert(tparams, varDeclaration(vparam[1], vparam[2] or 'is_define'))
462428 end
463429 end
464430
465431 str = str .. table.concat(tparams, '; ') .. ')'
466432 if isFunc then
467433 if mtype == 'int' then
468434 rType = 'integer'
469435 else
470436 rType = mtype
471437 end
472438 str = str .. ':' .. rType
473439 end
474440
475441 if mbody then
476442 local tblLabels = {}
477443 for v in mbody:gmatch('\n[\t ]*([%w_]+)[\t ]*:[^=]') do
478444 if v ~= 'default' then
479445 table.insert(tblLabels, v)
480446 end
481447 end
482448
483449 if #tblLabels > 0 then
484450 str = str .. ';\nlabel\n '.. table.concat(tblLabels, ', ')
485451 end
486452
487453 local declarations = {}
488454
489455 mbody = mbody:gsub('(\n[\t ]*)const[\t ]+([%w_]+)[\t ]+(%*?[%w_]+)[\t ]*:%=[\t ]*([^;\n]+;)','%1 %2 %3 := %4')
490456
491457 mbody = mbody:gsub('(\n[\t ]*)([%w_]+)[\t ]+(%*?[%w_]+)[\t ]*:%=[\t ]*([^;\n]+;)', function(m1, m2, m3, m4)
492458 table.insert(declarations, varDeclaration(m2, m3) .. ';' )
493459 return m1 .. m3 .. ' := ' .. m4
494460 end)
495461
496462 mbody = mbody:gsub('\n[\t ]*const[\t ]*([%w_]+)[\t ]+(%*?[%w_]+)[\t ]*;', function(m1,m2)
497463 table.insert(declarations, varDeclaration(m1, m2) .. ';')
498464 return ''
499465 end)
500466
501467 mbody = mbody:gsub('\n([\t ]*)([%w_]+)[\t ]+(%*?[%w_ ,]+);', function(m1,m2,m3)
502468 if m2 ~= 'goto' then
503469 table.insert(declarations, varDeclaration(m2, m3) .. ';')
504470 return ''
505471 else
506472 return '\n' .. m1 .. m2 .. ' ' .. m3 .. ';'
507473 end
508474 end)
509475
510476 if #declarations > 0 then
511477 local unique_decl = {}
512478 table.sort(declarations)
513479 local lastV = ''
514480 local k2, v2
515481 for k2,v2 in pairs(declarations) do
516482 if v2 ~= lastV then
517483 lastV = v2
518484 table.insert(unique_decl, v2)
519485 end
520486 end
521487 str = str .. ';\nvar\n '.. table.concat(unique_decl, '\n ') .. '\n'
522488 else
523489 str = str .. ';\n'
524490 end
525491
526492 str = str .. 'begin' .. trimChars(mbody, '{','}') .. 'end'
527493 end
528494 return str .. ';'
529495end
530496
531497--*************************************
532498--
533499-- Order of substitutions are important
534500--
535501--*************************************
536502
537503--=assignements statements
538504 pasbody = pasbody:gsub("([\t ]*)([%w_]+)[\t ]*=[\t ]*([^=;\n]+);", '%1%2 := %3;')
539505
540506--return
541507 pasbody = pasbody:gsub('([\t ]*)return[\t ]+([^;]+);', '%1exit(%2);')
542508
543509--NULL
544510 pasbody = pasbody:gsub('([^%w]+)NULL([^%w]+)', '%1nil%2')
545511
546512--void functions to procedure declaratios
547513 pasbody = pasbody:gsub('([%w_*]+)[\t ]+([%w_*]+)[\t ]*(%b())[%s]*(%b{})', addProcFunc)
548514 --pasbody = pasbody:gsub('\n([%w_]+)%s+([^%s]+)%s*(%b());', addProcFunc)
549515
550516--const/static
551517 tblLeaveCommented = {
552518 'const', 'static'
553519 }
554520 for k,v in pairs(tblLeaveCommented) do
555521 pasbody = pasbody:gsub('([\t ]*)(' .. v .. ')([\t ]*)', '%1{%2}%3')
556522 end
557523
558524--defines to const
559525 isConstEmmited = false
560526 function addConst(m1,m2)
561527 local sm
562528 sm = ''
563529 if not isConstEmmited then
564530 isConstEmmited = true
565531 sm = '\nconst'
566532 end
567533 return sm .. '\n\t' .. m1 .. ' = ' .. m2 .. ';'
568534 end
569535
570536 isConstEmmited = false
571537 pasbody = pasbody:gsub('\n#define%s+([^%s]+)%s+(%b())', '\nconst %1 = %2;')
572538 isConstEmmited = false
573539 pasbody = pasbody:gsub('\n#define%s+([^%s]+)[ \t]+([^%s]+)', '\nconst %1 = %2;')
574540
575541--includes
576542 tblUses = {}
577543 pasbody = pasbody:gsub('\n#[ \t]*(include)%s+([^\n]+)', function(m1,m2)
578544 for w in m2:gmatch('"([^.]+)%..+"') do
579545 table.insert(tblUses, w)
580546 end
581547 return '\n//#' .. m1 .. ' ' .. m2
582548 end)
583549 pasbody = '//uses ' .. table.concat(tblUses, ', ') .. ';\n' .. pasbody
584550
585551--defines to compiler directives
586552 pasbody = pasbody:gsub('\n#([^\n]+)', '\n{$%1}')
587553
588554--comments
589555 pasbody = pasbody:gsub('\n#', '\n//#')
590556 pasbody = pasbody:gsub('/%*', '(*')
591557 pasbody = pasbody:gsub('%*/', '*)')
592558
593559--declarations
594560 pasbody = pasbody:gsub('\n([\t ]*)([%w_]+)[\t ]+([%w_ ,]+);', checkDeclarationOrGoto)
595561
596562--structs
597563 function parseStruct(m1, m2)
598564 return '\n' .. m1 .. ' = record\n' .. trimChars(m2, '{', '}') .. '\nend'
599565 end
600566 pasbody = pasbody:gsub('\n[\t ]*struct[\t ]+([%w_]+)[\t ]*(%b{})', parseStruct)
601567
602568--if statements
603569 parseStatementCalled = false
604570
605571 function parseStatement(mSpace, mStatement, mCond, mBody)
606572 parseStatementCalled = true
607573 local vStatement
608574 if mStatement == 'if' then
609575 vStatement = mStatement .. ' ' .. mCond .. ' then'
610576 elseif mStatement == 'else' then
611577 return mSpace .. mStatement .. ' begin' .. trimChars(mCond, '{', '}') .. ' end;'
612578 elseif (mStatement == 'while') then
613579 vStatement = mStatement .. ' ' .. mCond .. ' do'
614580 elseif (mStatement == 'for') then
615581 vStatement = mStatement .. ' ' .. mCond .. ' do'
616582 --local forArgs = split(trimChars(mCond, '(', ')'), ';')
617583 --return mSpace .. (forArgs[1] or '') .. ';' .. mSpace ..'while (' ..
618584 -- (forArgs[2] or 'true') .. ') do' .. mSpace .. 'begin' .. mSpace .. trimChars(mBody, '{', '}') .. '\n' .. mSpace ..
619585 -- (forArgs[3] or '') .. ';\n' .. mSpace .. 'end;'
620586 else
621587 vStatement = mStatement .. ' ' .. mCond .. '\n'
622588 end
623589 return mSpace .. vStatement .. ' begin' .. trimChars(mBody, '{', '}') .. 'end;'
624590 end
625591
626592 -- for nested blocks
627593 parseStatementCalled = true
628594 while(parseStatementCalled) do
629595 parseStatementCalled = false
630596 pasbody = pasbody:gsub('([^%w_])(if)[\t ]*(%b())[\t ]*(%b{})', parseStatement)
631597 end
632598
633599 -- else
634600 pasbody = pasbody:gsub('([\t ]+)(else)[\t ]*(%b{})', parseStatement)
635601
636602 pasbody = pasbody:gsub("([^%w_])if[\t ]*(%b())[\t ]*([^;\n]+);", '%1if %2 then %3;')
637603
638604--?: short if
639605 pasbody = pasbody:gsub("([^?])?([^:;\n]):[\t ]*([^%s;]+);", ' iff(%1, %2, %3) ')
640606
641607--while loop
642608 -- for nested blocks
643609 parseStatementCalled = true
644610 while(parseStatementCalled) do
645611 parseStatementCalled = false
646612 pasbody = pasbody:gsub('([^%w_])(while)[\t ]*(%b())[\t ]*(%b{})', parseStatement)
647613 end
648614
649615--for loop
650616 -- for nested blocks
651617 parseStatementCalled = true
652618 while(parseStatementCalled) do
653619 parseStatementCalled = false
654620 pasbody = pasbody:gsub('([^%w_])(for)[\t ]*(%b())%s*(%b{})', parseStatement)
655621 end
656622
657623--do/while
658624 -- for nested blocks
659625 parseStatementCalled = true
660626 while(parseStatementCalled) do
661627 parseStatementCalled = false
662628 pasbody = pasbody:gsub("([^%w_])do[\t ]*(%b{})[\t ]*while([^;\n]+);",
663629 function(m1, m2, m3)
664630 parseStatementCalled = true
665631 return m1 .. 'repeat ' .. trimChars(m2, '{', '}') .. 'until not' .. m3
666632 end)
667633 end
668634
669635--switch/case statements
670636
671637 --begin/end enclose blocks around
672638 -- for nested blocks
673639 parseStatementCalled = true
674640 while(parseStatementCalled) do
675641 parseStatementCalled = false
676642 pasbody = pasbody:gsub("([^%w_]case[\t ]+[^:]+)[\t ]*:[%s]*(%b{})",
677643 function(m1, m2, m3)
678644 parseStatementCalled = true
679645 return m1 .. ': begin' .. trimChars(m2, '{', '}') .. 'end;'
680646 end)
681647 pasbody = pasbody:gsub("([^%w_]default[\t ]*):[%s]*(%b{})",
682648 function(m1, m2, m3)
683649 parseStatementCalled = true
684650 return m1 .. ': begin' .. trimChars(m2, '{', '}') .. 'end;'
685651 end)
686652 end
687653
688654 --breaks remove
689655 pasbody = pasbody:gsub("([^%w_]case[\t ]+[^:]+)[\t ]*:[%s]*(.-)break;", '%1: %2')
690656
691657 --case
692658 pasbody = pasbody:gsub("([^%w_])case[\t ]+([^:]+):", '%1%2:')
693659
694660 -- for nested blocks
695661 parseStatementCalled = true
696662 while(parseStatementCalled) do
697663 parseStatementCalled = false
698664 pasbody = pasbody:gsub("([^%w_])switch[\t ]*(%b())[\t ]*(%b{})",
699665 function(m1, m2, m3)
700666 parseStatementCalled = true
701667 return m1 .. 'case ' .. m2 .. ' of ' .. trimChars(m3, '{', '}') .. 'end;'
702668 end)
703669 end
704670
705671 --pasbody = pasbody:gsub("([\t ]*)case[\t ]+('.')[\t ]*:\n", '%1%2:\n')
706672 --pasbody = pasbody:gsub("([\t ]*)case[\t ]+(%d+)[\t ]*:\n", '%1%2:\n')
707673 --pasbody = pasbody:gsub("([\t ]*)(case)[\t ]+('.')[\t ]*:[\t ]*(%b{})", '%1%2 : %3')
708674
709675--pre/pos increments
710676 -- (n)++ -> PosInc(n)
711677 pasbody = pasbody:gsub("(%([^%s+]+%))%+%+", 'PosInc%1')
712678 -- n*++ -> PosInc(n*)
713679 pasbody = pasbody:gsub("([^%s*+]+)%+%+", 'PosInc(%1)')
714680 -- ++(n) -> PreInc(n)
715681 pasbody = pasbody:gsub("%+%+(%([^%s;+]+%))", 'PreInc%1')
716682 -- ++*n -> PreInc(n)
717683 pasbody = pasbody:gsub("%+%+([^%s*;+]+)", 'PreInc(%1)')
718684
719685 -- (n)--
720686 pasbody = pasbody:gsub("(%([^%s-]+%))%-%-", 'PosDec%1')
721687 -- n*--
722688 pasbody = pasbody:gsub("([^%s*-]+)%-%-", 'PosDec(%1)')
723689 -- --(n)
724690 pasbody = pasbody:gsub("%-%-(%([^%s;-]+%))", 'PreDec%1')
725691 -- --n
726692 pasbody = pasbody:gsub("%-%-([^%s;-]+)", 'PreDec(%1)')
727693
728694--boolean operators
729695 tblBooleanOperators = {
730696 {'||', 'or'},
731697 {'&&', 'and'},
732698 }
733699 for k,v in pairs(tblBooleanOperators) do
734700 pasbody = pasbody:gsub('([\t ]*)' .. v[1] .. '([\t ]*)', '%1) ' .. v[2] .. ' (%2')
735701 end
736702
737703--pointers
738704 pasbody = pasbody:gsub("%->", '.')
739705 pasbody = pasbody:gsub("%(%*([%w_]+)", '(%1^')
740706 pasbody = pasbody:gsub("%*%*([%w_]+)", '%1^^')
741707 pasbody = pasbody:gsub("&([%w_]+)", '@%1')
742708 pasbody = pasbody:gsub("%*([%w_]+)[\t ]*:[\t ]*([%w_]+);", '%1 : ^%2;')
743709 pasbody = pasbody:gsub("%^char", 'pchar')
744710 --pasbody = pasbody:gsub("%*([%w_]+)[\t ]*:=[\t ]*([^;\n]+);", '%1^ := %2;')
745711
746712--bit operators
747713 tblBitOperators = {
748714 {'<<', 'shl'},
749715 {'>>', 'shr'},
750716 {'|', 'or'},
751717 {'&', 'and'},
752718 {'~', 'not'},
753719 }
754720
755721 for k,v in pairs(tblBitOperators) do
756722 pasbody = pasbody:gsub('([\t ]*)' .. v[1] .. '([\t ]*)', '%1 ' .. v[2] .. ' %2')
757723 end
758724
759725--==eq operator
760726 pasbody = pasbody:gsub("([\t ]*)%=%=([\t ]*)", '%1 = %2')
761727
762728--!= not eq operator
763729 pasbody = pasbody:gsub("([\t ]*)!%=([\t ]*)", '%1 <> %2')
764730
765731--! unary
766732 pasbody = pasbody:gsub('([\t ]*)!([\t ]*)', '%1 not %2')
767733
768734--blocks
769735 --pasbody = pasbody:gsub("(%b{})", function(m1) return 'begin' .. trimChars(m1, '{', '}') .. 'end;' end)
770736 --pasbody = pasbody:gsub("(%b{})", function(m1) return 'begin' .. trimChars(m1, '{', '}') .. 'end;' end)
771737
772738--strings and escaped characters
773739 tblEscapedCahrs = {
774740 {'\\"', '"'},
775741 {'\\\\', '\\'},
776742 {'\\t', "'#9"},
777743 {'\\n', "'#10'"},
778744 {'\\f', "'#12'"},
779745 }
780746 pasbody = pasbody:gsub('%b""', function(m1)
781747 return "'" .. trimChars(m1:gsub("'", "''") , '"', '"') .. "'"
782748 end)
783749 for k,v in pairs(tblEscapedCahrs) do
784750 pasbody = pasbody:gsub(v[1], v[2])
785751 end
786752
787753--common functions/procedure
788754 tblOrigFuncNewFunc = {
789755 {'printf', 'format'},
790756 {'malloc', 'GetMem'},
791757 {'free', 'Dispose'},
792758 {'memcpy', 'Move'},
793759 }
794760
795761 for k,v in pairs(tblOrigFuncNewFunc) do
796762 pasbody = pasbody:gsub('([\t ]+)' .. v[1] .. '%(', '%1' .. v[2] .. '(')
797763 end
798764
799765--typedefs
800766 isTypeEmmited = false
801767 function addType(m1,m2)
802768 local sm
803769 sm = ''
804770 if not isTypeEmmited then
805771 isTypeEmmited = true
806772 sm = '\ntype'
807773 end
808774 return sm .. '\n\t' .. m2 .. ' = ' .. m1 .. ';'
809775 end
810776 --pasbody = pasbody:gsub('\ntypedef%s+struct%s+([^%s]+)%s+([^;]+)', addType)
811777 --pasbody = pasbody:gsub('\ntypedef%s+([^%s]+)%s+([^;]-)', addType)
812778 pasbody = pasbody:gsub('\n[\t ]*typedef[\t ]+struct[\t ]+([%w_]+)[\t ]+([^;]+)', '\n%2 = %1')
813779 pasbody = pasbody:gsub('\n[\t ]*typedef[\t ]+struct%s+(%b{})%s+([%w_]+);',
814780 function(m1, m2) return m2 .. ' = record' .. trimChars(m1, '{', '}') .. 'end;' end)
815781
816782-- print the results
817783print(pasbody)
818784```
819785
820786Here is an example of a c file converted.
821787
822788``` pascal
823789//uses lua, llimits, lmem, lstate, lzio;
824790(*
825791** $Id: lzio.c,v 1.31.1.1 2007/12/27 13:02:25 roberto Exp $
826792** a generic input stream interface
827793** See Copyright Notice in lua.h
828794*)
829795
830796//#include <string.h>
831797
832798{$define lzio_c}
833799{$define LUA_CORE}
834800
835801//#include 'lua.h'
836802
837803//#include 'llimits.h'
838804//#include 'lmem.h'
839805//#include 'lstate.h'
840806//#include 'lzio.h'
841807
842808function luaZ_fill(z : PTZIO):integer;
843809var
844810 L : Plua_State;
845811 buff : Pchar;
846812 size : size_t;
847813begin
848814 *L := z.L;
849815 lua_unlock(L);
850816 buff := z.reader(L, z.data, @size);
851817 lua_lock(L);
852818 if (buff = nil ) or ( size = 0) then exit(EOZ);
853819 z.n := size - 1;
854820 z.p := buff;
855821 exit(char2int(PosInc^((z.p))));
856822end;
857823
858824function luaZ_lookahead(z : PTZIO):integer;
859825begin
860826 if (z.n = 0) then begin
861827 if (luaZ_fill(z) = EOZ)
862828 exit(EOZ);
863829 else begin
864830 PosInc(z.n); (* luaZ_fill removed first byte; put back it *)
865831 z-PosDec(>p);
866832 end;
867833 end;
868834 exit(char2int(z^.p));
869835end;
870836
871837procedure luaZ_init(L : Plua_State; z : PTZIO; reader : lua_Reader; data : Ppointer);
872838begin
873839 z.L := L;
874840 z.reader := reader;
875841 z.data := data;
876842 z.n := 0;
877843 z.p := nil;
878844end;
879845
880846(* --------------------------------------------------------------- read --- *)
881847
882848function luaZ_read(z : PTZIO; b : Ppointer; n : size_t):size_t;
883849var
884850 m : size_t;
885851begin
886852 while (n) do begin
887853 if (luaZ_lookahead(z) = EOZ)
888854 exit(n); (* exit(number of missing bytes *)
889855 m = (n <= z.n) ? n : z.n); (* min. between n and z.n *)
890856 Move(b, z.p, m);
891857 z.n -= m;
892858 z.p += m;
893859 b := (char *)b + m;
894860 n -= m;
895861 end;
896862 exit(0);
897863end;
898864
899865(* ------------------------------------------------------------------------ *)
900866
901867function luaZ_openspace(L : Plua_State; buff : PTMbuffer; n : size_t):Pchar;
902868begin
903869 if (n > buff.buffsize) then begin
904870 if (n < LUA_MINBUFFER) then n := LUA_MINBUFFER;
905871 luaZ_resizebuffer(L, buff, n);
906872 end;
907873 exit(buff.buffer);
908874end;
909875```
910876
911877# Manual conversion of C headers to Pascal
912878
913879In this case one should utilize ones knowledge of C and Pascal together to make the headers. One can learn how to do the conversion by looking at examples.
914880
915881## Converting constants
916882
917883C:
918884
919885``` c
920886#define SOME_CONSTANT 5
921887#define ANOTHER_CONSTANT 6
922888```
923889
924890Pascal:
925891
926892``` pascal
927893const
928894 SOME_CONSTANT = 5;
929895 ANOTHER_CONSTANT = 6;
930896```
931897
932898## Converting exported routines
933899
934900The most basic routines can be translated like this:
935901
936902C:
937903
938904``` c
939905int somekind_of_method(int* param_first, int* param_second)
940906void another_method(char*)
941907```
942908
943909Pascal:
944910
945911``` pascal
946912uses ctypes;
947913
948914const
949915 MyLib = 'mylib';
950916
951917function somekind_of_method(param_first, param_second: pcint): cint; external MyLib;
952918procedure another_method_renamed(param1: PChar); external MyLib name 'another_method';
953919```
954920
955921Note that one should use the ctypes unit together with the types that it declares to convert C headers instead of trying to use the basic Pascal types directly. This ensures that the bindings are correct in all platforms, as the C types might have different sizes on different architectures and operating systems.
956922
957923Also, note that pointers to basic C types can be converted to types declared in ctypes, for example, int* is a pointer to a C int, so we can use the type pcint (pointer to a c integer). The C type int can be converted to cint and in Pascal we can shorten the declaration of parameters which are declared one after as folows: "param_first, param_second: pcint".
958924
959925Finally, note that if the return value is **void**, then we should use a procedure instead of function.
960926
961927[Creating Pascal bindings for C](https://github.com/williamhunter/pascal-bindings-for-c) is a tutorial with source code that explains in detail how to create manual bindings for C libraries. It deals with various data types (strings, arrays, etc). The tutorial also demonstrates how to create static (*.a) and shared/dynamic libraries (*.so or *.dll) libraries using the MinGW C compiler. Using this approach in combination with h2pas can be quite efficient.
962928
963929See also the [[http://www.freepascal.org/docs-html/ref/refse85.html External Functions]] section of the reference guide.
964930
965931# Publish your bindings on Lazarus-CCR or Free Pascal
966932
967933You can submit your binding code to the Lazarus CCR repository (see e.g. [[Using the Lazarus-ccr SVN repository]]) so others can use (and maintain) it, too. If the bindings are useful for general, cross-platform work, they could be included in the FreePascal distribution as well.
968934
969935# See also
970936
971937- G. Marcou, E. Engler, A. Varnek, *How to use C code in Free Pascal projects*, Université de Strasbourg, 2009, [Link 1](http://ftp.psu.ru/programming/fpc/docs-pdf/CinFreePascal.pdf), [Link 2](https://www.scribd.com/document/370417404/CinFreePascal-pdf), Short description: Short tutorial that describes the process of using C and C++ code in Free Pascal, including writing of wrapper code.
972938- \[<https://github.com/williamhunter/pascal-bindings-for-c/blob/master/docs/Creating%20Pascal%20bindings%20for%20C%20(v1.0>).pdf Another tutorial\] showing how to use object-code and libraries created in C.
973939- [[Common problems when converting C header files]]
974940- [[Pascal for C users]]
975941- [[Lazarus/FPC Libraries]]
976942- [[SWIG]]
977943
978944---
979945*From the Free Pascal wiki, page "Creating bindings for C libraries", as it stood on 2023-08-28 after a change by Myrmidon, out of the wiki's dump of 2024-05-26. Copyright stays with the author who wrote it, according to the old wiki's [[Licensing]] page. If you have concerns about this material, [message the admin](/wiki-contact).*

Back to history