使用模板/宏在nimlang中批量创建C接口?

时间:2019-01-26 08:32:25

标签: templates dll macros nim

BackGround

实际上,我正在将FLTK C 1.3.3 for FreeBASIC移植到nimlang。请注意,FLTK C 1.3.3 for FreeBASICFLTK in CPP上的 C 界面。

例如,DLL中的许多功能都具有相同的相似名称格式

#inclib "fltk-c-1.3.3-64" ' Windows 64-bit

function Fl_ButtonExNew (byval x as long, byval y as long, byval w as long, byval h as long, byval title as const zstring ptr=0) as Fl_ButtonEx Ptr
sub Fl_ButtonExDelete(byval x as Fl_ButtonEx ptr)

function Fl_BoxExNew (byval x as long, byval y as long, byval w as long, byval h as long, byval title as const zstring ptr=0) as Fl_BoxEx Ptr
sub Fl_BoxExDelete(byval x as Fl_BoxEx ptr)

and so on

似乎delcared函数/ sub会自动将相同名称的函数加载到fltk-c-1.3.3-64.dll中(如果出错,请纠正我)

FreeBASIC中的解决方案

因此,在FreeBASIC标头fltk-main.bi中,有一个辅助宏

#macro DeclareEx(_name_)
declare function _name_##ExNew(byval x as long, byval y as long, byval w as long, byval h as long, byval title as const zstring ptr=0) as _name_##Ex ptr
declare sub      _name_##ExDelete         (byref ex as _name_##Ex ptr)
#endmacro

借助上述帮助,上述代码(以及许多其他代码)可以通过一个简单的单行代码生成:

DeclareEx(Fl_Button)
DeclareEx(Fl_Box)

我在nimlang的问题

在nimlang中(请暂时忽略数字类型的转换),上面的代码可以手动翻译为

const fltk = "fltk-c-1.3.3-64.dll"
type long = int64
proc Fl_ButtonExNew (x: long, y: long, w: long, h: long, title: cstring=nil): Ptr Fl_ButtonEx {.cdecl, importc: "Fl_ButtonExNew", dynlib: fltk, discardable.}
proc Fl_ButtonExDelete(x: ptr Fl_ButtonEx) {.cdecl, importc: "Fl_ButtonExDelete", dynlib: fltk, discardable.}

proc Fl_BoxExNew (x: long, y: long, w: long, h: long, title: cstring=nil): Ptr Fl_BoxEx {.cdecl, importc: "Fl_BoxExNew", dynlib: fltk, discardable.}
proc Fl_BoxExDelete(x: ptr Fl_BoxEx) {.cdecl, importc: "Fl_BoxExDelete", dynlib: fltk, discardable.}

所以我尝试模仿FreeBASIC宏的作用

const fltk = "fltk-c-1.3.3-64.dll"
type long = int64

template DeclareEx*(name: untyped) {.dirty.}=
    type `name Ex` = object
    type `name ExNew`* = proc(x: long, y: long, w: long, h: long, title: cstring=nil): ptr `name Ex` {.cdecl, importc: "name New", dynlib: fltk, discardable.}
    type `name ExDelete`*  = proc(ex: ptr `name Ex`) {.cdecl, importc: "name ExDelete", dynlib: fltk, discardable.}

DeclareEx(Fl_Button)

但是当我编译它时,我得到了

  

d.nim(9,10)模板/ DeclareEx的通用实例

     

d.nim(6,118)错误:无效的编译指示:importc:“名称为新”

那么,有什么解决办法吗?谢谢

2 个答案:

答案 0 :(得分:2)

模板中提供的反引号插值只能在需要标识符的位置使用。 importc编译指示需要一个常量字符串表达式。您可以使用astToStr魔术将任何AST输入转换为其相应的字符串表示形式,这是此处解决方案的关键:

const fltk = "fltk-c-1.3.3-64.dll"
type long = int64

template DeclareEx*(name: untyped) =
  const
    newProc = astToStr(name) & "New"
    deleteProc = astToStr(name) & "ExDelete"

  type `name Ex`* {.inject.} = object
  proc `name ExNew`*(x: long, y: long, w: long, h: long, title: cstring=nil): ptr `name Ex` {.cdecl, inject, importc: newProc, dynlib: fltk, discardable.}
  proc `name ExDelete`* (ex: ptr `name Ex`) {.cdecl, inject, importc: deleteProc, dynlib: fltk, discardable.}

DeclareEx(Fl_Button)
DeclareEx(Fl_Window)

var btn: ptr Fl_ButtonEx
Fl_ButtonExDelete(btn)

答案 1 :(得分:0)

importc pragma用于从C导入proc或变量。但是,您正在编写importc: "name New",并且name New在C中不是有效的标识符,因为它在C中有空格它。您需要尝试使用该附加字符串sufix连接要传递到模板中的变量的符号名称。

不过,我不知道如何在模板中做到这一点。也许您可以将字符串作为参数传递,然后使用&运算符将其与后缀importc串联起来。