Extending Ruby using C++ and Rice

L

Lea Savage

Hi,

I am trying to extend Ruby and my current implementation is using C++
and Rice (http://rice.rubyforge.org/main.html).
Mac OS X 10.5.8
Ruby 1.8.6 (2008-08-11 patchlevel 287) [universal-darwin9.0]

I have completed the following steps as shown in Rice to create a cpp
class (my_test.cpp)

#include "rice/Class.hpp"
#include "rice/Data_Type.hpp"
#include "rice/Constructor.hpp"
#include "MyTest.h"

using namespace Rice;

extern "C"
void Init_my_test()
{
define_class<MyTest>("MyTest").
define_constructor(Constructor<MyTest>()).
define_method("read", &MyTest::read);
}

Here are my h and cpp files for MyTest

*** HEADER FILE ***
#ifndef __MyTest_h
#define __MyTest_h

#include <string>
#include "XternalDebug.hpp"

class MyTest
{
public:
MyTest() {};
virtual ~MyTest(void){
SX::Terminate();
SXMP::Terminate();
}
std::string read(std::string filename);
};
#endif


*** CPP FILE ***
#include "MyTest.h"

extern "C" std::string MyTest::read(std::string filename)
{
if(SXMP::Initialize())
{
// Must initialize SX before we use it
if (SX::Initialize())
{
std::string status = "";
bool ok;
SX myFile;
myFile.OpenFile(filename, UnknownFile, opts);
std::string buffer;
myFile.Get(&buffer);
return buffer;
}
}
return NULL;
}

SX and SXMP are taken from another library called libXternalDebug.a

My extconf.rb file looks as follows:
require 'rubygems'
require 'mkmf-rice'
$CPPFLAGS << ' -DMAC_ENV=1'
have_library("stdc++")
dir_config("xtoolkit")
have_library("XternalDebug")
create_makefile("my_test")

I pass the following arguments to extconf.rb
ARCHFLAGS="-arch i386" ruby extconf.rb
--with-xtoolkit-include=/Users/<username>/xtoolkit/public/include
--with-xtoolkit-lib=/Users/<username>/xtoolkit/public/libraries

$ ARCHFLAGS="-arch i386" ruby extconf.rb
--with-xtoolkit-include=/Users/<username>/xtoolkit/public/include
--with-xtoolkit-lib=/Users/<username>/xtoolkit/public/libraries
checking for main() in -lrice... yes
checking for main() in -lstdc++... yes
checking for main() in -lX... yes
creating Makefile

I then run make:
$ makeg++ -I. -I.
-I/System/Library/Frameworks/Ruby.framework/Versions/1.8/usr/lib/ruby/1.8/universal-darwin9.0
-I. -I/Users/<username>/xtoolkit/public/include
-I/Library/Ruby/Gems/1.8/gems/rice-1.3.1/ruby/lib/include -DMAC_ENV=1
-fno-common -arch i386 -Os -pipe -fno-common -Wall -g -c my_test.cpp
g++ -I. -I.
-I/System/Library/Frameworks/Ruby.framework/Versions/1.8/usr/lib/ruby/1.8/universal-darwin9.0
-I. -I/Users/<username>/xtoolkit/public/include
-I/Library/Ruby/Gems/1.8/gems/rice-1.3.1/ruby/lib/include -DMAC_ENV=1
-fno-common -arch i386 -Os -pipe -fno-common -Wall -g -c MyTest.cpp
g++ -arch i386 -pipe -bundle -undefined dynamic_lookup -o my_test.bundle
my_test.o MyTest.o -L.
-L/System/Library/Frameworks/Ruby.framework/Versions/1.8/usr/lib
-L/Users/<username>/xtoolkit/public/libraries -L. -arch i386
-L/Library/Ruby/Gems/1.8/gems/rice-1.3.1/ruby/lib/lib -lXternalDebug
-lstdc++ -lrice -lruby -lpthread -ldl -lm

(please note i have removed my username intentionally from the above
paths)

I then have the following ruby test:
require 'my_test'
require 'test/unit'

class TestTest < Test::Unit::TestCase
def test_test
t = MyTest.new
assert_equal(Object, MyTest.superclass)
assert_equal(MyTest, t.class)
end
end

When I try to run this I get the following error:
$ ruby testtest.rb
Loaded suite testtest
Started
 
J

Jason Roelofs

Hi,
=20
I am trying to extend Ruby and my current implementation is using C++
and Rice (http://rice.rubyforge.org/main.html).
Mac OS X 10.5.8
Ruby 1.8.6 (2008-08-11 patchlevel 287) [universal-darwin9.0]
=20
I have completed the following steps as shown in Rice to create a cpp
class (my_test.cpp)
=20
#include "rice/Class.hpp"
#include "rice/Data_Type.hpp"
#include "rice/Constructor.hpp"
#include "MyTest.h"
=20
using namespace Rice;
=20
extern "C"
void Init_my_test()
{
define_class<MyTest>("MyTest").
define_constructor(Constructor<MyTest>()).
define_method("read", &MyTest::read);
}
=20
Here are my h and cpp files for MyTest
=20
*** HEADER FILE ***
#ifndef __MyTest_h
#define __MyTest_h
=20
#include <string>
#include "XternalDebug.hpp"
=20
class MyTest
{
public:
MyTest() {};
virtual ~MyTest(void){
SX::Terminate();
SXMP::Terminate();
}
std::string read(std::string filename);
};
#endif
=20
=20
*** CPP FILE ***
#include "MyTest.h"
=20
extern "C" std::string MyTest::read(std::string filename)
{
if(SXMP::Initialize())
{
// Must initialize SX before we use it
if (SX::Initialize())
{
std::string status =3D "";
bool ok;
SX myFile;
myFile.OpenFile(filename, UnknownFile, opts);
std::string buffer;
myFile.Get(&buffer);
return buffer;
}
}
return NULL;
}
=20
SX and SXMP are taken from another library called libXternalDebug.a
=20
My extconf.rb file looks as follows:
require 'rubygems'
require 'mkmf-rice'
$CPPFLAGS << ' -DMAC_ENV=3D1'
have_library("stdc++")
dir_config("xtoolkit")
have_library("XternalDebug")
create_makefile("my_test")
=20
I pass the following arguments to extconf.rb
ARCHFLAGS=3D"-arch i386" ruby extconf.rb
--with-xtoolkit-include=3D/Users/<username>/xtoolkit/public/include
--with-xtoolkit-lib=3D/Users/<username>/xtoolkit/public/libraries
=20
$ ARCHFLAGS=3D"-arch i386" ruby extconf.rb
--with-xtoolkit-include=3D/Users/<username>/xtoolkit/public/include
--with-xtoolkit-lib=3D/Users/<username>/xtoolkit/public/libraries
checking for main() in -lrice... yes
checking for main() in -lstdc++... yes
checking for main() in -lX... yes
creating Makefile
=20
I then run make:
$ makeg++ -I. -I.
= -I/System/Library/Frameworks/Ruby.framework/Versions/1.8/usr/lib/ruby/1.8/=
universal-darwin9.0
-I. -I/Users/<username>/xtoolkit/public/include
-I/Library/Ruby/Gems/1.8/gems/rice-1.3.1/ruby/lib/include -DMAC_ENV=3D1
-fno-common -arch i386 -Os -pipe -fno-common -Wall -g -c my_test.cpp
g++ -I. -I.
= -I/System/Library/Frameworks/Ruby.framework/Versions/1.8/usr/lib/ruby/1.8/=
universal-darwin9.0
-I. -I/Users/<username>/xtoolkit/public/include
-I/Library/Ruby/Gems/1.8/gems/rice-1.3.1/ruby/lib/include -DMAC_ENV=3D1
-fno-common -arch i386 -Os -pipe -fno-common -Wall -g -c MyTest.cpp
g++ -arch i386 -pipe -bundle -undefined dynamic_lookup -o = my_test.bundle
my_test.o MyTest.o -L.
-L/System/Library/Frameworks/Ruby.framework/Versions/1.8/usr/lib
-L/Users/<username>/xtoolkit/public/libraries -L. -arch i386
-L/Library/Ruby/Gems/1.8/gems/rice-1.3.1/ruby/lib/lib = -lXternalDebug
-lstdc++ -lrice -lruby -lpthread -ldl -lm
=20
(please note i have removed my username intentionally from the above
paths)
=20
I then have the following ruby test:
require 'my_test'
require 'test/unit'
=20
class TestTest < Test::Unit::TestCase
def test_test
t =3D MyTest.new
assert_equal(Object, MyTest.superclass)
assert_equal(MyTest, t.class)
end
end
=20
When I try to run this I get the following error:
$ ruby testtest.rb
Loaded suite testtest
Started
.
Finished in 0.000476 seconds.
=20
1 tests, 2 assertions, 0 failures, 0 errors
dyld: lazy symbol binding failed: Symbol not found:
__ZN9TSXMISsE9TerminateEv
Referenced from: /Users/<username>/tests/rice_test2/my_test.bundle
Expected in: dynamic lookup
=20
dyld: Symbol not found: __ZN9TSXISsE9TerminateEv
Referenced from: /Users/<username>/tests/rice_test2/my_test.bundle
Expected in: dynamic lookup
=20
Trace/BPT trap
=20
The above does show the 2 tests as passing but then I get the error
below when it is deconstructing the class at it uses the external
library.
=20
Does anyone know what I am doing wrong or what I am missing to get = these
errors? Do I need to include this external library in a particular
directory for ruby to discover?
Any help is much appreciated.
=20
I have also heard of using a combination of FFI and SWIG but how does
this work when it is an external library and I do not have the source?
=20
Lea
--=20
Posted via http://www.ruby-forum.com/.
=20


For future reference, Rice has it's own mailing list at =
(e-mail address removed). Send a blank email to that address to register as it =
will be dropped, then you can send to the list.

As for your question here, the one thing that stands out is the "extern =
'C'" on MyTest::read. Rice expects normal C++ code and such a statement =
isn't required for Rice to properly use your code. I'm not sure this is =
causing your error but it is something I would recommend removing unless =
it's there for other reasons.

Otherwise, as you noticed, the error says that it can't find =
SXM::Terminate during runtime. My guess is that the load path on your =
machine (DYLD_LIBRARY_PATH) doesn't include the path that includes the =
SXM bundles. One way to quickly test this is to place the bundle right =
next to your my_test.bundle.

As for your final statement about FFI and SWIG, these two libraries are =
in effect for different purposes. FFI is about run-time linking to C =
libraries (C++ doesn't work because of non-portable name mangling) while =
SWIG will parse your C++ and generate compliant C code that wraps the =
C++ into Ruby. For completeness of the comparison, Rice takes wrapping a =
third way, and is built to allow wrapping C++ libraries into Ruby using =
a very simple C++ API, so you very rarely have to actually call a C-Ruby =
API method.

I would prefer to continue this discussion on rice's mailing list, to =
avoid project-specific clutter in ruby-talk, but I'm fine to continue it =
here as well.=20

Jason
 
L

Lea Savage

Thanks for the suggestions.

I now have this working by doing the following:

- Removed the extern "C" as Jason suggested
- Moved my code into a dynamic library (using XCode) and included
references to the xtoolkit library and include files.
- Ensured that the static library was being included during compilation
in my extconf.rb then checking that it is loaded correctly:

require 'rubygems'
require 'mkmf-rice'
$LIBS << ' -lXternalDebug'
have_library("stdc++")
have_library("XternalDebug")
dir_config("xtoolkit")
dir_config("mytest")
have_library("MyTest")
have_header("MyTest.h")
create_makefile("my_test")

Added the XternalDebug to my LD_LIBRARY_PATH and my MyTest dynamic
library to DYLD_LIBRARY_PATH

I run the extconf.rb with the following arguments:
ARCHFLAGS="-arch i386" ruby extconf.rb
--with-mytest-include=/Users/<username>/mytest/MyTest
--with-mytest-lib=/Users/<username>/mytest/MyTest
--with-xtoolkit-include=/Users/<username>/xtoolkit/public/include

Kind regards,
Lea
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

Forum statistics

Threads
473,755
Messages
2,569,535
Members
45,007
Latest member
obedient dusk

Latest Threads

Top