structuring complexly-interdependent C/Ruby libraries

Discussion in 'Ruby' started by Eric Peden, Feb 28, 2005.

  1. Eric Peden

    Eric Peden Guest

    First post. Hi. :)

    I'm writing a ray-tracer in Ruby (I know, I know...) Perhaps some other
    time I'll wax ecstatic over the advantages Ruby brings to such a
    project, but at the moment I have less pleasant issues I'm hoping to
    get some help with.

    A pure-Ruby ray tracer is just a tad slow. Clearly, some core
    functionality needs to be implemented in C for speed. The system is
    broken into classes similar to the following:

    Camera
    Engine
    Vector
    Point
    Ray
    Shape
    Sphere
    Polygon

    Of those classes, only a handful are performance critical, and of each
    of those, only one or two methods are true bottlenecks. I'm trying to
    keep as much of the code as possible in Ruby, while breaking out only
    the real work horses into C. Vector, for example, is almost pure C,
    whereas only the intersection-testing method in the Shape subclasses is
    in C. Unfortunately, I keep running into dependency problems when
    trying to build the C functions. My C code is organized like this:

    ext/
    extconf.rb
    ext.c
    vector.c
    sphere.c

    When built, this generates a single library that includes the code for
    all of the optimized classes. 'ext.c' has an Init_ function that calls
    initialization routines in the other files. Here's an example from
    sphere.c:

    Init_Sphere() {
    VALUE cSphere = rb_const_get(rb_cObject, rb_intern("Sphere"));
    }

    sphere.rb would look something like this:

    require 'vector'
    require 'ext/optimized'

    class Sphere < Shape
    ...
    end

    When I run this, I get a "undefined constant" error from the C code,
    which makes sense: when the C library is included, Sphere hasn't been
    defined yet. So I just move the "require 'ext/optimized'" to the bottom
    of sphere.rb, right? Sadly, no. There's only one library entry point,
    so I have to initialize all of my optimized classes at the same time.
    'vector.rb' also requires 'ext/optimized', so Init_Sphere() would still
    get called before Sphere is actually defined, and I'll still get my
    error. Furthermore, since Vector gets used in several other files, I
    have to worry about the order in which those files are parsed, as well.

    I can see a few solutions: one is to break out every C file so that it
    compiles into its own library. This would leave me with an ugly file
    structure, since I'd need either a new extconf.rb for each C file, or a
    sub-directory for each source file. I've tried doing something like
    this:

    VALUE cSphere = rb_eval_string("class Shape; end; class Sphere <
    Shape; end");

    but that feels extremely naughty, not to mention requires me to
    duplicate the heritage of each subclass I want to provide C extensions
    for. I suppose I could also create a "Optimizer" object in my C
    library's Init() method, and attach methods to that class that would
    call the class-specific Init_...() methods:

    # a .rb file
    require 'ext/optimized'
    class Sphere < Shape
    ..
    end
    Optimizer.init_sphere

    But that also seems rather distasteful.

    I apologize for my wordiness... I'm trying to make my situation clear.
    Am I making this more difficult than it really is? If the classes
    involved where pure-C, or if there wasn't any subclassing, I'd be fine.
    I just can't see how to manage the class dependencies while dealing
    with a single library entry point, and so I've come to seek the
    guidance of the wizards. ;) Any help--or even architecture bashing, if
    it's constructive--is appreciated.

    --
    eric
     
    Eric Peden, Feb 28, 2005
    #1
    1. Advertising

  2. Eric Peden

    Pit Capitain Guest

    Eric Peden schrieb:
    > First post. Hi. :)


    Welcome!

    > (...code snipped...)


    > When I run this, I get a "undefined constant" error from the C code,
    > which makes sense: when the C library is included, Sphere hasn't been
    > defined yet.


    I've never extended Ruby with C yet, but couldn't you create the optimized
    classes in C and later add some Ruby methods to them?

    Regards,
    Pit
     
    Pit Capitain, Feb 28, 2005
    #2
    1. Advertising

  3. Eric Peden

    leon breedt Guest

    On Mon, 28 Feb 2005 16:34:21 +0900, Eric Peden <> wrote:
    > I apologize for my wordiness... I'm trying to make my situation clear.
    > Am I making this more difficult than it really is? If the classes
    > involved where pure-C, or if there wasn't any subclassing, I'd be fine.
    > I just can't see how to manage the class dependencies while dealing
    > with a single library entry point, and so I've come to seek the
    > guidance of the wizards. ;) Any help--or even architecture bashing, if
    > it's constructive--is appreciated.

    I always define classes with C elements in the extension itself.

    You know you can define a class and the methods on it in Ruby as many
    times as you want as long as you don't change the inheritance? So its
    no problem to later in Ruby redefine the class with its pure-Ruby
    methods.

    Leon
     
    leon breedt, Feb 28, 2005
    #3
  4. Eric Peden wrote:
    > First post. Hi. :)
    >
    > I'm writing a ray-tracer in Ruby (I know, I know...) Perhaps some other
    > time I'll wax ecstatic over the advantages Ruby brings to such a
    > project, but at the moment I have less pleasant issues I'm hoping to get
    > some help with.
    >
    > A pure-Ruby ray tracer is just a tad slow. Clearly, some core
    > functionality needs to be implemented in C for speed. The system is
    > broken into classes similar to the following:
    >
    > Camera
    > Engine
    > Vector
    > Point
    > Ray
    > Shape
    > Sphere
    > Polygon
    >
    > Of those classes, only a handful are performance critical, and of each
    > of those, only one or two methods are true bottlenecks. I'm trying to
    > keep as much of the code as possible in Ruby, while breaking out only
    > the real work horses into C. Vector, for example, is almost pure C,
    > whereas only the intersection-testing method in the Shape subclasses is
    > in C. Unfortunately, I keep running into dependency problems when trying
    > to build the C functions. My C code is organized like this:
    >
    > ext/
    > extconf.rb
    > ext.c
    > vector.c
    > sphere.c


    You might take a look at RubyInline:

    http://rubyforge.org/projects/rubyinline/

    It will allow you to put the C code right into your Ruby classes.

    Regards,

    Michael
     
    Michael Neumann, Feb 28, 2005
    #4
  5. Hello Eric,

    Usually, you define classes in your C extension and likewise in your Ruby
    code, doing things twice, which does not matter since Ruby will take your
    meaning (creating one class).

    -- myclass.c
    Init_... {
    cMyClass = rb_define_class_under( mMyModule, "MyClass", rb_cObject );
    }

    -- myclass.rb
    require 'myclass.so'
    class MyClass
    # ...
    end

    But yeah, redefinition with a different superclass gives you a
    SuperClassMismatch. So I would aim for different classes altogether, using
    mixins to mix the c code into the Ruby code:

    -- myclass.c
    Init_... {
    mMyClassMixin = rb_define_module_under( mMyModule, "MyClassCMixin" );
    ...
    }

    -- myclass.rb
    require 'myclasscmixin'

    class MyClass < MySuperClass
    include MyClassCMixin
    #...
    end

    This method is tested and works, and it looks clean enough to me. Now does
    this solve all your problems ? Yes, if I read you right.

    k

    code manufacture & ruby lab at http://www.tua.ch/ruby
    --
    The man who sets out to carry a cat by its tail learns something that will
    always be useful and which never will grow dim or doubtful.
    -- Mark Twain
     
    Kaspar Schiess, Feb 28, 2005
    #5
  6. Eric Peden

    Eric Peden Guest

    On Feb 28, 2005, at 5:05 AM, Michael Neumann wrote:
    > You might take a look at RubyInline:
    >
    > http://rubyforge.org/projects/rubyinline/
    >
    > It will allow you to put the C code right into your Ruby classes.


    I have looked at RubyInline (and in fact use it in a couple of places
    in my code). It works great for shorter methods. However, for longer
    methods, or classes where I C-ify several methods, I prefer breaking
    the code out in a .c file. This makes my text editor happy, and lets me
    work with C-specific syntax highlighting and autocompletion features.

    There are also a few times where I want to work directly with the C
    representation of another optimized class, and I had trouble doing this
    with Inline. This was early on in my explorations, so it's likely I was
    just doing things the wrong way.

    Thanks for the suggestion!

    --
    eric
     
    Eric Peden, Feb 28, 2005
    #6
  7. Eric Peden

    Eric Peden Guest

    On Feb 28, 2005, at 5:12 AM, Kaspar Schiess wrote:
    > Usually, you define classes in your C extension and likewise in your
    > Ruby
    > code, doing things twice, which does not matter since Ruby will take
    > your
    > meaning (creating one class).


    This was suggested by several people (thanks, Pit and Leon!), and not
    only captures the essence of Ruby, but would work quite well... if it
    weren't for my need for inheritance, as you point out shortly. I
    _could_ also create "empty" versions of my superclasses in C, but then
    I'm recreating the inheritance hierarchy again. Or...

    > But yeah, redefinition with a different superclass gives you a
    > SuperClassMismatch. So I would aim for different classes altogether,
    > using
    > mixins to mix the c code into the Ruby code:

    <snip>
    > This method is tested and works, and it looks clean enough to me. Now
    > does
    > this solve all your problems ? Yes, if I read you right.


    ...I could just do this. I believe this is exactly what I need. Thanks
    for the help!

    --
    eric
     
    Eric Peden, Feb 28, 2005
    #7
  8. (In response to news: by Eric
    Peden)

    > ...I could just do this. I believe this is exactly what I need. Thanks
    > for the help!


    np. Just found out about this too. Thanks for asking the cool question.

    k

    --
    code manufacture & ruby lab at http://www.tua.ch/ruby
    o.
    ..o
    ooo
     
    Kaspar Schiess, Feb 28, 2005
    #8
  9. Eric Peden

    leon breedt Guest

    On Tue, 1 Mar 2005 00:50:29 +0900, Eric Peden <> wrote:
    > This was suggested by several people (thanks, Pit and Leon!), and not
    > only captures the essence of Ruby, but would work quite well... if it
    > weren't for my need for inheritance, as you point out shortly. I
    > _could_ also create "empty" versions of my superclasses in C, but then
    > I'm recreating the inheritance hierarchy again. Or...

    I'm not sure you need to recreate it:

    class Super
    end

    class Test < Super
    def one
    puts "one!"
    end
    end

    t1 = Test.new

    class Test
    def two
    puts "two!"
    end
    end

    t2 = Test.new

    puts t1.class.ancestors.inspect
    puts t2.class.ancestors.inspect


    It appears that Ruby "remembers" the ancestry from the first time that
    class was defined using that name.

    So if you define it in C first, and in your Ruby code, require the
    extension before redefining the classes, you don't need to recreate
    the inheritance hierarchy.

    Leon
     
    leon breedt, Mar 1, 2005
    #9
    1. Advertising

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

It takes just 2 minutes to sign up (and it's free!). Just click the sign up button to choose a username and then you can ask your own questions on the forum.
Similar Threads
  1. Interdependent validation

    , Oct 18, 2005, in forum: ASP .Net
    Replies:
    2
    Views:
    482
  2. =?Utf-8?B?VGltbQ==?=
    Replies:
    11
    Views:
    3,601
    Angna
    Jun 29, 2006
  3. Roal Zanazzi
    Replies:
    2
    Views:
    547
    Kevin Goodsell
    Sep 22, 2003
  4. MathStuf

    Interdependent classes

    MathStuf, May 5, 2007, in forum: C++
    Replies:
    5
    Views:
    971
    Gianni Mariani
    May 6, 2007
  5. Marc Hebert
    Replies:
    3
    Views:
    619
Loading...

Share This Page