I am learning Nim, and I wanted to see if I could import some functionality from a cpp lib: https://libtins.github.io/
I decided to try to do the example they have on the front page, but I am getting stuck trying to declare a proc for this:
const IP &ip = pdu.rfind_pdu<IP>();
The Nim manual has an example of templates:
proc getSubsystem*[T](): ptr T {.importcpp: "SystemManager::getSubsystem<'*0>()", nodecl.}
and an example of dot functions:
proc cppMethod(this: CppObj, a, b, c: cint) {.importcpp: "#.CppMethod(@)".}
But when I try to combine the two:
proc rfind_pdu[T](p: PDU): T {.importcpp: "#.rfind_pdu<'0>()".}
It produces the error: > /mnt/data/code/nim/test.nim:16:15 template/generic instantiation of rfind_pdu from here > /mnt/data/code/nim/test.nim:13:17 Error: cannot instantiate: 'T'
The error seems to disappear if I remove the PDU argument, which I don't understand. What's the cause of this error? How do I declare this proc?
Thanks for any info. b
(ps: I haven't programmed in cpp since before it had templates, so I'm not super familiar with it)
So after a little more work I've gotten to this (below):
I'm stuck at trying to figure out how to pass a callback into a cpp function. I have tried a few different things: adding cdecl, ptrs/no ptrs. The code provided produces the error:
error: cannot convert 'Tins::PDU' to 'Tins::PDU*' in argument passing
I'm not really sure what to try next, is anyone aware of an example of this type of thing?
type Sniffer {.header: "<tins/tins.h>", importcpp: "Tins::Sniffer".} = object
type PDU {.header: "<tins/tins.h>", importcpp: "Tins::PDU".} = object
type IP {.header: "<tins/tins.h>", importcpp: "Tins::IP".} = object
type TCP {.header: "<tins/tins.h>", importcpp: "Tins::TCP".} = object
type Callback = proc(p: ptr PDU): bool {.cdecl.}
proc newSniffer(nic: cstring): Sniffer {.header: "<tins/tins.h>", importcpp: "Tins::Sniffer(@)", constructor.}
proc sniff_loop(s: Sniffer, cb: Callback) {.importcpp: "#.sniff_loop(@)".}
proc rfind_pdu[T](p: ref PDU): ref T {.importcpp: "#.rfind_pdu<'0>()".}
proc src_addr(ip:IP) : string {.importcpp: "#.src_addr()".}
proc sport(tcp:TCP) : string {.importcpp: "#.sport()".}
proc dst_addr(ip:IP) : string {.importcpp: "#.dst_addr()".}
proc dport(tcp:TCP) : string {.importcpp: "#.dport()"}
proc callback*(pdu: ptr PDU): bool {.cdecl.} =
# let ip = pdu.rfind_pdu[:IP]()
# let tcp = pdu.rfind_pdu[:TCP]()
# echo ip.src_addr() #& ':' & tcp.sport() & " -> " & ip.dst_addr() & ':' & tcp.dport()
return true
proc main*() =
echo "start"
newSniffer("eth0").sniff_loop(callback)
echo "stop"
main()
It looks like the last missing piece to get it to work is handling a function that returns a cpp reference. It's segfaulting.
The cpp code is:
Tins::IP &ip = pdu.rfind_pdu<Tins::IP>();
but I can only get nim to produce:
Tins::IP ip = pdu.rfind_pdu<Tins::IP>();
Is there a way to get nim to use a reference variable on the lhs of a function call?
There might be a simpler way but in the worst case you can use codegenDecl somehow.
I think you should also not be using ref here as ref implies that Nim is able to track these pointers which doesn't really make sense with FFI.
Using ref was just a blind stab in the dark, I was able to get the arguments to be generated with & by using var, but the same doesn't work with return values.
The current version that compiles but segfaults is:
{.passL: "-ltins".}
type Sniffer {.header: "<tins/tins.h>", importcpp: "Tins::Sniffer".} = object
type PDU {.header: "<tins/tins.h>", importcpp: "Tins::PDU".} = object
type IP {.header: "<tins/tins.h>", importcpp: "Tins::IP".} = object
type TCP {.header: "<tins/tins.h>", importcpp: "Tins::TCP".} = object
type Callback = proc(p: var PDU): bool {.cdecl.}
proc newSniffer(nic: cstring): Sniffer {.header: "<tins/tins.h>",
importcpp: "Tins::Sniffer(@)", constructor.}
proc sniff_loop(s: Sniffer, cb: Callback) {.importcpp: "#.sniff_loop(@)".}
proc rfind_pdu[T](p: PDU): var T {.importcpp: "#.rfind_pdu<'*0>()".}
proc src_addr(ip: var IP): cstring {.importcpp: "#.src_addr()".}
proc sport(tcp: var TCP): cstring {.importcpp: "#.sport()".}
proc dst_addr(ip: var IP): cstring {.importcpp: "#.dst_addr()".}
proc dport(tcp: var TCP): cstring {.importcpp: "#.dport()".}
proc callback*(pdu: var PDU): bool {.cdecl.} =
var ip = pdu.rfind_pdu[:IP]()
echo ip, typeof ip
# let tcp = pdu.rfind_pdu[:TCP]()
echo ip.src_addr() #& ':' & tcp.sport() & " -> " & ip.dst_addr() & ':' & tcp.dport()
return true
proc main*() =
echo "start"
newSniffer("wlp8s0").sniff_loop(callback)
echo "stop"
main()
The call to ip.src_addr() is segfaulting, I believe because it's generating IP ip and not IP &ip.
tag your types that are always passed by reference with {.byref.}
Example: https://github.com/numforge/agent-smith/blob/a2d9251/third_party/ale_wrap.nim#L105-L123