[Gambas-user] Working with .so library

Tobias Boege taboege at ...626...
Thu Jun 15 20:45:35 CEST 2017


> > > > What I don't understand is how I construct the code in my
> > > > particular case.
> > > > 
> > > > To make an interface to the library I declare external pointer
> > > > like this:
> > > > 
> > > >      Extern CreateFptrInterface(ver As Integer) As Pointer
> > > > 
> > > > Then I declare some pointers that I'll use with help of the interface I
> > > > created:
> > > > 
> > > >      Extern put_DeviceEnable(p as Pointer, mode as Integer)
> > > > 
> > > >      Extern GetStatus(p as Pointer, StatRequest as String)
> > > > 
> > > > Then I declare the pointer which will be that interface:
> > > > 
> > > >      Public kkmDrv as Pointer
> > > > 
> > > > So then in sub I can do
> > > > 
> > > > kkmDrv = CreateFptrInterface(12) ' this establishes the interface
> > > > 
> > > > put_DeviceEnabled(kkmDrv, 1) ' this transfers the comand to the library
> > > > through the interface.
> > > > 
> > > > And it works great.
> > > > 
> > > > But then If I want to get some data from the library, as I
> > > > understand, I
> > > > have to declare another pointer, allocate ram for it and pass my
> > > > request.
> > > > 
> > > > I don't understand how should I pass that pointer to GetStatus()
> > > > while also
> > > > passing my interface pointer to it, let alone reading data back.
> > > > Totally
> > > > confused.
> > > > 
> > > This entirely depends on how the C functions in your library are
> > > declared.
> > > I don't know about your specific library but commonly the occurence
> > > of an
> > > error is indicated by an integer return code, e.g. this might be the
> > > signature of one of the functions in your library:
> > > 
> > >    int myfunction(void *interface, int argument)
> > > 
> > > If the documentation says that the return value (int) of this function
> > > indicates an error, then you just need to get that return value back
> > > into
> > > your Gambas program, which you accomplish by declaring the function in
> > > Gambas as
> > > 
> > >    Extern myfunction(interface As Pointer, argument As Integer) As
> > > Integer
> > > 
> > > (notice the trailing "As Integer"). Then you can use "myfunction" in
> > > your
> > > Gambas code like any other function and get and interpret its return
> > > value.
> > > 
> > > So, if this convention for error reporting is used, it is much
> > > simpler to
> > > get information about errors, without using Alloc() and co. Your library
> > > may use a different convention which actually involves pointers, but
> > > I wouldn't know.
> > > 
> > > Regards,
> > > Tobi
> > > 
> > I should've said it in the beginning. Ofcourse any function returns
> > integer value of 0 as success or -1 as error, but that only indicates
> > that function was successfully executed or not. So GetStatus() will
> > always return 0 because it shurely ran, nothing can go wrong here. But
> > that's not the result I want. GetStatus() actually gives back a string
> > with the status I asked for. Not that I fully understand how it does
> > that. I already gave links to the libfptr.so library itself
> > (http://allunix.ru/back/atol.tar.gz) and it's header files
> > (http://allunix.ru/back/atol-header.tar.gz) so that it's clearer, what
> > I'm talking about, unfortunately I am absolute zero in C to figure
> > things out myself.
> > 
> > For example I can see that to get serial number of the device driven by
> > that library i can use a function described like this:
> > 
> > get_SerialNumber(void *ptr, wchar_t *bfr, int bfrSize);
> > 
> > As far as I can tell what it does is it gets data needed and puts it
> > into some buffer. The result of executing this function through
> > put_SerialNumber(kkmDrv) will always be returned to me as 0.
> > 
> > So to see what's in that buffer, I have to then invoke GetStatus(kkmDrv)
> > describe in .h file like GetStatus(void *ptr); and the integer result of
> > this operation will also always be 0, which means that GetStatus itself
> > ran successfully, but I don't care about that, I want to see what it
> > actually told me, not that if it told me it successfully or not. So
> > that's the main confusion. If all this is too complicated and lamely
> > explained then nevermind, I expect it to be so and I'm sorry, that's the
> > best I can do. I'm just really confused that I recieve two answers, one
> > boolean telling if function successfully invoked and one string,
> > carrying the actual data I want.
> > 
> > 
> > Best Regards,
> > 
> > Dmitry.
> > 
> UPD: you know, I can be fundamentally wrong about all this library's
> functionality. Maybe it does not give me any data afterall, I'm beginning to
> think that this integer (or rather boolean) value is all it gives me back,
> and the "real" data is just written into it's log file. Which is sufficient
> to me, so, I guess, nevermind. Sorry about wasting your time.
> 

Don't worry about wasting my time. If I didn't want to use it, I wouldn't
have looked at your mail.

Let's have a look at fptrexample.cpp in atol-header.tar.gz which was linked
somewhere in this thread, e.g. these two instances where the programmer gets
data back from the library:

   54     int rc = EC_OK;
   55     iface->get_ResultCode(&rc);
----------------------------------------------------------------------------
   59         QVector<wchar_t> v(256);
   60         int size = iface->get_ResultDescription(&v[0], v.size());
   61         if (size <= 0)
   62             throw Exception("get_ResultDescription error");
   63         if (size > v.size())
   64         {
   65             v.clear();
   66             v.resize(size + 1);
   67             iface->get_ResultDescription(&v[0], v.size());
   68         }
   69         resultDescription = QString::fromWCharArray(&v[0]);

It's a little hard for me to transfer these into Gambas because I don't use
the Extern functionality in Gambas very often and I don't have the library
or the hardware to test my code -- but if you can accept a margin of error,
let me try.

What you have to do is look at how the C++ code uses the method of the
interface object. (BTW, I strongly suggest you get acquainted with C/C++!)
Gambas can't use the C++ interface, but luckily the library has a sane
C interface, too, so you have to look into the ifptr_c.h header to find
something that looks just like the get_ResultCode() method of the interface
object. This one looks very promising (as it has the same name):

   29 DTOSHARED_EXPORT int DTOSHARED_CCA get_ResultCode(void *ptr, int *resultCode);

Its signature tells you that you have to pass as the first argument ptr
(probably) your interface pointer (this makes a lot of sense, of course).
The second parameter is a pointer to an integer. This is where the
get_ResultCode function will store the result code (whatever that means).
You can also see that the return value of the function is int. The return
value from the function indicates if the get_ResultCode() call was
successful or not and if it was successful you will find the result code
you wanted written into the resultCode pointer that you passed. This
function signature translates to:

  Extern get_ResultCode(ptr As Pointer, resultCode As Pointer) As Integer

Assuming you already have a valid interface pointer ifptr you have to
call this function like this:

  ' Initialise the variable with EC_OK, as in line 54 above (the
  ' atol-header.tar.gz does not contain a definition of that thing,
  ' so you have to find it somewhere else):
  Dim iCode As Integer = ???

  get_ResultCode(ifptr, VarPtr(iCode))

Look up what VarPtr() does in the Gambas documentation. You can then just
use the iCode variable in your Gambas program. It contains the result code
you wanted to query from the library.

Now the second part (lines 59--69) are a little more difficult, because
you have to give a string buffer to presumably receive some description
of something. Again find the C function corresponding to the C++ method:

   30 DTOSHARED_EXPORT int DTOSHARED_CCA get_ResultDescription(void *ptr, wchar_t *bfr, int bfrSize);

It expects an interface pointer, a buffer of wchat_t's and the size of
that buffer (probably measured in wchar_t units and not in bytes).
Now, Gambas doesn't have a datatype corresponding to wchar_t and, sadly,
the size of a wchar_t is not standardised. You have to find out how big
a wchar_t is on your system or assume it's less than 8 bytes or work around
this issue some other way. I'll assume in the following that a wchar_t
consists of 4 bytes, because that is how it is on my system. So you
allocate a buffer that is enough to hold the description. The person who
wrote the C++ code thought that 256 characters might be sufficient:

  Extern get_ResultDescription(ptr As Pointer, bfr As Pointer, bfrSize As Integer) As Integer

  ' Note that you should not do what zxMarce hinted at and get a Gambas
  ' string and use VarPtr on it to get a string buffer, because, as the
  ' VarPtr documentation tells you, Gambas strings are read-only. You
  ' should not pass a VarPtr to its contents to an external function that
  ' may modify the contents. (Although it would probably be okay in this
  ' case, but the reason lies in how Gambas manages strings internally,
  ' but this is getting too far.)
  Dim pBuf As Pointer
  Dim iSize As Integer

  ' Create and clear the buffer. 256 characters and each character is 4 bytes.
  pBuf = Alloc(String$(256 * 4, "\0"))
  iSize = get_ResultDescription(ifptr, pBuf, 256)

Now just imitate what the C++ programmer did -- some error checking:

  If iSize <= 0 Then Error.Raise(("get_ResultDescription error"))
  If iSize > 256 Then ' the library needs more space than you provided
    ' Make the buffer larger and try again.
    ' I don't think the C++ makes much sense because it only increases
    ' the buffer length by 1 and tries again. There isn't even a loop.
    ' Anyway, figure this out yourself.
  Endif

Finally we want a real Gambas string out of the buffer pBuf. The C++
programmer kind of has the same problem in line 69. He doesn't like
wchar_t either and converts it to a QString. Get the string in pBuf
as a Gambas string by using String@() and then convert it from wchar_t
format to UTF-8. Then it's ready to be used in your program.

  Dim sDesc As String

  sDesc = Conv$(String@(pBuf), "WCHAR_T", "UTF-8")
  ' The documentation of String@() tells you not to Free() the pBuf you
  ' created the string from, until you are done dealing with the string.
  ' I _guess_ that since we Conv$'d the string, we can now Free the
  ' original pBuf, but you have to verify this!
  Free(pBuf)

I hope this gets you started. You should really look into C/C++ though.

Regards,
Tobi

-- 
"There's an old saying: Don't change anything... ever!" -- Mr. Monk




More information about the User mailing list