Sign in Join
Wiki copytemporary

Creating bindings for C libraries/zh CN

English · Español · Français · Русский · 中文(中国) · 日本語

简介

本页介绍了如何为“C语言库”创建“Pascal绑定”项目。为了在Pascal中使用C语言库,需要为每个C函数、类型和变量创建Pascal转译代码。这可以用h2pas之类的自动化工具来实现,或者对头文件进行人工转换。

H2PAS

概述

H2Pas工具可以对很多常见C语言元素进行自动转译。Lazarus提供了图形用户界面(GUI),可用h2pas和其他工具将创建过程自动化。这会创建一个用于更新绑定项目的规则集。这样后续版本的C库转换起来将会更加容易。h2pas向导程序的一个好特性就是使用临时文件,这样C语言头文件就能保持不变。

工作步骤

安装工具

正常安装fpc都会自带h2pas工具。

在Lazarus IDE中安装h2paswizard软件包。打开“软件包->安装/卸载软件包”,从右侧列表选中H2PasWizard 并点击“安装选中的”,然后点击“保存并重新构建IDE”。 重启IDE,就会出现新的菜单项:工具 -> h2pas。

获取C语言头文件

C语言.h头文件描述了C程序库的接口,通常不会和C程序库一起提供。必须得有源代码或者程序库的开发包才行。例如gtk库的头文件就在gtk+-devel包中。

示例:MPICH2

从http://www-unix.mcs.anl.gov/mpi/mpich2/下载mpich2-1.0.3.tar.gz并解压。.h文件都在mpich2-1.0.3/src/include下。 关于FPC的MPICH,更多信息请参阅here。

创建工作目录并命名绑定项目

请用有意义的名字创建一个目录。目录名不能包含特殊字符,如空格、变音字符、句点和逗号。 将.h文件拷入目录。

示例:MPICH2

Pascal文件将存放于h2p目录。 .h文件将存放于h2p/c_sources目录。

pascal
mkdir -p h2p/c_sources
    cp mpich2-1.0.3/src/include/*.h h2p/c_sources/

用h2pas向导程序新建h2pas项目

从“工具 -> h2pas”打开向导程序。这会打开一个窗口。可以和其他IDE窗口之间来回切换。最近的2pas项目会自动加载。如要新建一个项目,请点击“设置 -> 新建/清除设置”。然后点击底部的“保存设置”按钮,选取文件名。

注意:如果是第一次使用h2pas向导程序,需用“设置”页确认h2pas.exe转换程序的位置。转换程序h2pas.exe位于FreePascal编译器fpc.exe所在的目录中,如果连同Lazarus下载了FPC,则会位于...\Lazarus\fpc_version_ \bin目录。请保存设置,下次就不必再设置了。

示例:MPICH2

请点击“设置 -> 新建/清除设置”。然后点击底部的“保存设置”按钮,保存为h2p/mpi2.h2p。

在h2pas项目中添加.h文件

在“C 语言头文件”页可以添加/删除.h文件。可以启用/禁用某些.h文件,以便只转换部分文件。

示例:MPICH2

点击“C 语言头文件 -> 添加 .h 文件 ...”,并选取“mpi.h”和“mpio.h”文件。他们会自动启用。选中mpi.h并点击“合并除此以外的所有内容”,这样向导程序就会将所有头文件并入一个单元文件(mpi.pas)

设置h2pas参数

在“H2Pas 选项”页,可以设置h2pas程序的参数。

示例:MPICH2

运行向导程序

点击底部的“运行H2Pas”按钮。 这会将<example>.h文件拷贝成临时文件<example>.tmp.h,并运行“H2Pas 之前”页列出的工具。然后会运行h2pas将<example>.tmp.h转换为<example>.inc或<example>.pas,或者别的什么在“设置”页设置的输出扩展名。然后对输出文件运行“H2Pas 之后”页列出的工具。

如果h2pas发现语法错误,将会用IDE打开example.tmp.h文件,并跳转到出错的行。通常h2pas只会报“syntax error”,非常宽泛。请参阅转换C语言头文件时的常见问题。

示例:MPICH2

h2pas向导程序已包含的工具足以转换该头文件的所有内容,因此这里不会报错。但单元文件还没创建好,请继续。

请切换到“H2Pas 之后”页,加入“Reduce compiler directives in pascal file”工具。

在 Undefines 属性中加入:

pascal
MPI_INCLUDED
     MPIO_INCLUDE
     NEEDS_MPI_FINT
     MPI_BUILD_PROFILING
     MPICH_SUPPRESS_PROTOTYPES
     MPI_Wtime
     HAVE_MPICH2G2
     FOO
     HAVE_PRAGMA_HP_SEC_DEF

在 Defines 属性中加入:

pascal
MPICH2
     MPICH_NEW_MPIIO

自行选用转换程序中的工具

使用“搜索和替换”工具

很多类似修改变量名之类的活儿都能用搜索和替换 工具完成。这通过“H2Pas 之前”或“H2Pas 之后”页中的'添加新工具'按钮就能加入。 然后设置SearchFor 、ReplaceWith 、 Options 和Caption 属性即可。

trtRegExpr选项

如果Options 中的trtRegExpr设为True ,则表示要使用正则表达式。这样就能利用元字符 和限定符 对源代码进行复杂的调整和美化。IDE regular expressions中给出了元字符 和限定符 的清单及示例。

示例:将标识符Tguint更名为guint

属性 值
Caption Rename Tguint to guint
SearchFor Tguint
ReplaceWith guint
Options [trtMatchCase,trtWholeWord]

示例:为多个标识符更名

Tguint更名为guint,Tgulong更名为gulong,Tgint更名为gint:

属性 值
Caption Rename Tguint to guint
SearchFor T(guint|gint|gulong)
ReplaceWith $1
Options [trtMatchCase,trtWholeWord,trtRegExpr]

示例:MPICH2

删除MPI_DUP_FN宏

MPI_DUP_FN 在mpi头文件中定义了两次。前一次是个空的宏定义,后一次是个实际函数。因此,第一次的定义应该删除。

在“H2Pas 之前”加入搜索和替换 工具(加到最后),并设置如下:

属性 值
Caption Remove dummy define MPI_DUP_FN
Name RemoveDummyDefineMPI_DUP_FN
SearchFor ^#define\s+MPI_DUP_FN\s+MPIR_Dup_fn
ReplaceWith
Options [trtMatchCase,trtRegExpr]

删除针对MPI_DUP_FN的类型转换

在“H2Pas 之前”加入搜索和替换 工具(加到最后),并设置如下:

属性 值
Caption Remove MPI_DUP_FN Makros
Name Remove_MPI_DUP_Func
SearchFor ^#define .MPI_DUP_FN\).$
ReplaceWith
Options [trtMatchCase,trtRegExpr]

删除 MPIO_Test、PMPIO_Test、MPIO_Wait 和 PMPIO_Wait 函数

MPIO_Test、PMPIO_Test、MPIO_Wait和PMPIO_Wait都定义了两次,同时定义了宏。因此函数定义应该删除:

在“H2Pas 之前”加入搜索和替换 工具(加到最后),并设置如下:

属性 值
Caption Remove Func P?MPIO_(Test|Wait)
Name RemoveFuncP_MPIO_Test_Wait
SearchFor ^int P?MPIO_(Test|Wait).*$
ReplaceWith
Options [trtMatchCase,trtRegExpr]

改进已有转换工具

发现缺陷后想要修复,或者扩展上述工具。很好!

大部分转换工具的代码都在 h2paswizard 包的 h2pasconvert 单元中。 转换工具程序至少需要类名、说明和Execute 方法。 若要脱离 IDE 调试代码并节省大量的编译时间,请参阅项目 components/simpleideintf/examples/testh2pastool.lpi。 请编译后在控制台/终端中启动,并将 .h 文件名作为第一个命令行参数。例如:

pascal
./testh2pastool files/h2pastest.pas

编写自定义转换工具

自行编写转换工具并在 IDE 中注册,也都很容易。 启动一个软件包并加入新的单元文件(比如 unit1),定义一个类。已有工具的代码可以作为示例,请参阅前文。 新的转换工具编写完成并用上述 simpleideintf 项目测试通过后,即可在 IDE 中注册了: 在单元文件(unit1)中添加 register 过程,类似以下代码(伪代码):

pascal
uses
      Classes, ..., IDETextConverter;
    
    type
      TYourToolClass = class(TCustomTextConverterTool)
      public
        class function ClassDescription: string; override;
        function Execute(aText: TIDETextConverter): TModalResult; override;
      end;
    
    procedure Register;
    
    implementation
    
    procedure Register;
    begin
      TextConverterToolClasses.RegisterClass(TYourToolClass);
    end;

别忘了在软件包编辑器中为对应单元勾选 register ,否则 IDE 不会去调用 Register 过程。 然后在 IDE 中安装此软件包。

未来任务/未尽事宜

C:

pascal
typedef struct page
    {
       u32int present    : 1;   // Page present in memory
       u32int rw         : 1;   // Read-only if clear, readwrite if set
       u32int user       : 1;   // Supervisor level only if clear
       u32int accessed   : 1;   // Has the page been accessed since last refresh?
       u32int dirty      : 1;   // Has the page been written to since last refresh?
       u32int unused     : 7;   // Amalgamation of unused and reserved bits
       u32int frame      : 20;  // Frame address (shifted right 12 bits)
    } page_t;

Pascal:

pascal
type
        Unsigned_7  = 0 .. (1 shl 7)  - 1;
        Unsigned_20 = 0 .. (1 shl 20) - 1;
    
    type
        page_t = bitpacked record
           present  : boolean;
           rw       : boolean;
           user     : boolean;
           accessed : boolean;
           dirty    : boolean;
           unused   : Unsigned_7;
           frame    : Unsigned_20;
        end;

用 Lua 脚本将 C 转换为 Pascal

我对 Lua 脚本语言的强大功能印象深刻,很容易去完成一些有趣的事情。

pascal
--
    -- Please send any improvement to mingodadATgmailDOTcom.
    --
    
    inFileName = arg[1] or 'g:\\tmp\\plua\\clua\\lvm.c'
    
    fh = assert(io.open(inFileName))
    cbody = fh:read('*a')
    fh:close()
    
    pasbody = cbody
    
    function split(str, pat)
       local t = {}  -- NOTE: use {n = 0} in Lua-5.0
       local fpat = '(.-)' .. pat
       local last_end = 1
       local s, e, cap = str:find(fpat, 1)
       while s do
          if s ~= 1 or cap ~= '' then
        table.insert(t, cap)
          end
          last_end = e+1
          s, e, cap = str:find(fpat, last_end)
       end
       if last_end <= #str then
          cap = str:sub(last_end)
          table.insert(t, cap)
       end
       return t
    end
    
    function trim(s)
      -- from PiL2 20.4
      return (s:gsub('^%s*(.-)%s*$', '%1'))
    end
    
    function trimChars(str, c1, c2)
        return str:gsub('^%' .. c1 .. '*(.-)%' .. c2 .. '*$', '%1')
    end
    
    function getPascalType(cType)
        if cType == 'void' then
            return 'pointer'
        elseif cType == 'int' then
            return 'integer'
        elseif cType == 'short' then
            return 'Shortint'
        elseif cType == 'define' then
            return 'is_define'
        else
            return cType
        end
    end
    
    
    --check var name and type
    --if var starts with a capital letter
    -- add 'T' informt of it
    --if var is preceded by * then add a 'P' for type
    
    function checkVarType(aType, aVar)
        aType = getPascalType(aType)
        aType = aType:gsub('^(%u)', 'T%1')
        aVar = aVar:gsub('^(%*)', function(m)
                aType = 'P' .. aType
                return ''
            end)
        aVar = aVar:gsub('(%[[^]]+%])', function(m)
                aType = 'array' .. m .. ' of ' .. aType
                return ''
            end)
        return aType, aVar
    end
    
    function varDeclaration(aType, aVar)
        aType, aVar = checkVarType(aType, aVar)
        return aVar .. ' : ' .. aType
    end
    
    function checkDeclarationOrGoto(m1, m2, m3)
        if m2 ~= 'goto' then
            return '\n' .. m1 .. varDeclaration(m2, m3) .. ';'
        else
            return '\n' .. m1 .. m2 .. ' ' .. m3 .. ';'
        end
    end
    
    function addProcFunc(mtype, mname, mparams, mbody)
        local str, isFunc, rType, k, v
        if mtype == 'void' then
            str = '\nprocedure '
            isFunc = false
        else
            str = '\nfunction '
            isFunc = true
        end
        mtype, mname = checkVarType(mtype, mname)
        local vparams = split(trimChars(mparams, '(',')'), ',')
        str = str .. mname .. '('
    
        local tparams = {}
        for k, v in pairs(vparams) do
            local vparam = split(trim(v), ' ')
            if #vparam > 2 then
                table.insert(tparams, vparam[1] .. ' ' .. varDeclaration(vparam[2], vparam[3]))
            else
                --print(vparam[1], vparam[2])
                table.insert(tparams, varDeclaration(vparam[1], vparam[2] or 'is_define'))
            end
        end
    
        str = str .. table.concat(tparams, '; ') .. ')'
        if isFunc then
            if mtype == 'int' then
                rType = 'integer'
            else
                rType = mtype
            end
            str = str .. ':' .. rType
        end
    
        if mbody then
            local tblLabels = {}
            for v in mbody:gmatch('\n[\t ]*([%w_]+)[\t ]*:[^=]') do
                if v ~= 'default' then
                    table.insert(tblLabels, v)
                end
            end
    
            if #tblLabels > 0 then
                str = str .. ';\nlabel\n  '.. table.concat(tblLabels, ',  ')
            end
    
            local declarations = {}
    
            mbody = mbody:gsub('(\n[\t ]*)const[\t ]+([%w_]+)[\t ]+(%*?[%w_]+)[\t ]*:%=[\t ]*([^;\n]+;)','%1 %2 %3 := %4')
    
            mbody = mbody:gsub('(\n[\t ]*)([%w_]+)[\t ]+(%*?[%w_]+)[\t ]*:%=[\t ]*([^;\n]+;)', function(m1, m2, m3, m4)
                table.insert(declarations, varDeclaration(m2, m3) .. ';' )
                return m1 .. m3 .. ' := ' .. m4
            end)
    
            mbody = mbody:gsub('\n[\t ]*const[\t ]*([%w_]+)[\t ]+(%*?[%w_]+)[\t ]*;', function(m1,m2)
                    table.insert(declarations, varDeclaration(m1, m2) .. ';')
                    return ''
                end)
    
            mbody = mbody:gsub('\n([\t ]*)([%w_]+)[\t ]+(%*?[%w_ ,]+);', function(m1,m2,m3)
                    if m2 ~= 'goto' then
                        table.insert(declarations, varDeclaration(m2, m3) .. ';')
                        return ''
                    else
                        return '\n' .. m1 .. m2 .. ' ' .. m3 .. ';'
                    end
                end)
    
    
            if #declarations > 0 then
                local unique_decl = {}
                table.sort(declarations)
                local lastV = ''
                local k2, v2
                for k2,v2 in pairs(declarations) do
                    if v2 ~= lastV then
                        lastV = v2
                        table.insert(unique_decl, v2)
                    end
                end
                str = str .. ';\nvar\n  '.. table.concat(unique_decl, '\n  ')  .. '\n'
            else
                str = str .. ';\n'
            end
    
            str = str .. 'begin' .. trimChars(mbody, '{','}') .. 'end'
        end
        return str .. ';'
    end
    
    --*************************************
    --
    -- Order of substitutions are important
    --
    --*************************************
    
    --=assignements statements
        pasbody = pasbody:gsub("([\t ]*)([%w_]+)[\t ]*=[\t ]*([^=;\n]+);", '%1%2 := %3;')
    
    --return
        pasbody = pasbody:gsub('([\t ]*)return[\t ]+([^;]+);', '%1exit(%2);')
    
    --NULL
        pasbody = pasbody:gsub('([^%w]+)NULL([^%w]+)', '%1nil%2')
    
    --void functions to procedure declaratios
        pasbody = pasbody:gsub('([%w_*]+)[\t ]+([%w_*]+)[\t ]*(%b())[%s]*(%b{})', addProcFunc)
        --pasbody = pasbody:gsub('\n([%w_]+)%s+([^%s]+)%s*(%b());', addProcFunc)
    
    --const/static
        tblLeaveCommented = {
            'const', 'static'
        }
        for k,v in pairs(tblLeaveCommented) do
            pasbody = pasbody:gsub('([\t ]*)(' .. v .. ')([\t ]*)', '%1{%2}%3')
        end
    
    --defines to const
        isConstEmmited = false
        function addConst(m1,m2)
            local sm
            sm = ''
            if not isConstEmmited then
                isConstEmmited = true
                sm = '\nconst'
            end
            return sm .. '\n\t' .. m1 .. ' = ' .. m2 .. ';'
        end
    
        isConstEmmited = false
        pasbody = pasbody:gsub('\n#define%s+([^%s]+)%s+(%b())', '\nconst %1 = %2;')
        isConstEmmited = false
        pasbody = pasbody:gsub('\n#define%s+([^%s]+)[ \t]+([^%s]+)', '\nconst %1 = %2;')
    
    --includes
        tblUses = {}
        pasbody = pasbody:gsub('\n#[ \t]*(include)%s+([^\n]+)', function(m1,m2)
                for w in m2:gmatch('"([^.]+)%..+"') do
                    table.insert(tblUses, w)
                end
                return '\n//#' .. m1 .. ' ' .. m2
            end)
        pasbody = '//uses ' .. table.concat(tblUses, ', ') .. ';\n' .. pasbody
    
    --defines to compiler directives
        pasbody = pasbody:gsub('\n#([^\n]+)', '\n{$%1}')
    
    --comments
        pasbody = pasbody:gsub('\n#', '\n//#')
        pasbody = pasbody:gsub('/%*', '(*')
        pasbody = pasbody:gsub('%*/', '*)')
    
    --declarations
        pasbody = pasbody:gsub('\n([\t ]*)([%w_]+)[\t ]+([%w_ ,]+);', checkDeclarationOrGoto)
    
    --structs
        function parseStruct(m1, m2)
            return '\n' .. m1 .. ' = record\n' .. trimChars(m2, '{', '}') .. '\nend'
        end
        pasbody = pasbody:gsub('\n[\t ]*struct[\t ]+([%w_]+)[\t ]*(%b{})', parseStruct)
    
    --if statements
        parseStatementCalled = false
    
        function parseStatement(mSpace, mStatement, mCond, mBody)
            parseStatementCalled = true
            local vStatement
            if mStatement == 'if' then
                vStatement = mStatement .. ' ' .. mCond .. ' then'
            elseif mStatement == 'else' then
                return mSpace .. mStatement .. ' begin' .. trimChars(mCond, '{', '}') .. ' end;'
            elseif (mStatement == 'while') then
                vStatement = mStatement .. ' ' .. mCond .. ' do'
            elseif (mStatement == 'for') then
                vStatement = mStatement .. ' ' .. mCond .. ' do'
                --local forArgs = split(trimChars(mCond, '(', ')'), ';')
                --return mSpace .. (forArgs[1] or '') .. ';' .. mSpace ..'while (' ..
                --	(forArgs[2] or 'true') .. ') do' .. mSpace .. 'begin' .. mSpace .. trimChars(mBody, '{', '}') .. '\n' .. mSpace ..
                --	(forArgs[3] or '') .. ';\n' .. mSpace .. 'end;'
            else
                vStatement = mStatement .. ' ' .. mCond .. '\n'
            end
            return mSpace .. vStatement .. ' begin' ..  trimChars(mBody, '{', '}') .. 'end;'
        end
    
        -- for nested blocks
        parseStatementCalled = true
        while(parseStatementCalled) do
            parseStatementCalled = false
            pasbody = pasbody:gsub('([^%w_])(if)[\t ]*(%b())[\t ]*(%b{})', parseStatement)
        end
    
        -- else
        pasbody = pasbody:gsub('([\t ]+)(else)[\t ]*(%b{})', parseStatement)
    
        pasbody = pasbody:gsub("([^%w_])if[\t ]*(%b())[\t ]*([^;\n]+);", '%1if %2 then %3;')
    
    --?: short if
        pasbody = pasbody:gsub("([^?])?([^:;\n]):[\t ]*([^%s;]+);", ' iff(%1, %2, %3) ')
    
    --while loop
        -- for nested blocks
        parseStatementCalled = true
        while(parseStatementCalled) do
            parseStatementCalled = false
            pasbody = pasbody:gsub('([^%w_])(while)[\t ]*(%b())[\t ]*(%b{})', parseStatement)
        end
    
    --for loop
        -- for nested blocks
        parseStatementCalled = true
        while(parseStatementCalled) do
            parseStatementCalled = false
            pasbody = pasbody:gsub('([^%w_])(for)[\t ]*(%b())%s*(%b{})', parseStatement)
        end
    
    --do/while
        -- for nested blocks
        parseStatementCalled = true
        while(parseStatementCalled) do
            parseStatementCalled = false
            pasbody = pasbody:gsub("([^%w_])do[\t ]*(%b{})[\t ]*while([^;\n]+);",
                function(m1, m2, m3)
                    parseStatementCalled = true
                    return m1 .. 'repeat ' .. trimChars(m2, '{', '}') .. 'until not' .. m3
                end)
        end
    
    
    --switch/case statements
    
        --begin/end enclose blocks around
        -- for nested blocks
        parseStatementCalled = true
        while(parseStatementCalled) do
            parseStatementCalled = false
            pasbody = pasbody:gsub("([^%w_]case[\t ]+[^:]+)[\t ]*:[%s]*(%b{})",
                function(m1, m2, m3)
                    parseStatementCalled = true
                    return m1 .. ': begin' .. trimChars(m2, '{', '}') .. 'end;'
                end)
            pasbody = pasbody:gsub("([^%w_]default[\t ]*):[%s]*(%b{})",
                function(m1, m2, m3)
                    parseStatementCalled = true
                    return m1 .. ': begin' .. trimChars(m2, '{', '}') .. 'end;'
                end)
        end
    
        --breaks remove
        pasbody = pasbody:gsub("([^%w_]case[\t ]+[^:]+)[\t ]*:[%s]*(.-)break;", '%1: %2')
    
        --case
        pasbody = pasbody:gsub("([^%w_])case[\t ]+([^:]+):", '%1%2:')
    
        -- for nested blocks
        parseStatementCalled = true
        while(parseStatementCalled) do
            parseStatementCalled = false
            pasbody = pasbody:gsub("([^%w_])switch[\t ]*(%b())[\t ]*(%b{})",
                function(m1, m2, m3)
                    parseStatementCalled = true
                    return m1 .. 'case ' .. m2 .. ' of ' .. trimChars(m3, '{', '}') .. 'end;'
                end)
        end
    
        --pasbody = pasbody:gsub("([\t ]*)case[\t ]+('.')[\t ]*:\n", '%1%2:\n')
        --pasbody = pasbody:gsub("([\t ]*)case[\t ]+(%d+)[\t ]*:\n", '%1%2:\n')
        --pasbody = pasbody:gsub("([\t ]*)(case)[\t ]+('.')[\t ]*:[\t ]*(%b{})", '%1%2 : %3')
    
    --pre/pos increments
        -- (n)++ -> PosInc(n)
        pasbody = pasbody:gsub("(%([^%s+]+%))%+%+", 'PosInc%1')
        -- n*++ -> PosInc(n*)
        pasbody = pasbody:gsub("([^%s*+]+)%+%+", 'PosInc(%1)')
        -- ++(n) -> PreInc(n)
        pasbody = pasbody:gsub("%+%+(%([^%s;+]+%))", 'PreInc%1')
        -- ++*n -> PreInc(n)
        pasbody = pasbody:gsub("%+%+([^%s*;+]+)", 'PreInc(%1)')
    
        -- (n)--
        pasbody = pasbody:gsub("(%([^%s-]+%))%-%-", 'PosDec%1')
        -- n*--
        pasbody = pasbody:gsub("([^%s*-]+)%-%-", 'PosDec(%1)')
        -- --(n)
        pasbody = pasbody:gsub("%-%-(%([^%s;-]+%))", 'PreDec%1')
        -- --n
        pasbody = pasbody:gsub("%-%-([^%s;-]+)", 'PreDec(%1)')
    
    --boolean operators
        tblBooleanOperators = {
            {'||', 'or'},
            {'&&', 'and'},
        }
        for k,v in pairs(tblBooleanOperators) do
            pasbody = pasbody:gsub('([\t ]*)' .. v[1] .. '([\t ]*)', '%1) ' .. v[2] .. ' (%2')
        end
    
    
    --pointers
        pasbody = pasbody:gsub("%->", '.')
        pasbody = pasbody:gsub("%(%*([%w_]+)", '(%1^')
        pasbody = pasbody:gsub("%*%*([%w_]+)", '%1^^')
        pasbody = pasbody:gsub("&([%w_]+)", '@%1')
        pasbody = pasbody:gsub("%*([%w_]+)[\t ]*:[\t ]*([%w_]+);", '%1 : ^%2;')
        pasbody = pasbody:gsub("%^char", 'pchar')
        --pasbody = pasbody:gsub("%*([%w_]+)[\t ]*:=[\t ]*([^;\n]+);", '%1^ := %2;')
    
    --bit operators
        tblBitOperators = {
            {'<<', 'shl'},
            {'>>', 'shr'},
            {'|', 'or'},
            {'&', 'and'},
            {'~', 'not'},
        }
    
        for k,v in pairs(tblBitOperators) do
            pasbody = pasbody:gsub('([\t ]*)' .. v[1] .. '([\t ]*)', '%1 ' .. v[2] .. ' %2')
        end
    
    --==eq operator
        pasbody = pasbody:gsub("([\t ]*)%=%=([\t ]*)", '%1 = %2')
    
    --!= not eq operator
        pasbody = pasbody:gsub("([\t ]*)!%=([\t ]*)", '%1 <> %2')
    
    --! unary
        pasbody = pasbody:gsub('([\t ]*)!([\t ]*)', '%1 not %2')
    
    --blocks
        --pasbody = pasbody:gsub("(%b{})", function(m1) return 'begin' ..  trimChars(m1, '{', '}') .. 'end;' end)
        --pasbody = pasbody:gsub("(%b{})", function(m1) return 'begin' ..  trimChars(m1, '{', '}') .. 'end;' end)
    
    --strings and escaped characters
        tblEscapedCahrs = {
            {'\\"', '"'},
            {'\\\\', '\\'},
            {'\\t', "'#9"},
            {'\\n', "'#10'"},
            {'\\f', "'#12'"},
        }
        pasbody = pasbody:gsub('%b""', function(m1)
                return "'" .. trimChars(m1:gsub("'", "''") , '"', '"')  .. "'"
            end)
        for k,v in pairs(tblEscapedCahrs) do
            pasbody = pasbody:gsub(v[1], v[2])
        end
    
    --common functions/procedure
        tblOrigFuncNewFunc = {
            {'printf', 'format'},
            {'malloc', 'GetMem'},
            {'free', 'Dispose'},
            {'memcpy', 'Move'},
        }
    
        for k,v in pairs(tblOrigFuncNewFunc) do
            pasbody = pasbody:gsub('([\t ]+)' .. v[1] .. '%(', '%1' .. v[2] .. '(')
        end
    
    --typedefs
        isTypeEmmited = false
        function addType(m1,m2)
            local sm
            sm = ''
            if not isTypeEmmited then
                isTypeEmmited = true
                sm = '\ntype'
            end
            return sm .. '\n\t' .. m2 .. ' = ' .. m1 .. ';'
        end
        --pasbody = pasbody:gsub('\ntypedef%s+struct%s+([^%s]+)%s+([^;]+)', addType)
        --pasbody = pasbody:gsub('\ntypedef%s+([^%s]+)%s+([^;]-)', addType)
        pasbody = pasbody:gsub('\n[\t ]*typedef[\t ]+struct[\t ]+([%w_]+)[\t ]+([^;]+)', '\n%2 = %1')
        pasbody = pasbody:gsub('\n[\t ]*typedef[\t ]+struct%s+(%b{})%s+([%w_]+);',
            function(m1, m2) return m2 .. ' = record' .. trimChars(m1, '{', '}') .. 'end;' end)
    
    
    -- print the results
    print(pasbody)

以下是转换后的 C 文件示例:

pascal
//uses lua, llimits, lmem, lstate, lzio;
    (*
    ** $Id: lzio.c,v 1.31.1.1 2007/12/27 13:02:25 roberto Exp $
    ** a generic input stream interface
    ** See Copyright Notice in lua.h
    *)
    
    
    //#include <string.h>
    
    {$define lzio_c}
    {$define LUA_CORE}
    
    //#include 'lua.h'
    
    //#include 'llimits.h'
    //#include 'lmem.h'
    //#include 'lstate.h'
    //#include 'lzio.h'
    
    
    
    function luaZ_fill(z : PTZIO):integer;
    var
      L : Plua_State;
      buff : Pchar;
      size : size_t;
    begin
      *L := z.L;
      lua_unlock(L);
      buff := z.reader(L, z.data, @size);
      lua_lock(L);
      if (buff  =  nil ) or ( size  =  0) then exit(EOZ);
      z.n := size - 1;
      z.p := buff;
      exit(char2int(PosInc^((z.p))));
    end;
    
    
    
    function luaZ_lookahead(z : PTZIO):integer;
    begin
      if (z.n  =  0) then begin
        if (luaZ_fill(z)  =  EOZ)
          exit(EOZ);
        else begin
          PosInc(z.n);  (* luaZ_fill removed first byte; put back it *)
          z-PosDec(>p);
         end;
      end;
      exit(char2int(z^.p));
    end;
    
    
    
    procedure luaZ_init(L : Plua_State; z : PTZIO; reader : lua_Reader; data : Ppointer);
    begin
      z.L := L;
      z.reader := reader;
      z.data := data;
      z.n := 0;
      z.p := nil;
    end;
    
    
    (* --------------------------------------------------------------- read --- *)
    
    function luaZ_read(z : PTZIO; b : Ppointer; n : size_t):size_t;
    var
      m : size_t;
    begin
      while (n) do begin
        if (luaZ_lookahead(z)  =  EOZ)
          exit(n);  (* exit(number of missing bytes *)
        m = (n <= z.n) ? n : z.n);  (* min. between n and z.n *)
        Move(b, z.p, m);
        z.n -= m;
        z.p += m;
        b := (char *)b + m;
        n -= m;
      end;
      exit(0);
    end;
    
    (* ------------------------------------------------------------------------ *)
    
    function luaZ_openspace(L : Plua_State; buff : PTMbuffer; n : size_t):Pchar;
    begin
      if (n > buff.buffsize) then begin
        if (n < LUA_MINBUFFER) then n := LUA_MINBUFFER;
        luaZ_resizebuffer(L, buff, n);
      end;
      exit(buff.buffer);
    end;

人工将 C 头文件转换为 Pascal

这时需要同时运用 C 和 Pascal 知识来制作头文件。请参阅以下示例学习转换过程。

转换常量

C:

pascal
#define SOME_CONSTANT 5
    #define ANOTHER_CONSTANT 6

Pascal:

pascal
const
      SOME_CONSTANT = 5;
      ANOTHER_CONSTANT = 6;

转换已声明的函数

绝大部分普通函数可以转换如下:

C:

pascal
int somekind_of_method(int* param_first, int* param_second)
    void another_method(char*)

Pascal:

pascal
uses ctypes;
    
    const
      MyLib = 'mylib';
    
    function somekind_of_method(param_first, param_second: pcint): cint; external MyLib;
    procedure another_method_renamed(param1: PChar); external MyLib name 'another_method';

首先请注意,应采用 ctypes 单元及其声明的类型来转换 C 头文件,不要直接采用 Pascal 基础类型。这样可以确保在所有平台上都能正确转换,因为 C 类型在不同架构和操作系统中可能会有不同的长度。

其次请注意,基础类型的 C 指针可以转换为 ctypes 中声明的类型,例如 int* 是 C int 型的指针,所以可转换为 pcint 类型(指向 C 整数的指针)。C int 可以转换为 cint,并且在 Pascal 中可以简化同类型参数的声明:“param_first, param_second: pcint”。

最后请注意,如果返回值为 void ,应该将函数替换成过程。

创建 C 到 Pascal 的对应关系 是带有源代码的教程,详细介绍了如何对 C 代码库进行人工转换,处理各种数据类型(字符串、数组等)。该教程还演示了如何用 MinGW C 编译器创建静态(.a)和共享/动态库(.so 或 *.dll)。再结合使用 h2pas 即非常有效。

请参阅参考手册中的外部函数。

在 Lazarus-CCR 或 Free Pascal 发布绑定代码

绑定代码可提交至 Lazarus CCR 库(参见 Using the Lazarus-ccr SVN repository),以供其他人使用和维护。如果这些绑定代码可跨平台应用,还可能会纳入 FreePascal 发行版中。

参阅


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.

Revision 3, last changed by WikiArchive .

Talk about this page

Nobody has opened a bench for this page yet. Sign in to open one.