Sign in Join
Wiki copytemporary

Creating bindings for C libraries/zh CN: what changed

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

OldNewLine
11# 简介
22
33本页介绍了如何为“C语言库”创建“Pascal绑定”项目。为了在Pascal中使用C语言库,需要为每个C函数、类型和变量创建Pascal转译代码。这可以用h2pas之类的自动化工具来实现,或者对头文件进行人工转换。
44
55# H2PAS
66
77## 概述
88
99[[H2Pas]]工具可以对很多常见C语言元素进行自动转译。Lazarus提供了图形用户界面(GUI),可用h2pas和其他工具将创建过程自动化。这会创建一个用于更新绑定项目的规则集。这样后续版本的C库转换起来将会更加容易。h2pas向导程序的一个好特性就是使用临时文件,这样C语言头文件就能保持不变。
1010
1111## 工作步骤
1212
1313 * 获取需要转译的C语言头文件。
1414 * 创建工作目录并命名绑定项目。
1515 * 用向导程序新建h2pas项目。
1616 * 将.h文件加入项目。
1717 * 配置h2pas参数。
1818 * 运行向导程序。
1919 * 用文本添加工具修正错误,再次运行向导。
2020 * 如果h2pas运行没有错误了,尝试编译并加入其它工具以美化输出结果。
2121 * 编写测试程序对绑定项目进行测试。
2222 * 在lazarus-ccr或者FreePascal发布绑定项目。
2323
2424## 安装工具
2525
2626正常安装fpc都会自带h2pas工具。
2727
2828在Lazarus IDE中安装h2paswizard软件包。打开“软件包->安装/卸载软件包”,从右侧列表选中H2PasWizard 并点击“安装选中的”,然后点击“保存并重新构建IDE”。 重启IDE,就会出现新的菜单项:工具 -> h2pas。
2929
3030## 获取C语言头文件
3131
3232C语言.h头文件描述了C程序库的接口,通常不会和C程序库一起提供。必须得有源代码或者程序库的开发包才行。例如gtk库的头文件就在gtk+-devel包中。
3333
3434### 示例:MPICH2
3535
3636从http://www-unix.mcs.anl.gov/mpi/mpich2/下载mpich2-1.0.3.tar.gz并解压。.h文件都在mpich2-1.0.3/src/include下。 关于FPC的MPICH,更多信息请参阅[[MPICH|here]]。
3737
3838## 创建工作目录并命名绑定项目
3939
4040请用有意义的名字创建一个目录。目录名不能包含特殊字符,如空格、变音字符、句点和逗号。 将.h文件拷入目录。
4141
4242### 示例:MPICH2
4343
4444Pascal文件将存放于h2p目录。 .h文件将存放于h2p/c_sources目录。
4545```
4646mkdir -p h2p/c_sources
4747 cp mpich2-1.0.3/src/include/*.h h2p/c_sources/
4848```
4949
5050## 用h2pas向导程序新建h2pas项目
5151
5252从“工具 -> h2pas”打开向导程序。这会打开一个窗口。可以和其他IDE窗口之间来回切换。最近的2pas项目会自动加载。如要新建一个项目,请点击“设置 -> 新建/清除设置”。然后点击底部的“保存设置”按钮,选取文件名。
5353
5454注意:如果是第一次使用h2pas向导程序,需用“设置”页确认h2pas.exe转换程序的位置。转换程序h2pas.exe位于FreePascal编译器fpc.exe所在的目录中,如果连同Lazarus下载了FPC,则会位于...\Lazarus\fpc\_version_ \bin目录。请保存设置,下次就不必再设置了。
5555
5656### 示例:MPICH2
5757
5858请点击“设置 -> 新建/清除设置”。然后点击底部的“保存设置”按钮,保存为h2p/mpi2.h2p。
5959
6060## 在h2pas项目中添加.h文件
6161
6262在“C 语言头文件”页可以添加/删除.h文件。可以启用/禁用某些.h文件,以便只转换部分文件。
6363
6464### 示例:MPICH2
6565
6666点击“C 语言头文件 -> 添加 .h 文件 ...”,并选取“mpi.h”和“mpio.h”文件。他们会自动启用。选中mpi.h并点击“合并除此以外的所有内容”,这样向导程序就会将所有头文件并入一个单元文件(mpi.pas)
6767
6868## 设置h2pas参数
6969
7070在“H2Pas 选项”页,可以设置h2pas程序的参数。
7171
7272### 示例:MPICH2
7373
7474 * 启用-e、-D、-p、-w,并禁用其他所有选项。
7575 * -l 库名称路径设为“mpich”。
7676 * 输出扩展名为“.pas”。
7777 * 输出目录默认是h2pas/,所以保持为空即可。
7878
7979## 运行向导程序
8080
8181点击底部的“运行H2Pas”按钮。 这会将<example>.h文件拷贝成临时文件<example>.tmp.h,并运行“H2Pas 之前”页列出的工具。然后会运行h2pas将<example>.tmp.h转换为<example>.inc或<example>.pas,或者别的什么在“设置”页设置的输出扩展名。然后对输出文件运行“H2Pas 之后”页列出的工具。
8282
8383如果h2pas发现语法错误,将会用IDE打开example.tmp.h文件,并跳转到出错的行。通常h2pas只会报“syntax error”,非常宽泛。请参阅[[Common problems when converting C header files|转换C语言头文件时的常见问题]]。
8484
8585### 示例:MPICH2
8686
8787h2pas向导程序已包含的工具足以转换该头文件的所有内容,因此这里不会报错。但单元文件还没创建好,请继续。
8888
8989请切换到“H2Pas 之后”页,加入“Reduce compiler directives in pascal file”工具。
9090
9191在 _Undefines_ 属性中加入:
9292```
9393MPI_INCLUDED
9494 MPIO_INCLUDE
9595 NEEDS_MPI_FINT
9696 MPI_BUILD_PROFILING
9797 MPICH_SUPPRESS_PROTOTYPES
9898 MPI_Wtime
9999 HAVE_MPICH2G2
100100 FOO
101101 HAVE_PRAGMA_HP_SEC_DEF
102102```
103103
104104
105105在 _Defines_ 属性中加入:
106106```
107107MPICH2
108108 MPICH_NEW_MPIIO
109109```
110110
111111## 自行选用转换程序中的工具
112112
113113### 使用“搜索和替换”工具
114114
115115很多类似修改变量名之类的活儿都能用**搜索和替换** 工具完成。这通过“H2Pas 之前”或“H2Pas 之后”页中的'添加新工具'按钮就能加入。 然后设置**SearchFor** 、**ReplaceWith** 、 **Options** 和**Caption** 属性即可。
116116
117117#### trtRegExpr选项
118118
119119如果**Options** 中的trtRegExpr设为**True** ,则表示要使用[[IDE regular expressions|正则表达式]]。这样就能利用**元字符** 和**限定符** 对源代码进行复杂的调整和美化。[[IDE regular expressions]]中给出了**元字符** 和**限定符** 的清单及示例。
120120
121121### 示例:将标识符Tguint更名为guint
122122
123-属性 | 值
124----|---
125-Caption | Rename Tguint to guint
126-SearchFor | Tguint
127-ReplaceWith | guint
128-Options | [trtMatchCase,trtWholeWord]
129-
123+| 属性 | 值 |
124+|---|---|
125+| Caption | Rename Tguint to guint |
126+| SearchFor | Tguint |
127+| ReplaceWith | guint |
128+| Options | [trtMatchCase,trtWholeWord] |
129+
130130### 示例:为多个标识符更名
131131
132132Tguint更名为guint,Tgulong更名为gulong,Tgint更名为gint:
133133
134-属性 | 值
135----|---
136-Caption | Rename Tguint to guint
137-SearchFor | T(guint|gint|gulong)
138-ReplaceWith | $1
139-Options | [trtMatchCase,trtWholeWord,trtRegExpr]
140-
134+| 属性 | 值 |
135+|---|---|
136+| Caption | Rename Tguint to guint |
137+| SearchFor | T(guint\|gint\|gulong) |
138+| ReplaceWith | $1 |
139+| Options | [trtMatchCase,trtWholeWord,trtRegExpr] |
140+
141141### 示例:MPICH2
142142
143143#### 删除MPI_DUP_FN宏
144144
145145_MPI_DUP_FN_ 在mpi头文件中定义了两次。前一次是个空的宏定义,后一次是个实际函数。因此,第一次的定义应该删除。
146146
147147在“H2Pas 之前”加入**搜索和替换** 工具(加到最后),并设置如下:
148148
149-属性 | 值
150----|---
151-Caption | Remove dummy define MPI_DUP_FN
152-Name | RemoveDummyDefineMPI_DUP_FN
153-SearchFor | ^#define\s+MPI_DUP_FN\s+MPIR_Dup_fn
154-ReplaceWith |
155-Options | [trtMatchCase,trtRegExpr]
156-
149+| 属性 | 值 |
150+|---|---|
151+| Caption | Remove dummy define MPI_DUP_FN |
152+| Name | RemoveDummyDefineMPI_DUP_FN |
153+| SearchFor | ^#define\s+MPI_DUP_FN\s+MPIR_Dup_fn |
154+| ReplaceWith | |
155+| Options | [trtMatchCase,trtRegExpr] |
156+
157157#### 删除针对MPI_DUP_FN的类型转换
158158
159159在“H2Pas 之前”加入**搜索和替换** 工具(加到最后),并设置如下:
160160
161-属性 | 值
162----|---
163-Caption | Remove MPI_DUP_FN Makros
164-Name | Remove_MPI_DUP_Func
165-SearchFor | ^#define .*MPI_DUP_FN\\).*$
166-ReplaceWith |
167-Options | [trtMatchCase,trtRegExpr]
168-
161+| 属性 | 值 |
162+|---|---|
163+| Caption | Remove MPI_DUP_FN Makros |
164+| Name | Remove_MPI_DUP_Func |
165+| SearchFor | ^#define .*MPI_DUP_FN\\).*$ |
166+| ReplaceWith | |
167+| Options | [trtMatchCase,trtRegExpr] |
168+
169169#### 删除 MPIO_Test、PMPIO_Test、MPIO_Wait 和 PMPIO_Wait 函数
170170
171171MPIO_Test、PMPIO_Test、MPIO_Wait和PMPIO_Wait都定义了两次,同时定义了宏。因此函数定义应该删除:
172172
173173在“H2Pas 之前”加入**搜索和替换** 工具(加到最后),并设置如下:
174174
175-属性 | 值
176----|---
177-Caption | Remove Func P?MPIO_(Test|Wait)
178-Name | RemoveFuncP_MPIO_Test_Wait
179-SearchFor | ^int P?MPIO_(Test|Wait).*$
180-ReplaceWith |
181-Options | [trtMatchCase,trtRegExpr]
182-
175+| 属性 | 值 |
176+|---|---|
177+| Caption | Remove Func P?MPIO_(Test\|Wait) |
178+| Name | RemoveFuncP_MPIO_Test_Wait |
179+| SearchFor | ^int P?MPIO_(Test\|Wait).*$ |
180+| ReplaceWith | |
181+| Options | [trtMatchCase,trtRegExpr] |
182+
183183## 改进已有转换工具
184184
185185发现缺陷后想要修复,或者扩展上述工具。很好!
186186
187187大部分转换工具的代码都在 h2paswizard 包的 h2pasconvert 单元中。 转换工具程序至少需要类名、说明和**Execute** 方法。 若要脱离 IDE 调试代码并节省大量的编译时间,请参阅项目 components/simpleideintf/examples/testh2pastool.lpi。 请编译后在控制台/终端中启动,并将 .h 文件名作为第一个命令行参数。例如:
188188```
189189./testh2pastool files/h2pastest.pas
190190```
191191
192192## 编写自定义转换工具
193193
194194自行编写转换工具并在 IDE 中注册,也都很容易。 启动一个软件包并加入新的单元文件(比如 unit1),定义一个类。已有工具的代码可以作为示例,请参阅前文。 新的转换工具编写完成并用上述 simpleideintf 项目测试通过后,即可在 IDE 中注册了: 在单元文件(unit1)中添加 **register** 过程,类似以下代码(伪代码):
195195```
196196uses
197197 Classes, ..., IDETextConverter;
198198
199199 type
200200 TYourToolClass = class(TCustomTextConverterTool)
201201 public
202202 class function ClassDescription: string; override;
203203 function Execute(aText: TIDETextConverter): TModalResult; override;
204204 end;
205205
206206 procedure Register;
207207
208208 implementation
209209
210210 procedure Register;
211211 begin
212212 TextConverterToolClasses.RegisterClass(TYourToolClass);
213213 end;
214214```
215215
216216别忘了在软件包编辑器中为对应单元勾选 **register** ,否则 IDE 不会去调用 Register 过程。 然后在 IDE 中安装此软件包。
217217
218218## 未来任务/未尽事宜
219219
220220 * 将静态链接函数转换为动态链接函数变量的工具
221221 * 修正参数名缺失函数的工具,可通过检索 .c 文件完成修复。还可加入注释,注明找到对应函数的 c 文件名。
222222 * 查到缺失的标识符,并让用户确认是否要注释掉或用基础类型取而代之。
223223 * 建立一份已完成部分转译的宏清单。
224224
225225C:
226226```
227227typedef struct page
228228 {
229229 u32int present : 1; // Page present in memory
230230 u32int rw : 1; // Read-only if clear, readwrite if set
231231 u32int user : 1; // Supervisor level only if clear
232232 u32int accessed : 1; // Has the page been accessed since last refresh?
233233 u32int dirty : 1; // Has the page been written to since last refresh?
234234 u32int unused : 7; // Amalgamation of unused and reserved bits
235235 u32int frame : 20; // Frame address (shifted right 12 bits)
236236 } page_t;
237237```
238238
239239Pascal:
240240```
241241type
242242 Unsigned_7 = 0 .. (1 shl 7) - 1;
243243 Unsigned_20 = 0 .. (1 shl 20) - 1;
244244
245245 type
246246 page_t = bitpacked record
247247 present : boolean;
248248 rw : boolean;
249249 user : boolean;
250250 accessed : boolean;
251251 dirty : boolean;
252252 unused : Unsigned_7;
253253 frame : Unsigned_20;
254254 end;
255255```
256256
257257# 用 Lua 脚本将 C 转换为 Pascal
258258
259259我对 Lua 脚本语言的强大功能印象深刻,很容易去完成一些有趣的事情。
260260```
261261--
262262 -- Please send any improvement to mingodadATgmailDOTcom.
263263 --
264264
265265 inFileName = arg[1] or 'g:\\tmp\\plua\\clua\\lvm.c'
266266
267267 fh = assert(io.open(inFileName))
268268 cbody = fh:read('*a')
269269 fh:close()
270270
271271 pasbody = cbody
272272
273273 function split(str, pat)
274274 local t = {} -- NOTE: use {n = 0} in Lua-5.0
275275 local fpat = '(.-)' .. pat
276276 local last_end = 1
277277 local s, e, cap = str:find(fpat, 1)
278278 while s do
279279 if s ~= 1 or cap ~= '' then
280280 table.insert(t, cap)
281281 end
282282 last_end = e+1
283283 s, e, cap = str:find(fpat, last_end)
284284 end
285285 if last_end <= #str then
286286 cap = str:sub(last_end)
287287 table.insert(t, cap)
288288 end
289289 return t
290290 end
291291
292292 function trim(s)
293293 -- from PiL2 20.4
294294 return (s:gsub('^%s*(.-)%s*$', '%1'))
295295 end
296296
297297 function trimChars(str, c1, c2)
298298 return str:gsub('^%' .. c1 .. '*(.-)%' .. c2 .. '*$', '%1')
299299 end
300300
301301 function getPascalType(cType)
302302 if cType == 'void' then
303303 return 'pointer'
304304 elseif cType == 'int' then
305305 return 'integer'
306306 elseif cType == 'short' then
307307 return 'Shortint'
308308 elseif cType == 'define' then
309309 return 'is_define'
310310 else
311311 return cType
312312 end
313313 end
314314
315315
316316 --check var name and type
317317 --if var starts with a capital letter
318318 -- add 'T' informt of it
319319 --if var is preceded by * then add a 'P' for type
320320
321321 function checkVarType(aType, aVar)
322322 aType = getPascalType(aType)
323323 aType = aType:gsub('^(%u)', 'T%1')
324324 aVar = aVar:gsub('^(%*)', function(m)
325325 aType = 'P' .. aType
326326 return ''
327327 end)
328328 aVar = aVar:gsub('(%[[^]]+%])', function(m)
329329 aType = 'array' .. m .. ' of ' .. aType
330330 return ''
331331 end)
332332 return aType, aVar
333333 end
334334
335335 function varDeclaration(aType, aVar)
336336 aType, aVar = checkVarType(aType, aVar)
337337 return aVar .. ' : ' .. aType
338338 end
339339
340340 function checkDeclarationOrGoto(m1, m2, m3)
341341 if m2 ~= 'goto' then
342342 return '\n' .. m1 .. varDeclaration(m2, m3) .. ';'
343343 else
344344 return '\n' .. m1 .. m2 .. ' ' .. m3 .. ';'
345345 end
346346 end
347347
348348 function addProcFunc(mtype, mname, mparams, mbody)
349349 local str, isFunc, rType, k, v
350350 if mtype == 'void' then
351351 str = '\nprocedure '
352352 isFunc = false
353353 else
354354 str = '\nfunction '
355355 isFunc = true
356356 end
357357 mtype, mname = checkVarType(mtype, mname)
358358 local vparams = split(trimChars(mparams, '(',')'), ',')
359359 str = str .. mname .. '('
360360
361361 local tparams = {}
362362 for k, v in pairs(vparams) do
363363 local vparam = split(trim(v), ' ')
364364 if #vparam > 2 then
365365 table.insert(tparams, vparam[1] .. ' ' .. varDeclaration(vparam[2], vparam[3]))
366366 else
367367 --print(vparam[1], vparam[2])
368368 table.insert(tparams, varDeclaration(vparam[1], vparam[2] or 'is_define'))
369369 end
370370 end
371371
372372 str = str .. table.concat(tparams, '; ') .. ')'
373373 if isFunc then
374374 if mtype == 'int' then
375375 rType = 'integer'
376376 else
377377 rType = mtype
378378 end
379379 str = str .. ':' .. rType
380380 end
381381
382382 if mbody then
383383 local tblLabels = {}
384384 for v in mbody:gmatch('\n[\t ]*([%w_]+)[\t ]*:[^=]') do
385385 if v ~= 'default' then
386386 table.insert(tblLabels, v)
387387 end
388388 end
389389
390390 if #tblLabels > 0 then
391391 str = str .. ';\nlabel\n '.. table.concat(tblLabels, ', ')
392392 end
393393
394394 local declarations = {}
395395
396396 mbody = mbody:gsub('(\n[\t ]*)const[\t ]+([%w_]+)[\t ]+(%*?[%w_]+)[\t ]*:%=[\t ]*([^;\n]+;)','%1 %2 %3 := %4')
397397
398398 mbody = mbody:gsub('(\n[\t ]*)([%w_]+)[\t ]+(%*?[%w_]+)[\t ]*:%=[\t ]*([^;\n]+;)', function(m1, m2, m3, m4)
399399 table.insert(declarations, varDeclaration(m2, m3) .. ';' )
400400 return m1 .. m3 .. ' := ' .. m4
401401 end)
402402
403403 mbody = mbody:gsub('\n[\t ]*const[\t ]*([%w_]+)[\t ]+(%*?[%w_]+)[\t ]*;', function(m1,m2)
404404 table.insert(declarations, varDeclaration(m1, m2) .. ';')
405405 return ''
406406 end)
407407
408408 mbody = mbody:gsub('\n([\t ]*)([%w_]+)[\t ]+(%*?[%w_ ,]+);', function(m1,m2,m3)
409409 if m2 ~= 'goto' then
410410 table.insert(declarations, varDeclaration(m2, m3) .. ';')
411411 return ''
412412 else
413413 return '\n' .. m1 .. m2 .. ' ' .. m3 .. ';'
414414 end
415415 end)
416416
417417
418418 if #declarations > 0 then
419419 local unique_decl = {}
420420 table.sort(declarations)
421421 local lastV = ''
422422 local k2, v2
423423 for k2,v2 in pairs(declarations) do
424424 if v2 ~= lastV then
425425 lastV = v2
426426 table.insert(unique_decl, v2)
427427 end
428428 end
429429 str = str .. ';\nvar\n '.. table.concat(unique_decl, '\n ') .. '\n'
430430 else
431431 str = str .. ';\n'
432432 end
433433
434434 str = str .. 'begin' .. trimChars(mbody, '{','}') .. 'end'
435435 end
436436 return str .. ';'
437437 end
438438
439439 --*************************************
440440 --
441441 -- Order of substitutions are important
442442 --
443443 --*************************************
444444
445445 --=assignements statements
446446 pasbody = pasbody:gsub("([\t ]*)([%w_]+)[\t ]*=[\t ]*([^=;\n]+);", '%1%2 := %3;')
447447
448448 --return
449449 pasbody = pasbody:gsub('([\t ]*)return[\t ]+([^;]+);', '%1exit(%2);')
450450
451451 --NULL
452452 pasbody = pasbody:gsub('([^%w]+)NULL([^%w]+)', '%1nil%2')
453453
454454 --void functions to procedure declaratios
455455 pasbody = pasbody:gsub('([%w_*]+)[\t ]+([%w_*]+)[\t ]*(%b())[%s]*(%b{})', addProcFunc)
456456 --pasbody = pasbody:gsub('\n([%w_]+)%s+([^%s]+)%s*(%b());', addProcFunc)
457457
458458 --const/static
459459 tblLeaveCommented = {
460460 'const', 'static'
461461 }
462462 for k,v in pairs(tblLeaveCommented) do
463463 pasbody = pasbody:gsub('([\t ]*)(' .. v .. ')([\t ]*)', '%1{%2}%3')
464464 end
465465
466466 --defines to const
467467 isConstEmmited = false
468468 function addConst(m1,m2)
469469 local sm
470470 sm = ''
471471 if not isConstEmmited then
472472 isConstEmmited = true
473473 sm = '\nconst'
474474 end
475475 return sm .. '\n\t' .. m1 .. ' = ' .. m2 .. ';'
476476 end
477477
478478 isConstEmmited = false
479479 pasbody = pasbody:gsub('\n#define%s+([^%s]+)%s+(%b())', '\nconst %1 = %2;')
480480 isConstEmmited = false
481481 pasbody = pasbody:gsub('\n#define%s+([^%s]+)[ \t]+([^%s]+)', '\nconst %1 = %2;')
482482
483483 --includes
484484 tblUses = {}
485485 pasbody = pasbody:gsub('\n#[ \t]*(include)%s+([^\n]+)', function(m1,m2)
486486 for w in m2:gmatch('"([^.]+)%..+"') do
487487 table.insert(tblUses, w)
488488 end
489489 return '\n//#' .. m1 .. ' ' .. m2
490490 end)
491491 pasbody = '//uses ' .. table.concat(tblUses, ', ') .. ';\n' .. pasbody
492492
493493 --defines to compiler directives
494494 pasbody = pasbody:gsub('\n#([^\n]+)', '\n{$%1}')
495495
496496 --comments
497497 pasbody = pasbody:gsub('\n#', '\n//#')
498498 pasbody = pasbody:gsub('/%*', '(*')
499499 pasbody = pasbody:gsub('%*/', '*)')
500500
501501 --declarations
502502 pasbody = pasbody:gsub('\n([\t ]*)([%w_]+)[\t ]+([%w_ ,]+);', checkDeclarationOrGoto)
503503
504504 --structs
505505 function parseStruct(m1, m2)
506506 return '\n' .. m1 .. ' = record\n' .. trimChars(m2, '{', '}') .. '\nend'
507507 end
508508 pasbody = pasbody:gsub('\n[\t ]*struct[\t ]+([%w_]+)[\t ]*(%b{})', parseStruct)
509509
510510 --if statements
511511 parseStatementCalled = false
512512
513513 function parseStatement(mSpace, mStatement, mCond, mBody)
514514 parseStatementCalled = true
515515 local vStatement
516516 if mStatement == 'if' then
517517 vStatement = mStatement .. ' ' .. mCond .. ' then'
518518 elseif mStatement == 'else' then
519519 return mSpace .. mStatement .. ' begin' .. trimChars(mCond, '{', '}') .. ' end;'
520520 elseif (mStatement == 'while') then
521521 vStatement = mStatement .. ' ' .. mCond .. ' do'
522522 elseif (mStatement == 'for') then
523523 vStatement = mStatement .. ' ' .. mCond .. ' do'
524524 --local forArgs = split(trimChars(mCond, '(', ')'), ';')
525525 --return mSpace .. (forArgs[1] or '') .. ';' .. mSpace ..'while (' ..
526526 -- (forArgs[2] or 'true') .. ') do' .. mSpace .. 'begin' .. mSpace .. trimChars(mBody, '{', '}') .. '\n' .. mSpace ..
527527 -- (forArgs[3] or '') .. ';\n' .. mSpace .. 'end;'
528528 else
529529 vStatement = mStatement .. ' ' .. mCond .. '\n'
530530 end
531531 return mSpace .. vStatement .. ' begin' .. trimChars(mBody, '{', '}') .. 'end;'
532532 end
533533
534534 -- for nested blocks
535535 parseStatementCalled = true
536536 while(parseStatementCalled) do
537537 parseStatementCalled = false
538538 pasbody = pasbody:gsub('([^%w_])(if)[\t ]*(%b())[\t ]*(%b{})', parseStatement)
539539 end
540540
541541 -- else
542542 pasbody = pasbody:gsub('([\t ]+)(else)[\t ]*(%b{})', parseStatement)
543543
544544 pasbody = pasbody:gsub("([^%w_])if[\t ]*(%b())[\t ]*([^;\n]+);", '%1if %2 then %3;')
545545
546546 --?: short if
547547 pasbody = pasbody:gsub("([^?])?([^:;\n]):[\t ]*([^%s;]+);", ' iff(%1, %2, %3) ')
548548
549549 --while loop
550550 -- for nested blocks
551551 parseStatementCalled = true
552552 while(parseStatementCalled) do
553553 parseStatementCalled = false
554554 pasbody = pasbody:gsub('([^%w_])(while)[\t ]*(%b())[\t ]*(%b{})', parseStatement)
555555 end
556556
557557 --for loop
558558 -- for nested blocks
559559 parseStatementCalled = true
560560 while(parseStatementCalled) do
561561 parseStatementCalled = false
562562 pasbody = pasbody:gsub('([^%w_])(for)[\t ]*(%b())%s*(%b{})', parseStatement)
563563 end
564564
565565 --do/while
566566 -- for nested blocks
567567 parseStatementCalled = true
568568 while(parseStatementCalled) do
569569 parseStatementCalled = false
570570 pasbody = pasbody:gsub("([^%w_])do[\t ]*(%b{})[\t ]*while([^;\n]+);",
571571 function(m1, m2, m3)
572572 parseStatementCalled = true
573573 return m1 .. 'repeat ' .. trimChars(m2, '{', '}') .. 'until not' .. m3
574574 end)
575575 end
576576
577577
578578 --switch/case statements
579579
580580 --begin/end enclose blocks around
581581 -- for nested blocks
582582 parseStatementCalled = true
583583 while(parseStatementCalled) do
584584 parseStatementCalled = false
585585 pasbody = pasbody:gsub("([^%w_]case[\t ]+[^:]+)[\t ]*:[%s]*(%b{})",
586586 function(m1, m2, m3)
587587 parseStatementCalled = true
588588 return m1 .. ': begin' .. trimChars(m2, '{', '}') .. 'end;'
589589 end)
590590 pasbody = pasbody:gsub("([^%w_]default[\t ]*):[%s]*(%b{})",
591591 function(m1, m2, m3)
592592 parseStatementCalled = true
593593 return m1 .. ': begin' .. trimChars(m2, '{', '}') .. 'end;'
594594 end)
595595 end
596596
597597 --breaks remove
598598 pasbody = pasbody:gsub("([^%w_]case[\t ]+[^:]+)[\t ]*:[%s]*(.-)break;", '%1: %2')
599599
600600 --case
601601 pasbody = pasbody:gsub("([^%w_])case[\t ]+([^:]+):", '%1%2:')
602602
603603 -- for nested blocks
604604 parseStatementCalled = true
605605 while(parseStatementCalled) do
606606 parseStatementCalled = false
607607 pasbody = pasbody:gsub("([^%w_])switch[\t ]*(%b())[\t ]*(%b{})",
608608 function(m1, m2, m3)
609609 parseStatementCalled = true
610610 return m1 .. 'case ' .. m2 .. ' of ' .. trimChars(m3, '{', '}') .. 'end;'
611611 end)
612612 end
613613
614614 --pasbody = pasbody:gsub("([\t ]*)case[\t ]+('.')[\t ]*:\n", '%1%2:\n')
615615 --pasbody = pasbody:gsub("([\t ]*)case[\t ]+(%d+)[\t ]*:\n", '%1%2:\n')
616616 --pasbody = pasbody:gsub("([\t ]*)(case)[\t ]+('.')[\t ]*:[\t ]*(%b{})", '%1%2 : %3')
617617
618618 --pre/pos increments
619619 -- (n)++ -> PosInc(n)
620620 pasbody = pasbody:gsub("(%([^%s+]+%))%+%+", 'PosInc%1')
621621 -- n*++ -> PosInc(n*)
622622 pasbody = pasbody:gsub("([^%s*+]+)%+%+", 'PosInc(%1)')
623623 -- ++(n) -> PreInc(n)
624624 pasbody = pasbody:gsub("%+%+(%([^%s;+]+%))", 'PreInc%1')
625625 -- ++*n -> PreInc(n)
626626 pasbody = pasbody:gsub("%+%+([^%s*;+]+)", 'PreInc(%1)')
627627
628628 -- (n)--
629629 pasbody = pasbody:gsub("(%([^%s-]+%))%-%-", 'PosDec%1')
630630 -- n*--
631631 pasbody = pasbody:gsub("([^%s*-]+)%-%-", 'PosDec(%1)')
632632 -- --(n)
633633 pasbody = pasbody:gsub("%-%-(%([^%s;-]+%))", 'PreDec%1')
634634 -- --n
635635 pasbody = pasbody:gsub("%-%-([^%s;-]+)", 'PreDec(%1)')
636636
637637 --boolean operators
638638 tblBooleanOperators = {
639639 {'||', 'or'},
640640 {'&&', 'and'},
641641 }
642642 for k,v in pairs(tblBooleanOperators) do
643643 pasbody = pasbody:gsub('([\t ]*)' .. v[1] .. '([\t ]*)', '%1) ' .. v[2] .. ' (%2')
644644 end
645645
646646
647647 --pointers
648648 pasbody = pasbody:gsub("%->", '.')
649649 pasbody = pasbody:gsub("%(%*([%w_]+)", '(%1^')
650650 pasbody = pasbody:gsub("%*%*([%w_]+)", '%1^^')
651651 pasbody = pasbody:gsub("&([%w_]+)", '@%1')
652652 pasbody = pasbody:gsub("%*([%w_]+)[\t ]*:[\t ]*([%w_]+);", '%1 : ^%2;')
653653 pasbody = pasbody:gsub("%^char", 'pchar')
654654 --pasbody = pasbody:gsub("%*([%w_]+)[\t ]*:=[\t ]*([^;\n]+);", '%1^ := %2;')
655655
656656 --bit operators
657657 tblBitOperators = {
658658 {'<<', 'shl'},
659659 {'>>', 'shr'},
660660 {'|', 'or'},
661661 {'&', 'and'},
662662 {'~', 'not'},
663663 }
664664
665665 for k,v in pairs(tblBitOperators) do
666666 pasbody = pasbody:gsub('([\t ]*)' .. v[1] .. '([\t ]*)', '%1 ' .. v[2] .. ' %2')
667667 end
668668
669669 --==eq operator
670670 pasbody = pasbody:gsub("([\t ]*)%=%=([\t ]*)", '%1 = %2')
671671
672672 --!= not eq operator
673673 pasbody = pasbody:gsub("([\t ]*)!%=([\t ]*)", '%1 <> %2')
674674
675675 --! unary
676676 pasbody = pasbody:gsub('([\t ]*)!([\t ]*)', '%1 not %2')
677677
678678 --blocks
679679 --pasbody = pasbody:gsub("(%b{})", function(m1) return 'begin' .. trimChars(m1, '{', '}') .. 'end;' end)
680680 --pasbody = pasbody:gsub("(%b{})", function(m1) return 'begin' .. trimChars(m1, '{', '}') .. 'end;' end)
681681
682682 --strings and escaped characters
683683 tblEscapedCahrs = {
684684 {'\\"', '"'},
685685 {'\\\\', '\\'},
686686 {'\\t', "'#9"},
687687 {'\\n', "'#10'"},
688688 {'\\f', "'#12'"},
689689 }
690690 pasbody = pasbody:gsub('%b""', function(m1)
691691 return "'" .. trimChars(m1:gsub("'", "''") , '"', '"') .. "'"
692692 end)
693693 for k,v in pairs(tblEscapedCahrs) do
694694 pasbody = pasbody:gsub(v[1], v[2])
695695 end
696696
697697 --common functions/procedure
698698 tblOrigFuncNewFunc = {
699699 {'printf', 'format'},
700700 {'malloc', 'GetMem'},
701701 {'free', 'Dispose'},
702702 {'memcpy', 'Move'},
703703 }
704704
705705 for k,v in pairs(tblOrigFuncNewFunc) do
706706 pasbody = pasbody:gsub('([\t ]+)' .. v[1] .. '%(', '%1' .. v[2] .. '(')
707707 end
708708
709709 --typedefs
710710 isTypeEmmited = false
711711 function addType(m1,m2)
712712 local sm
713713 sm = ''
714714 if not isTypeEmmited then
715715 isTypeEmmited = true
716716 sm = '\ntype'
717717 end
718718 return sm .. '\n\t' .. m2 .. ' = ' .. m1 .. ';'
719719 end
720720 --pasbody = pasbody:gsub('\ntypedef%s+struct%s+([^%s]+)%s+([^;]+)', addType)
721721 --pasbody = pasbody:gsub('\ntypedef%s+([^%s]+)%s+([^;]-)', addType)
722722 pasbody = pasbody:gsub('\n[\t ]*typedef[\t ]+struct[\t ]+([%w_]+)[\t ]+([^;]+)', '\n%2 = %1')
723723 pasbody = pasbody:gsub('\n[\t ]*typedef[\t ]+struct%s+(%b{})%s+([%w_]+);',
724724 function(m1, m2) return m2 .. ' = record' .. trimChars(m1, '{', '}') .. 'end;' end)
725725
726726
727727 -- print the results
728728 print(pasbody)
729729```
730730
731731以下是转换后的 C 文件示例:
732732```
733733//uses lua, llimits, lmem, lstate, lzio;
734734 (*
735735 ** $Id: lzio.c,v 1.31.1.1 2007/12/27 13:02:25 roberto Exp $
736736 ** a generic input stream interface
737737 ** See Copyright Notice in lua.h
738738 *)
739739
740740
741741 //#include <string.h>
742742
743743 {$define lzio_c}
744744 {$define LUA_CORE}
745745
746746 //#include 'lua.h'
747747
748748 //#include 'llimits.h'
749749 //#include 'lmem.h'
750750 //#include 'lstate.h'
751751 //#include 'lzio.h'
752752
753753
754754
755755 function luaZ_fill(z : PTZIO):integer;
756756 var
757757 L : Plua_State;
758758 buff : Pchar;
759759 size : size_t;
760760 begin
761761 *L := z.L;
762762 lua_unlock(L);
763763 buff := z.reader(L, z.data, @size);
764764 lua_lock(L);
765765 if (buff = nil ) or ( size = 0) then exit(EOZ);
766766 z.n := size - 1;
767767 z.p := buff;
768768 exit(char2int(PosInc^((z.p))));
769769 end;
770770
771771
772772
773773 function luaZ_lookahead(z : PTZIO):integer;
774774 begin
775775 if (z.n = 0) then begin
776776 if (luaZ_fill(z) = EOZ)
777777 exit(EOZ);
778778 else begin
779779 PosInc(z.n); (* luaZ_fill removed first byte; put back it *)
780780 z-PosDec(>p);
781781 end;
782782 end;
783783 exit(char2int(z^.p));
784784 end;
785785
786786
787787
788788 procedure luaZ_init(L : Plua_State; z : PTZIO; reader : lua_Reader; data : Ppointer);
789789 begin
790790 z.L := L;
791791 z.reader := reader;
792792 z.data := data;
793793 z.n := 0;
794794 z.p := nil;
795795 end;
796796
797797
798798 (* --------------------------------------------------------------- read --- *)
799799
800800 function luaZ_read(z : PTZIO; b : Ppointer; n : size_t):size_t;
801801 var
802802 m : size_t;
803803 begin
804804 while (n) do begin
805805 if (luaZ_lookahead(z) = EOZ)
806806 exit(n); (* exit(number of missing bytes *)
807807 m = (n <= z.n) ? n : z.n); (* min. between n and z.n *)
808808 Move(b, z.p, m);
809809 z.n -= m;
810810 z.p += m;
811811 b := (char *)b + m;
812812 n -= m;
813813 end;
814814 exit(0);
815815 end;
816816
817817 (* ------------------------------------------------------------------------ *)
818818
819819 function luaZ_openspace(L : Plua_State; buff : PTMbuffer; n : size_t):Pchar;
820820 begin
821821 if (n > buff.buffsize) then begin
822822 if (n < LUA_MINBUFFER) then n := LUA_MINBUFFER;
823823 luaZ_resizebuffer(L, buff, n);
824824 end;
825825 exit(buff.buffer);
826826 end;
827827```
828828
829829# 人工将 C 头文件转换为 Pascal
830830
831831这时需要同时运用 C 和 Pascal 知识来制作头文件。请参阅以下示例学习转换过程。
832832
833833## 转换常量
834834
835835C:
836836```
837837#define SOME_CONSTANT 5
838838 #define ANOTHER_CONSTANT 6
839839```
840840
841841Pascal:
842842```
843843const
844844 SOME_CONSTANT = 5;
845845 ANOTHER_CONSTANT = 6;
846846```
847847
848848## 转换已声明的函数
849849
850850绝大部分普通函数可以转换如下:
851851
852852C:
853853```
854854int somekind_of_method(int* param_first, int* param_second)
855855 void another_method(char*)
856856```
857857
858858Pascal:
859859```
860860uses ctypes;
861861
862862 const
863863 MyLib = 'mylib';
864864
865865 function somekind_of_method(param_first, param_second: pcint): cint; external MyLib;
866866 procedure another_method_renamed(param1: PChar); external MyLib name 'another_method';
867867```
868868
869869首先请注意,应采用 ctypes 单元及其声明的类型来转换 C 头文件,不要直接采用 Pascal 基础类型。这样可以确保在所有平台上都能正确转换,因为 C 类型在不同架构和操作系统中可能会有不同的长度。
870870
871871其次请注意,基础类型的 C 指针可以转换为 ctypes 中声明的类型,例如 int* 是 C int 型的指针,所以可转换为 pcint 类型(指向 C 整数的指针)。C int 可以转换为 cint,并且在 Pascal 中可以简化同类型参数的声明:“param_first, param_second: pcint”。
872872
873873最后请注意,如果返回值为 **void** ,应该将函数替换成过程。
874874
875875[创建 C 到 Pascal 的对应关系](<https://github.com/williamhunter/pascal-bindings-for-c>) 是带有源代码的教程,详细介绍了如何对 C 代码库进行人工转换,处理各种数据类型(字符串、数组等)。该教程还演示了如何用 MinGW C 编译器创建静态(*.a)和共享/动态库(*.so 或 *.dll)。再结合使用 h2pas 即非常有效。
876876
877877请参阅参考手册中的[外部函数](<http://www.freepascal.org/docs-html/ref/refse85.html>)。
878878
879879# 在 Lazarus-CCR 或 Free Pascal 发布绑定代码
880880
881881绑定代码可提交至 Lazarus CCR 库(参见 [[Using the Lazarus-ccr SVN repository]]),以供其他人使用和维护。如果这些绑定代码可跨平台应用,还可能会纳入 FreePascal 发行版中。
882882
883883# 参阅
884884
885885 * 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>), 简述:如何在 Free Pascal 中使用 C 和 C++ 代码的简短教程,包括编写封装代码。
886886 * [另一教程](<https://github.com/williamhunter/pascal-bindings-for-c/blob/master/docs/Creating%20Pascal%20bindings%20for%20C%20\(v1.0\).pdf>)演示了如何使用面向对象的 C 语言代码和库。
887887 * [[Common problems when converting C header files|转换 C 语言头文件的常见问题]]
888888 * [[Pascal for C users|C 语言用户的 Pascal 说明]]
889889 * [[Lazarus/FPC Libraries]]
890890 * [[SWIG]]
891-
892891---
893892*From the Free Pascal wiki, page "Creating bindings for C libraries/zh CN", as the Internet Archive kept it on 2025-10-04, last changed on 2024-09-22. 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