[MacRuby-devel] Porting Cocoa OpenGL sample code

Brian Chapados chapbr at gmail.com
Fri Mar 6 11:54:32 PST 2009


I'm not sure what's happening with the pointer to an array of longs in
the first case. It looks like the Pointer is being created, but there
is no way to assign/retrieve values?

p = Pointer.new_with_type('^L')
p.assign([1,2,3])
p[0][1]  # => 2

I think the reason that this works is that rb_objc_rval_to_ocval seems
to only handle assigning a ruby array to an ObjC pointer type ("^").

I guess you could just go with it, but if the function you are passing
to will write to that address, then you need to ensure that the space
has been allocated (see below). When you call assign with an array,
it's like writing something like this in C:

unsigned long *p[1];
unsigned long array[3] = {1,2,3};
*p = array;  // same as p[0] = array;
*p[1]; // => 2

Ideally you just want a real C array:

unsigned long[10];

or the dynamically allocated equivalent.

The issue here is that CGGetActiveDisplayList is going to write data
to the pointer you pass it, and it is your responsibility to make sure
that you own that memory.  If you tell MacRuby that you want a pointer
to "^L", it reserves space for the pointer, but not space to hold the
data.  When you assign a ruby array to the pointer, the Pointer class
dynamically allocates space for each element in the array, so if
you're going to use that method, I think you might need to assign a
blank array of the proper size before you pass the pointer to
function:

p = Pointer.new_with_type('^L')
p.assign(Array.new(count,0))
count_p = Pointer.new_with_type("L")
CGGetActiveDisplayList(count, p[0], count_p)

This is my understanding from reading through objc.m, someone please
correct me if I'm wrong about this.

Brian

On Fri, Feb 27, 2009 at 8:57 AM, Julien Jassaud <sojastar07 at gmail.com> wrote:
> Brian,
> Thanks for the very detailed answer. This tremendously helps. I am now
> having problems with arrays, though. I try to make a pointer to an array of
> 32 longs, like this :
>>> p = Pointer.new_with_type('[32L]')
> => #<Pointer:0x80052af00>
>>> p[0]
> ArgumentError: can't convert C/Objective-C value `0x80052ae40' of type
> `[32i]' to Ruby object
> from (irb):49:in `[]'
> from (irb):49
> from /usr/local/bin/macirb:12:in `<main>'
> Am I not getting the syntax explained in the documentation page you
> mentioned ?
> Also, something is puzzling me. In C, arrays are pointers so I thought I
> could solve the problem (and it works) by doing :
>>> p=Pointer.new_with_type('^L')
> => #<Pointer:0x800530380>
>>> p.assign([1,2,3])
> => [1, 2, 3]
>>> p[0][1]
> => 2
> Great ! I have a pointer to something that looks like an array of longs (and
> it seems to satisfy the CGGetActiveDisplayList function). But how is it
> possible when everything here is an object and not a series of neatly
> aligned bytes ?
> Anyway, you are right. The part of Cocoa OpenGL I am porting now probably
> doesn't need to and should be wrapped in an Objective-C object.
> Thanks,
> Julien Jassaud
> Le 26 févr. 09 à 05:43, Brian Chapados a écrit :
>
> Can you explain: "^{_CGLRendererInfoObject=}"? is that some secret
>
> incantation only known by the MacRuby/Obj overlords?
>
> Yes, it is actually a 7th level spell: 'Encode Structure'.  To learn
> it, you must study the ancient tome:
> http://developer.apple.com/documentation/Cocoa/Conceptual/ObjCRuntimeGuide/Articles/chapter_7_section_1.html#//apple_ref/doc/uid/TP40008048-CH100-SW1
>
> If it makes you feel better, I usually need to look it up (or just
> run the Obj-C code sample), unless it is something simple (like '@').
> I don't keep this spell in memory, as it is only required in special
> situations.
>
> Seriously though, that is actually how you encode a pointer to a
> struct.  If you use the @encode(type) directive, the compiler will
> return the runtime encoding for 'type'.  It's a bit cryptic, but not
> too bad once you know the syntax:
>
> '^type' encodes a pointer to type.
> '{name=<field1 type><field2 type><field3 type>...<fieldN type>}'
> encodes a struct with N fields.
>
> To break it down using 2 examples:
>
> (from the docs)
> typedef struct example {
>    id   anObject; // encoding = @
>    char *aString; // encoding = c
>    int  anInt; // enoding = i
> } Example;
>
> @encode(Example) = "^{example=@ci}"
>
> CGLRendererInfoObj is a pointer to an opaque struct (we don't know
> anything about the fields) named _CGLRendererInfoObject. All we know
> is:
> typedef struct _CGLRendererInfoObject *CGLRendererInfoObj;
>
> so it's just "^{_CGLRendererInfoObject=}"
>
> In practice (unless you're working with CoreFoundation C APIs) you
> usually just need a pointer to an object.  The most common usage I run
> across is to retrieve NSError objects.  The CoreFoundation C API uses
> pass-by-reference extensively.  To use these functions with MacRuby,
> you need to create lots of pointer objects. In general, this is an
> area where interfacing ruby with C is just fugly.  I'd personally
> avoid doing this in MacRuby.  If you find yourself needing to use lots
> Pointer objects, it's probably better and less error-prone to write
> that code in C and expose it to MacRuby through an Objective-C
> interface.
>
> That said, for common things, I've used something like this extension
> to the Pointer class:
>
> ----
> class Pointer
>  def self.ptr
>    new_with_type("@")
>  end
>
>  def self.to(type = :object)
>    case type
>      when :object
>        new_with_type('@')
>      when :int
>        new_with_type('i')
>      when :char
>      when :bool
>      when :BOOL
>        new_with_type('c')
>      when :unsigned
>        new_with_type('I')
>    end
>  end
>
>  def value
>    self[0]
>  end
> end
>
> ----
>
> Need a pointer to an ObjC object?
> p = Pointer.ptr
>
> To a BOOL?
> p = Pointer.to(:BOOL)
>
> Need the value?
> p.value
>
> Those are the most common types I've needed.
>
> On Wed, Feb 25, 2009 at 11:11 AM, Matt Aimonetti
> <mattaimonetti at gmail.com> wrote:
>
> Brian, what's up with this syntax: info =
>
> Pointer.new_with_type("^{_CGLRendererInfoObject=}")
>
> Can you explain: "^{_CGLRendererInfoObject=}"? is that some secret
>
> incantation only known by the MacRuby/Obj overlords?
>
> Thanks,
>
> - Matt
>
> On Wed, Feb 25, 2009 at 10:13 AM, Brian Chapados <chapbr at gmail.com> wrote:
>
> CGLRendererInfo is a pointer to a struct:
>
>    typedef struct _CGLRendererInfoObject *CGLRendererInfoObj;
>
> try creating a pointer to void or to the struct:
>
> info = Pointer.new_with_type("^v")  # void *info;
>
> or
>
> info = Pointer.new_with_type("^{_CGLRendererInfoObject=}") #
>
> CGLRendererInfo *info
>
> I think the second one is effectively the same as what you were trying
>
> to do with:
>
> info = Pointer.new_with_type("CGLRendererInfoObj")
>
> except that the runtime doesn't know what to do with
>
> "CGLRendererInfo".  The argument to Pointer.new_with_type must be a
>
> valid Objective-C type encoding[1].
>
> [1]:
>
> http://developer.apple.com/documentation/Cocoa/Conceptual/ObjCRuntimeGuide/Articles/chapter_7_section_1.html#//apple_ref/doc/uid/TP40008048-CH100-SW1
>
> If you are ever in doubt about what encoding to use, you can always
>
> compile a small Objective-C program that prints out the output of
>
> @encode().  For example:
>
> #import <Foundation/Foundation.h>
>
> #import <OpenGL/OpenGL.h>
>
> int main(int argc, char *argv[])
>
> {
>
>    char *encoding = @encode(CGLRendererInfoObj);
>
>    printf("\nencoding => %s\n\n", encoding);
>
>    return 0;
>
> }
>
> compile with:
>
> gcc -Wall -o encode encode.m -framework Foundation -framework OpenGL
>
> then run:
>
> ./encode
>
> Maybe there is an easier way to obtain the output of @encode(). I'm not
>
> sure.
>
> Brian
>
> On Wed, Feb 25, 2009 at 1:42 AM, Julien Jassaud <julien at collectapply.jp>
>
> wrote:
>
> Hello,
>
> I am trying to port the Cocoa OpenGL sample to MacRuby and encountered a
>
> few
>
> problems.
>
> First, I can't access some constants defined in an enum in GLTypes.h. Do
>
> I
>
> need to port those constants to ruby by hand ? Is that related
>
> to gen_bridge_metadata ?
>
> Second, I need to use CGLQueryRendererInfo
>
> and CGLDescribeRenderer functions. The first one requires a pointer to
>
> a CGLRendererInfoObj structure but the second requires the object to be
>
> passed directly. I tried some C style pointer arithmetic :
>
> info = Pointer.new_with_type("CGLRendererInfoObj")
>
> count = Pointer.new_with_type("l")
>
> CGLQueryRendererInfo(caps[:cgl_display_mask], info, count) <- works
>
> fine,
>
> but CGLRendererInfoObj is opaque so I can't check it in irb.
>
> CGLDescribeRenderer(info[0], 0, kCGLRPRendererCount, count) <- I naively
>
> tried to dereference the pointer, but it doesn't work.
>
> CGLDescribeRenderer(info, 0, kCGLRPRendererCount, count) <- No
>
> complaints,
>
> but the value for count[0] is not consistent (100468704 renderers).
>
> I see in MacIRB that there is a CGLRendererInfoObj class but I
>
> can't instantiate it.
>
> This is all new to me and I may be overlooking something obvious. If
>
> anyone
>
> has an idea, please help.
>
> Thanks,
>
> Julien Jassaud
>
> _______________________________________________
>
> MacRuby-devel mailing list
>
> MacRuby-devel at lists.macosforge.org
>
> http://lists.macosforge.org/mailman/listinfo.cgi/macruby-devel
>
>
> _______________________________________________
>
> MacRuby-devel mailing list
>
> MacRuby-devel at lists.macosforge.org
>
> http://lists.macosforge.org/mailman/listinfo.cgi/macruby-devel
>
>
> _______________________________________________
>
> MacRuby-devel mailing list
>
> MacRuby-devel at lists.macosforge.org
>
> http://lists.macosforge.org/mailman/listinfo.cgi/macruby-devel
>
>
> _______________________________________________
> MacRuby-devel mailing list
> MacRuby-devel at lists.macosforge.org
> http://lists.macosforge.org/mailman/listinfo.cgi/macruby-devel
>
>
> _______________________________________________
> MacRuby-devel mailing list
> MacRuby-devel at lists.macosforge.org
> http://lists.macosforge.org/mailman/listinfo.cgi/macruby-devel
>
>


More information about the MacRuby-devel mailing list