From: Sam Roberts Date: 2004-04-19T23:13:03+09:00 Subject: Re: [Swig] my typemaps are (mostly) ignored in ruby Wrote Lyle Johnson , on Thu, Apr 15, 2004 at 09:11:02PM -0500: Thank you for comments, I will read them carefully and try to implement your suggestions. It will take me a few days to get to it, but you asked me to clarify how I'm thinking some things should work, so I'll try to do that now. > On Apr 14, 2004, at 8:46 AM, Sam Roberts wrote: > >// - creating opaque objects > >// > >// FIXME: This isn't working, Example::ParamsCreate in ruby still > >takes two > >// arguments! > > > >int ParamsCreate(int i, Params* OUTPUT); > > The typemaps.i library file provides OUTPUT typemaps for all of the > primitive types (e.g. ints, floats and such) but doesn't know about > your own types (like Params). For this to work you need to basically > follow the model shown in typemaps.i and apply that to the writing of > your own OUTPUT typemap(s). > > Before we go down that road, however, I am curious about how you want > users to be able to call this method from their Ruby code. From the way > it's declared, I'm guessing its use in C code goes something like this: > > Params p; > if (ParamsCreate(i, &p)) { > /* success! */ > } > > Do you want the corresponding Ruby code going to go like this? > > return_code, params = ParamsCreate(i) That was what I was expecting. What I'd really like is exception mapping, so this looks like: params = ParamsCreate(i) (where if result != 0, an exception was raised) I'm not too attached to the syntax in ruby right now, I'll do anything that works. Making it pretty is stage II. > >// FIXME: can I turn all non-zero return values into a Ruby exception? > > > >int ParamsGet(Params p, int* OUTPUT); > > Try this: > > %exception { > $action > if (result != 0) { > rb_raise(rb_eRuntimeError, "bleh"); > } > } Ok, tried this, but its halfway to what I was hoping for. If return != 0 it throws an exception, but if return == 0, it returns (for ParamsGet) an output array: [0, OUTPUT], because return is always 0 if an exception isn't raised. I would like to not see the 0, and just have the single integer OUTPUT value returned. Is that possible? No problem if it isn't, I can wrap all swig-generated ruby APIs in ruby to convert the first integer in the return array to an exception. > The %exception directive is described in this chapter of the SWIG > documentation: > > http://www.swig.org/Doc1.3/Customization.html Well, if you can pass on a comment to the writers of that chapter: I'd read this, but I wasn't quite able to figure out how to apply this to normal C APIs returning an error code (a very common case, I would think). It has two styles of example, one in which the C++ try/catch is called (not my case), and the other where it appears to suggest rewriting C APIs to return void, and cache the "exception" somewhere (but I want to wrap C APIs, not rewrite them in C to be more swig-friendly). Btw, your example above would make a great addition to section 22.5, Simple Exception Handling. > The problem is that SWIG's typemap matcher tries to be very specific if > you specify the parameter names in addition to their types. It begins to make sense. I should be able to get this working my next try. > >int CtxGet(Ctx c, size_t* fooSz, unsigned char* foo); > > I think I understand how it works in C, but (as before) tell me how > you'd like your users to call this from Ruby. What would the interface > look like? I would like a String, foo, to be allocated and returned. In C, this would look something like the following, in the swig-generated code (hopefully): size_t fooSz; unsigned char* foo = 0; int err = CtxGet(c, &fooSz, 0); if(err) return err; foo = malloc(fooSz); if(!foo) return ENOMEM err = CtxGet(c, &fooSz, foo); if(err) return err; return ruby_make_str(foo, fooSz); And on the ruby side, if I can't transform the return value into an exception, I would call the swig-generated stuff in ruby with something like: def CtxGet(ctx) out = Example.CtxGet(ctx) # out is: [ return (a Integer), foo (a String) ] if(out[0] != 0) raise ArgumentError, Example.ErrToStr(out[0]) out[1] end If I could get the exception handling to work as I described above, I would be able to call this more directly as: foo = Example.CtxGet(ctx) > >// Q5 - mutating buffers > >// > >// Is this considered bad practice? There are huge efficiency gains to > >be made > >// when you don't force dynamic allocation for your output! It's like > >a ruby > >// "!"-method. > > > >%typemap(in)(size_t sz, const unsigned char* in, unsigned char* out) { > > $1 = (size_t) RSTRING($input)->len; > > $2 = STR2CSTR($input); > > $3 = STR2CSTR($input); > >} > > > >int CtxDo(Ctx c, size_t length, const unsigned char* in, unsigned > >char* out); > > This looks dangerous to me, but then again I don't know how CtxDo() > works. I'll assume that it reads data from "in" and writes data to > "out", and it's OK if those two locations overlap in memory. Exactly. > Is it guaranteed that CtxDo() won't write a number of bytes that is > larger than the capacity of the Ruby string? Yes, the output is guaranteed to be the same size as the input (thats why there is need for only one size_t argument, and theres is no output "size_t*" aguments). In ruby, I would think to call this like: input_output_string = "some txt" return_code = CtxDo(ctx, input_output_string) If return_code is 0, then input_output_string would be mutated. By transforming the triplet of (size_t, in ptr, out ptr) to a single argument, I'm guaranteeing that the output buffer will always be as large as the input buffer, since they are the same. In the real APIs I want to wrap, these kind of APIs are for bulk encryption. If you are reading and encrypting a 120M file in 4K chunks, you don't want to have to allocate the output buffer for every chunk, and you don't care about the unencrypted value of the chunk after you've encrypted it, its fine to overwrite it with the encrypted value. Maybe its silly to worry about performance in a dynamic language such as ruby, where every read from a file results in a newly allocated string? Thanks for your help, I really appreciate it. Cheers, Sam -- Sam Roberts