bug in fiddle or something I did is wrong?

83 views
Skip to first unread message

Liz Huang

unread,
Nov 20, 2015, 12:34:33 PM11/20/15
to Ruby on Rails: Talk


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


Frederick Cheung

unread,
Nov 20, 2015, 2:57:33 PM11/20/15
to Ruby on Rails: Talk
On Friday, November 20, 2015 at 5:34:33 PM UTC, Liz Huang wrote:
> 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!";


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

Liz Huang

unread,
Nov 23, 2015, 10:25:07 AM11/23/15
to Ruby on Rails: Talk


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:

Encoding::UndefinedConversionError in SamplesizeController#compute

"\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

Frederick Cheung

unread,
Nov 23, 2015, 12:21:27 PM11/23/15
to Ruby on Rails: Talk
On Monday, November 23, 2015 at 3:25:07 PM UTC, Liz Huang wrote:


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

Liz Huang

unread,
Nov 23, 2015, 1:02:36 PM11/23/15
to Ruby on Rails: Talk


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

Liz Huang

unread,
Nov 23, 2015, 2:07:41 PM11/23/15
to Ruby on Rails: Talk


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

Liz Huang

unread,
Nov 23, 2015, 2:22:24 PM11/23/15
to Ruby on Rails: Talk


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

Liz Huang

unread,
Nov 24, 2015, 10:54:52 AM11/24/15
to Ruby on Rails: Talk



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

Liz
Reply all
Reply to author
Forward
0 new messages