• cffi and ffidl

    From Jacob@21:1/5 to All on Fri Feb 17 07:04:37 2023
    Hello,

    I have perhaps a trivial question, but it's been giving me trouble as I
    don't work with C code or dll files often. I'd like to call a function
    from a dll file and have successfully done this using the cffi package.
    Here is an example of what that looks like:

    package require cffi
    cffi::Wrapper create myFunc {C:\myFunc.dll}
    ::myFunc function myFunc long {a {long[30] inout} b {double[30] inout} c
    {long inout} d {long[30] inout} e {double[30] inout} f {double[30]
    inout} g {long inout} h {long inout} i {string}}
    set a {1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1}
    set b {7 0.00018 -45.2107 0.39324 32.5714 -4.82 0.536 207.8 99.6 \
    -20.7 12 10 100 3 0 0 -2 -3 1 17 0.00018181 -10.1909 -3.3898 29.2007 \
    0.26492 0.29827 0.4567 0.00428 0.0077969 -0.039173}
    set c 30
    set d {1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1}
    set e {1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1}
    set f {1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1}
    set g 1
    set h 0
    set i U
    myFunc a b c d e f g h i
    set resp [lindex $e 0]
    puts "Response: $resp"

    Unfortunately, it turns out I need to do this on Tcl 8.5.9, which
    doesn't appear to work with cffi. So I believe I need to do this with
    Fifdl instead. My understanding is that all the arrays are treated as
    pointers, but I haven't been able to get it to work. Any help would be appreciated.

    Thanks,

    Jacob

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From Jacob@21:1/5 to Jacob on Tue Feb 21 07:56:34 2023
    On 2/17/2023 7:04 AM, Jacob wrote:
    Hello,

    I have perhaps a trivial question, but it's been giving me trouble as I
    don't work with C code or dll files often. I'd like to call a function
    from a dll file and have successfully done this using the cffi package.
    Here is an example of what that looks like:

    package require cffi
    cffi::Wrapper create myFunc {C:\myFunc.dll}
    ::myFunc function myFunc long {a {long[30] inout} b {double[30] inout} c {long inout} d {long[30] inout} e {double[30] inout} f {double[30]
    inout} g {long inout} h {long inout} i {string}}
    set a {1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1}
    set b {7 0.00018 -45.2107 0.39324 32.5714 -4.82 0.536 207.8 99.6 \
    -20.7 12 10 100 3 0 0 -2 -3 1 17 0.00018181 -10.1909 -3.3898 29.2007 \ 0.26492 0.29827 0.4567 0.00428 0.0077969 -0.039173}
    set c 30
    set d {1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1}
    set e {1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1}
    set f {1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1}
    set g 1
    set h 0
    set i U
    myFunc a b c d e f g h i
    set resp [lindex $e 0]
    puts "Response: $resp"

    Unfortunately, it turns out I need to do this on Tcl 8.5.9, which
    doesn't appear to work with cffi. So I believe I need to do this with
    Fifdl instead. My understanding is that all the arrays are treated as pointers, but I haven't been able to get it to work. Any help would be appreciated.

    Thanks,

    Jacob

    Sorry to bump this message, but perhaps a simple example could get me
    moving in the right direction. If I have the following c function held
    in dPlus.dll, how would I interface with FFidl?

    void dPlus(const double a[2], const double b[2], double c[2])
    {
    c[0] = a[0] + b[0];
    c[1] = a[1] + b[1];
    }


    Thanks,

    Jacob

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From Jacob@21:1/5 to Jacob on Tue Feb 21 09:55:32 2023
    On 2/21/2023 7:56 AM, Jacob wrote:
    On 2/17/2023 7:04 AM, Jacob wrote:
    Hello,

    I have perhaps a trivial question, but it's been giving me trouble as
    I don't work with C code or dll files often. I'd like to call a
    function from a dll file and have successfully done this using the
    cffi package. Here is an example of what that looks like:

    package require cffi
    cffi::Wrapper create myFunc {C:\myFunc.dll}
    ::myFunc function myFunc long {a {long[30] inout} b {double[30] inout}
    c {long inout} d {long[30] inout} e {double[30] inout} f {double[30]
    inout} g {long inout} h {long inout} i {string}}
    set a {1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1}
    set b {7 0.00018 -45.2107 0.39324 32.5714 -4.82 0.536 207.8 99.6 \
    -20.7 12 10 100 3 0 0 -2 -3 1 17 0.00018181 -10.1909 -3.3898 29.2007 \
    0.26492 0.29827 0.4567 0.00428 0.0077969 -0.039173}
    set c 30
    set d {1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1}
    set e {1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1}
    set f {1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1}
    set g 1
    set h 0
    set i U
    myFunc a b c d e f g h i
    set resp [lindex $e 0]
    puts "Response: $resp"

    Unfortunately, it turns out I need to do this on Tcl 8.5.9, which
    doesn't appear to work with cffi. So I believe I need to do this with
    Fifdl instead. My understanding is that all the arrays are treated as
    pointers, but I haven't been able to get it to work. Any help would be
    appreciated.

    Thanks,

    Jacob

    Sorry to bump this message, but perhaps a simple example could get me
    moving in the right direction. If I have the following c function held
    in dPlus.dll, how would I interface with FFidl?

    void dPlus(const double a[2], const double b[2], double c[2])
    {
      c[0] = a[0] + b[0];
      c[1] = a[1] + b[1];
    }


    Thanks,

    Jacob


    I figured it out. I needed to convert to and from binary. I used the
    following functions from Arjen:

    proc Binarize {varType args} {
    foreach var $args {
    upvar $var x
    if [regexp {[ac]} $varType] {
    set x [binary format a* $x]
    } else {
    set x [binary format $varType[llength $x] $x]
    }
    }
    };#End proc Binarize

    proc deBinarize {varType args} {
    foreach var $args {
    upvar $var x
    switch $varType {
    i {binary scan $x i[expr {[string length $x]/4}] x}
    f {binary scan $x f[expr {[string length $x]/4}] x}
    d {binary scan $x d[expr {[string length $x]/8}] x}
    default {binary scan $x a* x}
    }
    }
    };#End proc deBinarize

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From Ashok@21:1/5 to Jacob on Wed Feb 22 21:05:32 2023
    On 2/21/2023 11:25 PM, Jacob wrote:
    I figured it out. I needed to convert to and from binary. I used the following functions from Arjen:



    Jacob,

    You haven't shown what you do with those generated binaries but be aware
    that if your function is in fact treating those array parameters as
    in-out (as you have shown in your CFFI definition), then it will write
    to the storage allocated by the Tcl_Obj. This is generally a no-no. If
    they are input parameters only then that should be ok.

    For output and inout parameters, you probably have to explicitly
    allocate memory in ffidl. I'm not sufficiently familiar with ffidl to
    know how to do that.

    /Ashok

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From Jacob@21:1/5 to Ashok on Wed Feb 22 15:03:21 2023
    On 2/22/2023 7:35 AM, Ashok wrote:
    On 2/21/2023 11:25 PM, Jacob wrote:
    I figured it out. I needed to convert to and from binary. I used the
    following functions from Arjen:



    Jacob,

    You haven't shown what you do with those generated binaries but be aware
    that if your function is in fact treating those array parameters as
    in-out (as you have shown in your CFFI definition), then it will write
    to the storage allocated by the Tcl_Obj. This is generally a no-no. If
    they are input parameters only then that should be ok.

    For output and inout parameters, you probably have to explicitly
    allocate memory in ffidl. I'm not sufficiently familiar with ffidl to
    know how to do that.

    /Ashok

    Ashok,

    Thank you for your feedback. As far as I can tell things are working
    correctly, but I would not be surprised if there is something dangerous
    I'm doing. Here is the code for completeness. This function in the dll
    file is a little strange in that it does have many in-out parameters,
    but only one is actually relevant to me (rresp). Considering the script basically ends here and tcl closes, hopefully whatever risky behavior
    going on behind the scenes can be ignored.

    # current script location
    set scriptFolder [file dirname [info script]]
    # load ffid package
    load [file join $scriptFolder Ffidl07t.dll]
    # setup dll call
    set DLL [file join $scriptFolder myFunc.dll]
    set typeList {pointer-var pointer-var pointer-var pointer-var
    pointer-var pointer-var pointer-var pointer-var pointer-utf8}
    ffidl::callout myFunc $typeList long [ffidl::symbol $DLL myFunc]
    # initialize inputs
    set iparam {1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1}
    set nparam 30
    set iresp {1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1}
    set rresp {1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1}
    set dresp {1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1}
    set nresp 1
    set isens 0
    set userdata U
    Binarize i iparam nparam iresp nresp isens
    Binarize d dresp rresp rparam
    set rparam {7 0.00018 -45.2107 0.39324 32.5714 -4.82 0.536 207.8 99.6 \
    -20.7 12 10 100 3 0 0 -2 -3 1 17 0.00018181 -10.1909 -3.3898 29.2007 \
    0.26492 0.29827 0.4567 0.00428 0.0077969 -0.039173}
    myFunc iparam rparam nparam iresp rresp dresp nresp isens userdata
    deBinarize d rresp
    set resp [lindex $rresp 0]
    puts "Response: $resp"

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)