I am wrapping VapourSynth and I facing a problem that I don't how to best face it.
I want to make available a number of functions that are available from plugins. The plugins available are known at runtime. For example the following works:
macro gen_functions():typed =
var source = """
proc Version():ptr VSMap =
let plugins = getPlugins()
for plugin in plugins:
if plugin[0] == "ffms2":
let plug = getPluginById(plugin[1])
let args = createMap()
return API.invoke(plug, "Version".cstring, args)
"""
result = parseStmt(source)
It works, because I know there is function call "Version" provided by the plugin "ffms2", so I provide the information to the compiler.
What I would like to do is moving:
let plugins = getPlugins()
for plugin in plugins:
...
outside of the "source" variable. But if I do that, obviously nim complains becuase getPlugins requires information only available at runtime.
Can I create functions at runtime? How?
Can you think in a better strategy to create all those functions?
macro gen_function(plugin:string, functionName:string) =
var source = fmt"""
proc {functionName}():ptr VSMap =
let plug = getPluginById("{plugin}")
let args = createMap()
return API.invoke(plug, "{functionName}".cstring, args)
"""
result = parseStmt(source)
when isMainModule:
let plugins = getPlugins()
for plugin in plugins:
if plugin[0] == "ffms2":
for f in plugin[3]:
gen_function( plugin[1], f[0])
@mantielero Nim does not support creating functions at runtime. Generally, compiled languages such as Nim get around this by compiling a source file into a DLL and then loading that. It's unfortunately not as simple as you want it to be.
The library @Hlaaftana linked to is being created by @shashlick (which is probably not ready yet) and it will likely allow an easy interface for creating plugins in Nim.
In the meantime, this is how you would do it. Put the following 3 files in the same directory.
# english.nim
proc greet*(): string {.exportc, dynlib, cdecl.} =
result = "Hello!"
Compile the above with nim c --app:lib english.nim. This will result in libenglish.dylib on Mac, libenglish.so on other Unix, and english.dll on Windows.
# french.nim
proc greet*(): string {.exportc, dynlib, cdecl.} =
result = "Hello!"
Compile the above with nim c --app:lib french.nim. This will result in libfrench.dylib on Mac, libfrench.so on other Unix, and french.dll on Windows.
# testplugin.nim
import dynlib
type
greetFunction = proc(): string {.nimcall.}
proc main() =
echo "Enter a language: "
let lang = stdin.readLine()
let lib = case lang
of "french":
loadLib("./libfrench.dylib") # Rename for your OS
else:
loadLib("./libenglish.dylib") # Rename for your OS
if lib == nil:
echo "Error loading library"
quit(QuitFailure)
let greet = cast[greetFunction](lib.symAddr("greet"))
if greet == nil:
echo "Error loading 'greet' function from library"
quit(QuitFailure)
let greeting = greet()
echo greeting
unloadLib(lib)
main()
Then compile and run the above with nim c -r testplugin.nim. You should get a prompt to enter your language. Enter "french" for "Bonjour!" or anything else for "Hello!".
You could easily extend the above to get a function name from a list of plugin paths. Hope this is helpful to you.
I've created a plugin system that's ready for use. It was part of the feud text editor I was working on and was recently extracted into a standalone repo and package. It has diverged since extraction and feud is yet to be ported to use it but that will happen eventually.
Appreciate all reviews and feedback.
https://github.com/genotrance/plugins