Sign in Join
Wiki copytemporary

Creating bindings for C libraries: what changed

Revision 2 to 3. 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
96-` MPI_INCLUDED`\
97-` MPIO_INCLUDE`\
98-` NEEDS_MPI_FINT`\
99-` MPI_BUILD_PROFILING`\
100-` MPICH_SUPPRESS_PROTOTYPES`\
101-` MPI_Wtime`\
102-` HAVE_MPICH2G2`\
103-` FOO`\
104-` HAVE_PRAGMA_HP_SEC_DEF`
96+```text
97+ MPI_INCLUDED
98+ MPIO_INCLUDE
99+ NEEDS_MPI_FINT
100+ MPI_BUILD_PROFILING
101+ MPICH_SUPPRESS_PROTOTYPES
102+ MPI_Wtime
103+ HAVE_MPICH2G2
104+ FOO
105+ HAVE_PRAGMA_HP_SEC_DEF
106+```
105107
106108Add the following to the *Defines* property:
107109
108-` MPICH2`\
109-` MPICH_NEW_MPIIO`
110+```text
111+ MPICH2
112+ MPICH_NEW_MPIIO
113+```
110114
111115## Writing your own converter tools
112116
113117### Using the tool "Search and replace"
114118
115119Many 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.
116120
117121#### Option: trtRegExpr
118122
119123If 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]].
120124
121125### Example: rename an identifier Tguint to guint
122126
123127| Property | Value |
124128|-------------|-------------------------------|
125129| Caption | Rename Tguint to guint |
126130| SearchFor | Tguint |
127131| ReplaceWith | guint |
128132| Options | \[trtMatchCase,trtWholeWord\] |
129133
130134### Example: rename several identifiers
131135
132136Rename Tguint to guint, Tgulong to gulong, Tgint to gint:
133137
134138**Property**
135139
136140**Value**
137141
138142Caption
139143
140144Rename Tguint to guint
141145
142146SearchFor
143147
144148 T(guint|gint|gulong)
145149
146150ReplaceWith
147151
148152\$1
149153
150154Options
151155
152156\[trtMatchCase,trtWholeWord,trtRegExpr\]
153157
154158### Example: MPICH2
155159
156160#### Removing Makro MPI_DUP_FN
157161
158162The *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.
159163
160164Add a 'Search and replace' tool to the before H2Pas tools (at the end) with the following settings:
161165
162166**Property**
163167
164168**Value**
165169
166170Caption
167171
168172Remove dummy define MPI_DUP_FN
169173
170174Name
171175
172176RemoveDummyDefineMPI_DUP_FN
173177
174178SearchFor
175179
176180 ^#define\s+MPI_DUP_FN\s+MPIR_Dup_fn
177181
178182ReplaceWith
179183
180184Options
181185
182186\[trtMatchCase,trtRegExpr\]
183187
184188#### Removing typecasts to MPI_DUP_FN
185189
186190Add a 'Search and replace' tool to the before H2Pas tools (at the end) with the following settings:
187191
188192**Property**
189193
190194**Value**
191195
192196Caption
193197
194198Remove MPI_DUP_FN Makros
195199
196200Name
197201
198202Remove_MPI_DUP_Func
199203
200204SearchFor
201205
202206 ^#define .*MPI_DUP_FN\).*$
203207
204208ReplaceWith
205209
206210Options
207211
208212\[trtMatchCase,trtRegExpr\]
209213
210214#### Removing functions MPIO_Test, PMPIO_Test, MPIO_Wait and PMPIO_Wait
211215
212216The MPIO_Test, PMPIO_Test, MPIO_Wait and PMPIO_Wait functions are defined twice and redefined via macro. So, the functions can be removed:
213217
214218Add a 'Search and replace' tool to the before H2Pas tools (at the end) with the following settings:
215219
216220**Property**
217221
218222**Value**
219223
220224Caption
221225
222226 Remove Func P?MPIO_(Test|Wait)
223227
224228Name
225229
226230RemoveFuncP_MPIO_Test_Wait
227231
228232SearchFor
229233
230234 ^int P?MPIO_(Test|Wait).*$
231235
232236ReplaceWith
233237
234238Options
235239
236240\[trtMatchCase,trtRegExpr\]
237241
238242## Improving an existing tool
239243
240244You found a bug and want to fix it or you want to extend one of the above tools. Great!
241245
242246Most 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:
243247
244248``` bash
245249./testh2pastool files/h2pastest.pas
246250```
247251
248252## Writing a custom tool
249253
250254You 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):
251255
252256``` pascal
253257uses
254258 Classes, ..., IDETextConverter;
255259
256260type
257261 TYourToolClass = class(TCustomTextConverterTool)
258262 public
259263 class function ClassDescription: string; override;
260264 function Execute(aText: TIDETextConverter): TModalResult; override;
261265 end;
262266
263267procedure Register;
264268
265269implementation
266270
267271procedure Register;
268272begin
269273 TextConverterToolClasses.RegisterClass(TYourToolClass);
270274end;
271275```
272276
273277Do 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.
274278
275279## Future work / Missing stuff
276280
277281- A tool to convert static linked procedures to dynamic linked procedure variables
278282- 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.
279283- Find missing identifiers and let the user check which one to comment or replace with base types.
280284- Create a list of macro functions, which were half translated.
281285
282286C:
283287
284288``` c
285289typedef struct page
286290{
287291 u32int present : 1; // Page present in memory
288292 u32int rw : 1; // Read-only if clear, readwrite if set
289293 u32int user : 1; // Supervisor level only if clear
290294 u32int accessed : 1; // Has the page been accessed since last refresh?
291295 u32int dirty : 1; // Has the page been written to since last refresh?
292296 u32int unused : 7; // Amalgamation of unused and reserved bits
293297 u32int frame : 20; // Frame address (shifted right 12 bits)
294298} page_t;
295299```
296300
297301Pascal:
298302
299303``` pascal
300304type
301305 Unsigned_7 = 0 .. (1 shl 7) - 1;
302306 Unsigned_20 = 0 .. (1 shl 20) - 1;
303307
304308type
305309 page_t = bitpacked record
306310 present : boolean;
307311 rw : boolean;
308312 user : boolean;
309313 accessed : boolean;
310314 dirty : boolean;
311315 unused : Unsigned_7;
312316 frame : Unsigned_20;
313317 end;
314318```
315319
316320# Convert C to Pascal with lua script
317321
318322I'm impressed by the power of the lua scripting language and how easy it is to do funny things with it.
319323
320324``` lua
321325--
322326-- Please send any improvement to mingodadATgmailDOTcom.
323327--
324328
325329inFileName = arg[1] or 'g:\\tmp\\plua\\clua\\lvm.c'
326330
327331fh = assert(io.open(inFileName))
328332cbody = fh:read('*a')
329333fh:close()
330334
331335pasbody = cbody
332336
333337function split(str, pat)
334338 local t = {} -- NOTE: use {n = 0} in Lua-5.0
335339 local fpat = '(.-)' .. pat
336340 local last_end = 1
337341 local s, e, cap = str:find(fpat, 1)
338342 while s do
339343 if s ~= 1 or cap ~= '' then
340344 table.insert(t, cap)
341345 end
342346 last_end = e+1
343347 s, e, cap = str:find(fpat, last_end)
344348 end
345349 if last_end <= #str then
346350 cap = str:sub(last_end)
347351 table.insert(t, cap)
348352 end
349353 return t
350354end
351355
352356function trim(s)
353357 -- from PiL2 20.4
354358 return (s:gsub('^%s*(.-)%s*$', '%1'))
355359end
356360
357361function trimChars(str, c1, c2)
358362 return str:gsub('^%' .. c1 .. '*(.-)%' .. c2 .. '*$', '%1')
359363end
360364
361365function getPascalType(cType)
362366 if cType == 'void' then
363367 return 'pointer'
364368 elseif cType == 'int' then
365369 return 'integer'
366370 elseif cType == 'short' then
367371 return 'Shortint'
368372 elseif cType == 'define' then
369373 return 'is_define'
370374 else
371375 return cType
372376 end
373377end
374378
375379--check var name and type
376380--if var starts with a capital letter
377381-- add 'T' informt of it
378382--if var is preceded by * then add a 'P' for type
379383
380384function checkVarType(aType, aVar)
381385 aType = getPascalType(aType)
382386 aType = aType:gsub('^(%u)', 'T%1')
383387 aVar = aVar:gsub('^(%*)', function(m)
384388 aType = 'P' .. aType
385389 return ''
386390 end)
387391 aVar = aVar:gsub('(%[[^]]+%])', function(m)
388392 aType = 'array' .. m .. ' of ' .. aType
389393 return ''
390394 end)
391395 return aType, aVar
392396end
393397
394398function varDeclaration(aType, aVar)
395399 aType, aVar = checkVarType(aType, aVar)
396400 return aVar .. ' : ' .. aType
397401end
398402
399403function checkDeclarationOrGoto(m1, m2, m3)
400404 if m2 ~= 'goto' then
401405 return '\n' .. m1 .. varDeclaration(m2, m3) .. ';'
402406 else
403407 return '\n' .. m1 .. m2 .. ' ' .. m3 .. ';'
404408 end
405409end
406410
407411function addProcFunc(mtype, mname, mparams, mbody)
408412 local str, isFunc, rType, k, v
409413 if mtype == 'void' then
410414 str = '\nprocedure '
411415 isFunc = false
412416 else
413417 str = '\nfunction '
414418 isFunc = true
415419 end
416420 mtype, mname = checkVarType(mtype, mname)
417421 local vparams = split(trimChars(mparams, '(',')'), ',')
418422 str = str .. mname .. '('
419423
420424 local tparams = {}
421425 for k, v in pairs(vparams) do
422426 local vparam = split(trim(v), ' ')
423427 if #vparam > 2 then
424428 table.insert(tparams, vparam[1] .. ' ' .. varDeclaration(vparam[2], vparam[3]))
425429 else
426430 --print(vparam[1], vparam[2])
427431 table.insert(tparams, varDeclaration(vparam[1], vparam[2] or 'is_define'))
428432 end
429433 end
430434
431435 str = str .. table.concat(tparams, '; ') .. ')'
432436 if isFunc then
433437 if mtype == 'int' then
434438 rType = 'integer'
435439 else
436440 rType = mtype
437441 end
438442 str = str .. ':' .. rType
439443 end
440444
441445 if mbody then
442446 local tblLabels = {}
443447 for v in mbody:gmatch('\n[\t ]*([%w_]+)[\t ]*:[^=]') do
444448 if v ~= 'default' then
445449 table.insert(tblLabels, v)
446450 end
447451 end
448452
449453 if #tblLabels > 0 then
450454 str = str .. ';\nlabel\n '.. table.concat(tblLabels, ', ')
451455 end
452456
453457 local declarations = {}
454458
455459 mbody = mbody:gsub('(\n[\t ]*)const[\t ]+([%w_]+)[\t ]+(%*?[%w_]+)[\t ]*:%=[\t ]*([^;\n]+;)','%1 %2 %3 := %4')
456460
457461 mbody = mbody:gsub('(\n[\t ]*)([%w_]+)[\t ]+(%*?[%w_]+)[\t ]*:%=[\t ]*([^;\n]+;)', function(m1, m2, m3, m4)
458462 table.insert(declarations, varDeclaration(m2, m3) .. ';' )
459463 return m1 .. m3 .. ' := ' .. m4
460464 end)
461465
462466 mbody = mbody:gsub('\n[\t ]*const[\t ]*([%w_]+)[\t ]+(%*?[%w_]+)[\t ]*;', function(m1,m2)
463467 table.insert(declarations, varDeclaration(m1, m2) .. ';')
464468 return ''
465469 end)
466470
467471 mbody = mbody:gsub('\n([\t ]*)([%w_]+)[\t ]+(%*?[%w_ ,]+);', function(m1,m2,m3)
468472 if m2 ~= 'goto' then
469473 table.insert(declarations, varDeclaration(m2, m3) .. ';')
470474 return ''
471475 else
472476 return '\n' .. m1 .. m2 .. ' ' .. m3 .. ';'
473477 end
474478 end)
475479
476480 if #declarations > 0 then
477481 local unique_decl = {}
478482 table.sort(declarations)
479483 local lastV = ''
480484 local k2, v2
481485 for k2,v2 in pairs(declarations) do
482486 if v2 ~= lastV then
483487 lastV = v2
484488 table.insert(unique_decl, v2)
485489 end
486490 end
487491 str = str .. ';\nvar\n '.. table.concat(unique_decl, '\n ') .. '\n'
488492 else
489493 str = str .. ';\n'
490494 end
491495
492496 str = str .. 'begin' .. trimChars(mbody, '{','}') .. 'end'
493497 end
494498 return str .. ';'
495499end
496500
497501--*************************************
498502--
499503-- Order of substitutions are important
500504--
501505--*************************************
502506
503507--=assignements statements
504508 pasbody = pasbody:gsub("([\t ]*)([%w_]+)[\t ]*=[\t ]*([^=;\n]+);", '%1%2 := %3;')
505509
506510--return
507511 pasbody = pasbody:gsub('([\t ]*)return[\t ]+([^;]+);', '%1exit(%2);')
508512
509513--NULL
510514 pasbody = pasbody:gsub('([^%w]+)NULL([^%w]+)', '%1nil%2')
511515
512516--void functions to procedure declaratios
513517 pasbody = pasbody:gsub('([%w_*]+)[\t ]+([%w_*]+)[\t ]*(%b())[%s]*(%b{})', addProcFunc)
514518 --pasbody = pasbody:gsub('\n([%w_]+)%s+([^%s]+)%s*(%b());', addProcFunc)
515519
516520--const/static
517521 tblLeaveCommented = {
518522 'const', 'static'
519523 }
520524 for k,v in pairs(tblLeaveCommented) do
521525 pasbody = pasbody:gsub('([\t ]*)(' .. v .. ')([\t ]*)', '%1{%2}%3')
522526 end
523527
524528--defines to const
525529 isConstEmmited = false
526530 function addConst(m1,m2)
527531 local sm
528532 sm = ''
529533 if not isConstEmmited then
530534 isConstEmmited = true
531535 sm = '\nconst'
532536 end
533537 return sm .. '\n\t' .. m1 .. ' = ' .. m2 .. ';'
534538 end
535539
536540 isConstEmmited = false
537541 pasbody = pasbody:gsub('\n#define%s+([^%s]+)%s+(%b())', '\nconst %1 = %2;')
538542 isConstEmmited = false
539543 pasbody = pasbody:gsub('\n#define%s+([^%s]+)[ \t]+([^%s]+)', '\nconst %1 = %2;')
540544
541545--includes
542546 tblUses = {}
543547 pasbody = pasbody:gsub('\n#[ \t]*(include)%s+([^\n]+)', function(m1,m2)
544548 for w in m2:gmatch('"([^.]+)%..+"') do
545549 table.insert(tblUses, w)
546550 end
547551 return '\n//#' .. m1 .. ' ' .. m2
548552 end)
549553 pasbody = '//uses ' .. table.concat(tblUses, ', ') .. ';\n' .. pasbody
550554
551555--defines to compiler directives
552556 pasbody = pasbody:gsub('\n#([^\n]+)', '\n{$%1}')
553557
554558--comments
555559 pasbody = pasbody:gsub('\n#', '\n//#')
556560 pasbody = pasbody:gsub('/%*', '(*')
557561 pasbody = pasbody:gsub('%*/', '*)')
558562
559563--declarations
560564 pasbody = pasbody:gsub('\n([\t ]*)([%w_]+)[\t ]+([%w_ ,]+);', checkDeclarationOrGoto)
561565
562566--structs
563567 function parseStruct(m1, m2)
564568 return '\n' .. m1 .. ' = record\n' .. trimChars(m2, '{', '}') .. '\nend'
565569 end
566570 pasbody = pasbody:gsub('\n[\t ]*struct[\t ]+([%w_]+)[\t ]*(%b{})', parseStruct)
567571
568572--if statements
569573 parseStatementCalled = false
570574
571575 function parseStatement(mSpace, mStatement, mCond, mBody)
572576 parseStatementCalled = true
573577 local vStatement
574578 if mStatement == 'if' then
575579 vStatement = mStatement .. ' ' .. mCond .. ' then'
576580 elseif mStatement == 'else' then
577581 return mSpace .. mStatement .. ' begin' .. trimChars(mCond, '{', '}') .. ' end;'
578582 elseif (mStatement == 'while') then
579583 vStatement = mStatement .. ' ' .. mCond .. ' do'
580584 elseif (mStatement == 'for') then
581585 vStatement = mStatement .. ' ' .. mCond .. ' do'
582586 --local forArgs = split(trimChars(mCond, '(', ')'), ';')
583587 --return mSpace .. (forArgs[1] or '') .. ';' .. mSpace ..'while (' ..
584588 -- (forArgs[2] or 'true') .. ') do' .. mSpace .. 'begin' .. mSpace .. trimChars(mBody, '{', '}') .. '\n' .. mSpace ..
585589 -- (forArgs[3] or '') .. ';\n' .. mSpace .. 'end;'
586590 else
587591 vStatement = mStatement .. ' ' .. mCond .. '\n'
588592 end
589593 return mSpace .. vStatement .. ' begin' .. trimChars(mBody, '{', '}') .. 'end;'
590594 end
591595
592596 -- for nested blocks
593597 parseStatementCalled = true
594598 while(parseStatementCalled) do
595599 parseStatementCalled = false
596600 pasbody = pasbody:gsub('([^%w_])(if)[\t ]*(%b())[\t ]*(%b{})', parseStatement)
597601 end
598602
599603 -- else
600604 pasbody = pasbody:gsub('([\t ]+)(else)[\t ]*(%b{})', parseStatement)
601605
602606 pasbody = pasbody:gsub("([^%w_])if[\t ]*(%b())[\t ]*([^;\n]+);", '%1if %2 then %3;')
603607
604608--?: short if
605609 pasbody = pasbody:gsub("([^?])?([^:;\n]):[\t ]*([^%s;]+);", ' iff(%1, %2, %3) ')
606610
607611--while loop
608612 -- for nested blocks
609613 parseStatementCalled = true
610614 while(parseStatementCalled) do
611615 parseStatementCalled = false
612616 pasbody = pasbody:gsub('([^%w_])(while)[\t ]*(%b())[\t ]*(%b{})', parseStatement)
613617 end
614618
615619--for loop
616620 -- for nested blocks
617621 parseStatementCalled = true
618622 while(parseStatementCalled) do
619623 parseStatementCalled = false
620624 pasbody = pasbody:gsub('([^%w_])(for)[\t ]*(%b())%s*(%b{})', parseStatement)
621625 end
622626
623627--do/while
624628 -- for nested blocks
625629 parseStatementCalled = true
626630 while(parseStatementCalled) do
627631 parseStatementCalled = false
628632 pasbody = pasbody:gsub("([^%w_])do[\t ]*(%b{})[\t ]*while([^;\n]+);",
629633 function(m1, m2, m3)
630634 parseStatementCalled = true
631635 return m1 .. 'repeat ' .. trimChars(m2, '{', '}') .. 'until not' .. m3
632636 end)
633637 end
634638
635639--switch/case statements
636640
637641 --begin/end enclose blocks around
638642 -- for nested blocks
639643 parseStatementCalled = true
640644 while(parseStatementCalled) do
641645 parseStatementCalled = false
642646 pasbody = pasbody:gsub("([^%w_]case[\t ]+[^:]+)[\t ]*:[%s]*(%b{})",
643647 function(m1, m2, m3)
644648 parseStatementCalled = true
645649 return m1 .. ': begin' .. trimChars(m2, '{', '}') .. 'end;'
646650 end)
647651 pasbody = pasbody:gsub("([^%w_]default[\t ]*):[%s]*(%b{})",
648652 function(m1, m2, m3)
649653 parseStatementCalled = true
650654 return m1 .. ': begin' .. trimChars(m2, '{', '}') .. 'end;'
651655 end)
652656 end
653657
654658 --breaks remove
655659 pasbody = pasbody:gsub("([^%w_]case[\t ]+[^:]+)[\t ]*:[%s]*(.-)break;", '%1: %2')
656660
657661 --case
658662 pasbody = pasbody:gsub("([^%w_])case[\t ]+([^:]+):", '%1%2:')
659663
660664 -- for nested blocks
661665 parseStatementCalled = true
662666 while(parseStatementCalled) do
663667 parseStatementCalled = false
664668 pasbody = pasbody:gsub("([^%w_])switch[\t ]*(%b())[\t ]*(%b{})",
665669 function(m1, m2, m3)
666670 parseStatementCalled = true
667671 return m1 .. 'case ' .. m2 .. ' of ' .. trimChars(m3, '{', '}') .. 'end;'
668672 end)
669673 end
670674
671675 --pasbody = pasbody:gsub("([\t ]*)case[\t ]+('.')[\t ]*:\n", '%1%2:\n')
672676 --pasbody = pasbody:gsub("([\t ]*)case[\t ]+(%d+)[\t ]*:\n", '%1%2:\n')
673677 --pasbody = pasbody:gsub("([\t ]*)(case)[\t ]+('.')[\t ]*:[\t ]*(%b{})", '%1%2 : %3')
674678
675679--pre/pos increments
676680 -- (n)++ -> PosInc(n)
677681 pasbody = pasbody:gsub("(%([^%s+]+%))%+%+", 'PosInc%1')
678682 -- n*++ -> PosInc(n*)
679683 pasbody = pasbody:gsub("([^%s*+]+)%+%+", 'PosInc(%1)')
680684 -- ++(n) -> PreInc(n)
681685 pasbody = pasbody:gsub("%+%+(%([^%s;+]+%))", 'PreInc%1')
682686 -- ++*n -> PreInc(n)
683687 pasbody = pasbody:gsub("%+%+([^%s*;+]+)", 'PreInc(%1)')
684688
685689 -- (n)--
686690 pasbody = pasbody:gsub("(%([^%s-]+%))%-%-", 'PosDec%1')
687691 -- n*--
688692 pasbody = pasbody:gsub("([^%s*-]+)%-%-", 'PosDec(%1)')
689693 -- --(n)
690694 pasbody = pasbody:gsub("%-%-(%([^%s;-]+%))", 'PreDec%1')
691695 -- --n
692696 pasbody = pasbody:gsub("%-%-([^%s;-]+)", 'PreDec(%1)')
693697
694698--boolean operators
695699 tblBooleanOperators = {
696700 {'||', 'or'},
697701 {'&&', 'and'},
698702 }
699703 for k,v in pairs(tblBooleanOperators) do
700704 pasbody = pasbody:gsub('([\t ]*)' .. v[1] .. '([\t ]*)', '%1) ' .. v[2] .. ' (%2')
701705 end
702706
703707--pointers
704708 pasbody = pasbody:gsub("%->", '.')
705709 pasbody = pasbody:gsub("%(%*([%w_]+)", '(%1^')
706710 pasbody = pasbody:gsub("%*%*([%w_]+)", '%1^^')
707711 pasbody = pasbody:gsub("&([%w_]+)", '@%1')
708712 pasbody = pasbody:gsub("%*([%w_]+)[\t ]*:[\t ]*([%w_]+);", '%1 : ^%2;')
709713 pasbody = pasbody:gsub("%^char", 'pchar')
710714 --pasbody = pasbody:gsub("%*([%w_]+)[\t ]*:=[\t ]*([^;\n]+);", '%1^ := %2;')
711715
712716--bit operators
713717 tblBitOperators = {
714718 {'<<', 'shl'},
715719 {'>>', 'shr'},
716720 {'|', 'or'},
717721 {'&', 'and'},
718722 {'~', 'not'},
719723 }
720724
721725 for k,v in pairs(tblBitOperators) do
722726 pasbody = pasbody:gsub('([\t ]*)' .. v[1] .. '([\t ]*)', '%1 ' .. v[2] .. ' %2')
723727 end
724728
725729--==eq operator
726730 pasbody = pasbody:gsub("([\t ]*)%=%=([\t ]*)", '%1 = %2')
727731
728732--!= not eq operator
729733 pasbody = pasbody:gsub("([\t ]*)!%=([\t ]*)", '%1 <> %2')
730734
731735--! unary
732736 pasbody = pasbody:gsub('([\t ]*)!([\t ]*)', '%1 not %2')
733737
734738--blocks
735739 --pasbody = pasbody:gsub("(%b{})", function(m1) return 'begin' .. trimChars(m1, '{', '}') .. 'end;' end)
736740 --pasbody = pasbody:gsub("(%b{})", function(m1) return 'begin' .. trimChars(m1, '{', '}') .. 'end;' end)
737741
738742--strings and escaped characters
739743 tblEscapedCahrs = {
740744 {'\\"', '"'},
741745 {'\\\\', '\\'},
742746 {'\\t', "'#9"},
743747 {'\\n', "'#10'"},
744748 {'\\f', "'#12'"},
745749 }
746750 pasbody = pasbody:gsub('%b""', function(m1)
747751 return "'" .. trimChars(m1:gsub("'", "''") , '"', '"') .. "'"
748752 end)
749753 for k,v in pairs(tblEscapedCahrs) do
750754 pasbody = pasbody:gsub(v[1], v[2])
751755 end
752756
753757--common functions/procedure
754758 tblOrigFuncNewFunc = {
755759 {'printf', 'format'},
756760 {'malloc', 'GetMem'},
757761 {'free', 'Dispose'},
758762 {'memcpy', 'Move'},
759763 }
760764
761765 for k,v in pairs(tblOrigFuncNewFunc) do
762766 pasbody = pasbody:gsub('([\t ]+)' .. v[1] .. '%(', '%1' .. v[2] .. '(')
763767 end
764768
765769--typedefs
766770 isTypeEmmited = false
767771 function addType(m1,m2)
768772 local sm
769773 sm = ''
770774 if not isTypeEmmited then
771775 isTypeEmmited = true
772776 sm = '\ntype'
773777 end
774778 return sm .. '\n\t' .. m2 .. ' = ' .. m1 .. ';'
775779 end
776780 --pasbody = pasbody:gsub('\ntypedef%s+struct%s+([^%s]+)%s+([^;]+)', addType)
777781 --pasbody = pasbody:gsub('\ntypedef%s+([^%s]+)%s+([^;]-)', addType)
778782 pasbody = pasbody:gsub('\n[\t ]*typedef[\t ]+struct[\t ]+([%w_]+)[\t ]+([^;]+)', '\n%2 = %1')
779783 pasbody = pasbody:gsub('\n[\t ]*typedef[\t ]+struct%s+(%b{})%s+([%w_]+);',
780784 function(m1, m2) return m2 .. ' = record' .. trimChars(m1, '{', '}') .. 'end;' end)
781785
782786-- print the results
783787print(pasbody)
784788```
785789
786790Here is an example of a c file converted.
787791
788792``` pascal
789793//uses lua, llimits, lmem, lstate, lzio;
790794(*
791795** $Id: lzio.c,v 1.31.1.1 2007/12/27 13:02:25 roberto Exp $
792796** a generic input stream interface
793797** See Copyright Notice in lua.h
794798*)
795799
796800//#include <string.h>
797801
798802{$define lzio_c}
799803{$define LUA_CORE}
800804
801805//#include 'lua.h'
802806
803807//#include 'llimits.h'
804808//#include 'lmem.h'
805809//#include 'lstate.h'
806810//#include 'lzio.h'
807811
808812function luaZ_fill(z : PTZIO):integer;
809813var
810814 L : Plua_State;
811815 buff : Pchar;
812816 size : size_t;
813817begin
814818 *L := z.L;
815819 lua_unlock(L);
816820 buff := z.reader(L, z.data, @size);
817821 lua_lock(L);
818822 if (buff = nil ) or ( size = 0) then exit(EOZ);
819823 z.n := size - 1;
820824 z.p := buff;
821825 exit(char2int(PosInc^((z.p))));
822826end;
823827
824828function luaZ_lookahead(z : PTZIO):integer;
825829begin
826830 if (z.n = 0) then begin
827831 if (luaZ_fill(z) = EOZ)
828832 exit(EOZ);
829833 else begin
830834 PosInc(z.n); (* luaZ_fill removed first byte; put back it *)
831835 z-PosDec(>p);
832836 end;
833837 end;
834838 exit(char2int(z^.p));
835839end;
836840
837841procedure luaZ_init(L : Plua_State; z : PTZIO; reader : lua_Reader; data : Ppointer);
838842begin
839843 z.L := L;
840844 z.reader := reader;
841845 z.data := data;
842846 z.n := 0;
843847 z.p := nil;
844848end;
845849
846850(* --------------------------------------------------------------- read --- *)
847851
848852function luaZ_read(z : PTZIO; b : Ppointer; n : size_t):size_t;
849853var
850854 m : size_t;
851855begin
852856 while (n) do begin
853857 if (luaZ_lookahead(z) = EOZ)
854858 exit(n); (* exit(number of missing bytes *)
855859 m = (n <= z.n) ? n : z.n); (* min. between n and z.n *)
856860 Move(b, z.p, m);
857861 z.n -= m;
858862 z.p += m;
859863 b := (char *)b + m;
860864 n -= m;
861865 end;
862866 exit(0);
863867end;
864868
865869(* ------------------------------------------------------------------------ *)
866870
867871function luaZ_openspace(L : Plua_State; buff : PTMbuffer; n : size_t):Pchar;
868872begin
869873 if (n > buff.buffsize) then begin
870874 if (n < LUA_MINBUFFER) then n := LUA_MINBUFFER;
871875 luaZ_resizebuffer(L, buff, n);
872876 end;
873877 exit(buff.buffer);
874878end;
875879```
876880
877881# Manual conversion of C headers to Pascal
878882
879883In 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.
880884
881885## Converting constants
882886
883887C:
884888
885889``` c
886890#define SOME_CONSTANT 5
887891#define ANOTHER_CONSTANT 6
888892```
889893
890894Pascal:
891895
892896``` pascal
893897const
894898 SOME_CONSTANT = 5;
895899 ANOTHER_CONSTANT = 6;
896900```
897901
898902## Converting exported routines
899903
900904The most basic routines can be translated like this:
901905
902906C:
903907
904908``` c
905909int somekind_of_method(int* param_first, int* param_second)
906910void another_method(char*)
907911```
908912
909913Pascal:
910914
911915``` pascal
912916uses ctypes;
913917
914918const
915919 MyLib = 'mylib';
916920
917921function somekind_of_method(param_first, param_second: pcint): cint; external MyLib;
918922procedure another_method_renamed(param1: PChar); external MyLib name 'another_method';
919923```
920924
921925Note 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.
922926
923927Also, 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".
924928
925929Finally, note that if the return value is **void**, then we should use a procedure instead of function.
926930
927931[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.
928932
929933See also the [[http://www.freepascal.org/docs-html/ref/refse85.html External Functions]] section of the reference guide.
930934
931935# Publish your bindings on Lazarus-CCR or Free Pascal
932936
933937You 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.
934938
935939# See also
936940
937941- 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.
938942- \[<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.
939943- [[Common problems when converting C header files]]
940944- [[Pascal for C users]]
941945- [[Lazarus/FPC Libraries]]
942946- [[SWIG]]
943947
944948---
945949*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