Skip to content

Instantly share code, notes, and snippets.

@Varriount
Created January 19, 2014 04:30
Show Gist options
  • Save Varriount/8500498 to your computer and use it in GitHub Desktop.
Save Varriount/8500498 to your computer and use it in GitHub Desktop.
A nimrod pragma macro which generates helper procs
import macros
from winlean import useWinUnicode
when useWinUnicode:
type winString = WideCString
let apiPrefix = "W"
else:
type winString = CString
let apiPrefix = "A"
macro WinImport*(baseProc: stmt): stmt {.immediate.} =
## A pragma meant to be used with procedures that wrap the Windows API
## Given a base procedure without a 'W' or 'A' suffix, generates two more
## Procedures:
## - A conversion/wrapper procedure with the same name,
## except that all WinString arguments are changed to string arguments,
## which are automatically converted to the WinString type as needed.
## - A raw procedure with the same behavior as the original, except that
## the name has a 'W' or 'A' suffix, depending on whether unicode or
## ansi api's are used.
# TODO - Return type conversion
# TODO - Fix use of implicit return
# TODO - Get rid of paramsIdent?
result = newStmtList()
var apiName: string
case baseProc.name.kind
of nnkPostfix:
apiName = $baseProc.name[1] & apiPrefix
else:
apiName = $baseProc.name & apiPrefix
# Calculate the new importc pragma
var cImportPragma = newNimNode(nnkExprColonExpr)
cImportPragma.add(newIdentNode("importc"), newStrLitNode(apiName))
baseProc.pragma.add(cImportPragma)
# Generate the raw procedure
var rawProc = copy(baseProc)
case baseProc.name.kind
of nnkPostfix:
rawProc.name[1] = newIdentNode(apiName)
else:
rawProc.name= newIdentNode(apiName)
# Generate the conversion proc
var conversionProc = copy(baseProc)
conversionProc.pragma= newEmptyNode()
conversionProc.body= newStmtList()
# Generate the conversion call, and modify the conversion procs parameters.
var conversionCall = newCall(apiName)
for i in 1.. <conversionProc.params.len: # Skip the first value (return type)
var currentParams = conversionProc.params[i]
var paramsType = currentParams[currentParams.len-2]
var paramsIdents = newSeq[PNimrodNode]()
for k in 0..len(currentParams)-3:
paramsIdents.add(currentParams[k])
if paramsType.kind != nnkEmpty and $paramsType == "WinString":
currentParams[currentParams.len-2] = newIdentNode("string")
for param in paramsIdents:
conversionCall.add(newCall("newWideCString", copy(param)))
else:
for param in paramsIdents:
conversionCall.add(copy(param))
# Add the call to the conversionProc
var callParent = conversionProc.body
if baseProc.params[0].kind != nnkEmpty and $baseProc.params[0] != "void":
callParent = newNimNode(nnkReturnStmt)
conversionProc.body.add(callParent)
callParent.add(conversionCall)
# Add the other procs. Order is important!
result.add(baseProc)
result.add(rawProc)
result.add(conversionProc)
echo(repr(result))
proc CreateDirectory*(pathName: WinString, security: pointer=nil): int32 {.
WinImport, dynlib: "kernel32", stdcall.}
when isMainModule:
var x = createDirectory("CreatedDirectory")
echo(x)
# dumpTree:
# proc getLost(e, s:string) =
# getFound(convert(e), convert(s))
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment