bug in fiddle or something I did is wrong?

Hi,

I am trying to use Fiddle to call C function in dynamic library, I used to be able to pass a return long variable and an error message, but now only return long variable is returned, can’t get the error message, I create a very simple example to test, this is my add.c

#include <stdio.h> #include <stdlib.h> #include <string.h>

long add(long maxn, double delta, double conf, char *errMsg) { long answer;

    errMsg = (char *) malloc(6*sizeof(char));
    answer = (long)(maxn + delta + conf);
    errMsg = "Hello!";

    return answer;

}

and add.h

#ifndef add_h #define add_h

long add(long maxn, double delta, double conf, char *errMsg);

#endif

I created lib_add.so and part of my .rb file calling fiddle:

class SamplesizeController < ApplicationController require ‘fiddle’

… def compute

     str = "there!"
     libm = Fiddle.dlopen('/var/www/myapp/smart/lib/lib_add.so')
     add = Fiddle::Function.new(libm['add'],[Fiddle::TYPE_LONG,
                               Fiddle::TYPE_DOUBLE,
                               Fiddle::TYPE_DOUBLE,
                               Fiddle::TYPE_VOIDP],
                               Fiddle::TYPE_LONG)

    add.call(session[:nmax], session[:delta], session[:conf], str)
    session[:errmsg] = str.to_s
    redirect_to :action => "results"
end

end

session[:errmsg] can be modified? only return variable can be changed or something I did was wrong? Any advice will be greatly appreciated.

Thanks! Liz

Hi,

I am trying to use Fiddle to call C function in dynamic library, I used to be able to pass a return long variable and an error message, but now only return long variable is returned, can't get the error message, I create a very simple example to test, this is my add.c

#include <stdio.h> #include <stdlib.h> #include <string.h>

long add(long maxn, double delta, double conf, char *errMsg) { long answer;

    errMsg = \(char \*\) malloc\(6\*sizeof\(char\)\);
    answer = \(long\)\(maxn \+ delta \+ conf\);
    errMsg = &quot;Hello\!&quot;;

Arguments are passed by value in C, so if you assign to a variable in a function it does not change its value in the calling function. You need to change your C function so that either:

- the argument is a pointer to some memory allocated by the caller and you copy into it by strncpy or similar - the argument is a pointer to a pointer size block of memory. Your function would then allocate a buffer, write the error message to that buffer and then write the value of the pointer to the argument, ie the last argument to the function is now char **message and your code does *message = malloc(...)

Fred

Encoding::UndefinedConversionError

in SamplesizeController#compute

Thanks!

Strange that I am able to get error message before just using char *, thought it meant passing string by pointer already.

I change the argument to char ** errMsg, but in Ruby, when I tried to convert the pointer to string, I got error message:

“\xC9” from ASCII-8BIT to UTF-8

Extracted source (around line #34):

32
33
34
35
36
37
if options.is_a?(::JSON::State)

# Called from JSON.{generate,dump}, forward it to JSON gem's to_json

self.to_json_without_active_support_encoder(options)

else

# to_json is being invoked directly, use ActiveSupport's encoder

ActiveSupport::JSON.encode(self, options)

Do I need to  change anything in ruby file when use a pointer?

Thanks!
Liz

Thanks!

Strange that I am able to get error message before just using char *, thought it meant passing string by pointer already.

it does, but if you want to allocate a new buffer and have the caller access that then you need pointer to a pointer

I change the argument to char ** errMsg, but in Ruby, when I tried to convert the pointer to string, I got error message:

[snip]

if options.is_a?(::JSON::State)

Called from JSON.{generate,dump}, forward it to JSON gem’s to_json

self.to_json_without_active_support_encoder(options)

else

to_json is being invoked directly, use ActiveSupport’s encoder

ActiveSupport::JSON.encode(self, options)

Do I need to change anything in ruby file when use a pointer?

Yes. You need to allocate a pointer size bit of memory for the C function (via Fiddle::Pointer). The C function fills that in, you then use the ptr method on Fiddle::Pointer to get the memory allocated by the C function, and create your string from there. (don’t forget to free the memory too)

Fred

If I don’t allocate a new buffer, then I don’t need pointer to a pointer? For example, I just copy an existing char * iarr to errMsg?

long add(long maxn, double delta, double conf, char *errMsg) { long answer; int i; char *iarr;

    //errMsg = (char *) malloc(6*sizeof(char));
    answer = (long)(maxn + delta + conf);
    iarr = "Hello!";
    for(i=0;i<6;i++) errMsg[i] = (char) iarr[i];

    return answer;

}

The document for Fiddle is so limited, wonder where I could find an example doing what you said? Now I can just guess and try, guess I use

buf = Fiddle::Pointer.malloc(8)

then pass buf to C function,

then try to convert string from buf returned?

Liz

I have trouble even getting adding value n returned when I tried to use Fiddle::Function.new, I returned to my old way of using Fiddle:

class SamplesizeController < ApplicationController require ‘fiddle’ require ‘fiddle/import’

module Libm extend Fiddle::Importer dlload ‘/var/www/myapp/smart/lib/lib_add.so’ extern ‘long add(long, double, double, char *)’ end

def compute session[:n] = Libm.add( session[:nmax], session[:delta], session[:conf], session[:errmsg] )

end

… end

This way, my session[:n] is returned correctly, even I couldn’t get session[:errmsg].

Before, I was able to pass session[:errmsg] and get out errormsg (don’t need to deal with Fiddle::Pointer), my c code, I didn’t use malloc for errmsg, but copy another char * to errmsg using a for loop…

Liz

I tried like this:

     msg = "      "
     buf = Fiddle::Pointer[msg]
     libm = Fiddle.dlopen('/var/www/myapp/smart/lib/lib_add.so')
     add = Fiddle::Function.new(libm['add'],[Fiddle::TYPE_LONG,
                               Fiddle::TYPE_DOUBLE,
                               Fiddle::TYPE_DOUBLE,
                               Fiddle::TYPE_VOIDP],
                               Fiddle::TYPE_LONG)

     session[:n]=add.call(session[:nmax], session[:delta], session[:conf], buf)
     session[:errmsg] = buf.to_str

session[:n] is correct, I see that I could get the underlying pointer for ruby object `` and return it as a Fiddle::Pointer object, and I can pass the pointer to C function, don’t know how to get session[:errmsg] from buf returned…

Liz

Follow example in fiddle document about “strcpy” and finally got it right.

Liz