0

BackGround

In fact, I am porting FLTK C 1.3.3 for FreeBASIC to nimlang. Please note FLTK C 1.3.3 for FreeBASIC is a C interface upon the FLTK in CPP.

Many functions in DLL meet the same similar name format, for example

#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

and it seems that the delcared function/sub will load the function of same name in fltk-c-1.3.3-64.dll automatically( correct me if am wrong)

Solution in FreeBASIC

So in the FreeBASIC header fltk-main.bi, there is an aided macro

#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

by the help of which, the above code( and many other codes) can be generated by a simple one line code:

DeclareEx(Fl_Button)
DeclareEx(Fl_Box)

My question in nimlang

In nimlang( please ignore the number type conversion for the time being), the above code can be translated by hand as

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.}

So I try to mimic what the FreeBASIC Macro does as

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)

But when I compile it, I get

d.nim(9, 10) template/generic instantiation of DeclareEx from here

d.nim(6, 118) Error: invalid pragma: importc: "name New"

So, any solution? Thanks

Community
  • 1
  • 1
oyster
  • 537
  • 3
  • 15

2 Answers2

2

The backticks interpolation offered in templates can be used only in positions where identifiers are expected. The importc pragma expects a constant string expression. You can use the astToStr magic to convert any AST input to its corresponding string representation and this is the key to the solution here:

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)
zah
  • 5,314
  • 1
  • 34
  • 31
  • thanks but I can't understand `importc: "x"` the part. How it can be translated into, for example, `importc: "Fl_BoxExNew"` – oyster Feb 09 '19 at 14:02
  • 1
    Ah, I've missed to make the final modifications to the code. The idea is that I'm passing the new constants to `importc`. The example is fixed now. – zah Feb 09 '19 at 14:11
  • when I use this template more than one times, for example, `DeclareEx(Fl_Button)`, `DeclareEx(Fl_Table)`, I get `Error: redefinition of 'newProc'; previous declaration here: a.nim(6, 5)`. So not-exported `newProc` does not behaves like a local variant in template. – oyster Feb 10 '19 at 04:24
  • This can be fixed by writing "proc `name ExNew`*(x: long, y: long, w: long, h: long, title: cstring=nil): ptr `name Ex` {.cdecl, importc: astToStr(name) & "ExNew", dynlib: fltk, discardable.}"; but I prefer to the way in your post(i.e. define the string first before using it, which makes the code clear/short to read), but how can we make `newProc` a local variant? Thanks – oyster Feb 10 '19 at 04:24
  • This is because the template is marked as `dirty` (see [hygiene in templates](https://nim-lang.org/docs/manual.html#templates-hygiene-in-templates) for an explanation). The issue is solved by marking the constants with the `gensym` pragma. – zah Feb 11 '19 at 13:18
  • sorry but in your code, when I try to make tons of widgets, for example, `DeclareEx(Fl_Button)` then `DeclareEx(Fl_Windows)`, I get `a.nim(6, 5) Error: redefinition of 'newProc'; previous declaration here: a.nim(6, 5)`. I checked https://nim-lang.org/docs/manual.html#templates-hygiene-in-templates which says "inject and gensym have no effect in dirty templates" – oyster Feb 15 '19 at 04:24
  • OK, I've modified the code once more to use a regular template instead of a dirty one. – zah Feb 16 '19 at 08:08
0

The importc pragma is used to import a proc or variable from C. However, you are writing importc: "name New", and name New is not a valid identifier in C because it has a space in it. You would need to try to concatenate the symbolic name of the variable you are passing into the template with that additional string suffix.

I don't know how you can do that in a template, though. Maybe you could pass a string as parameter and then use the & operator to concatenate it with the suffix for importc.

marc_s
  • 732,580
  • 175
  • 1,330
  • 1,459
Grzegorz Adam Hankiewicz
  • 7,349
  • 1
  • 36
  • 78