source: trunk/lang/ruby/Ruby_Rappture.cc @ 1095

Last change on this file since 1095 was 1081, checked in by gah, 16 years ago

fix for perl, ruby bindings; visviewer SendBytes? method sends all data at once

File size: 17.0 KB
Line 
1/*
2 * ----------------------------------------------------------------------
3 *  Ruby Rappture Extension Source
4 *
5 * ======================================================================
6 *  AUTHOR:  Benjamin Haley, Purdue University
7 *  Copyright (c) 2008  Purdue Research Foundation
8 *
9 *  See the file "license.terms" for information on usage and
10 *  redistribution of this file, and for a DISCLAIMER OF ALL WARRANTIES.
11 * ======================================================================
12 */
13
14/* This file uses Ruby's C API to create a new Ruby class named Rappture
15 *  as a wrapper around the Rappture library C++ API.
16 *
17 * Define RAISE_EXCEPTIONS (see Makefile) to raise Ruby Exceptions on errors.
18 * Without this, those methods which return a Ruby object on success return
19 *  Qnil on errors.  For those methods which return Qnil on either success or
20 *  failure, there is no way to check for errors without an Exception.
21 */
22
23#include <string>        /* std::string */
24#include <stdlib.h>      /* atof() */
25#include "RpLibrary.h"   /* RpLibrary, Rappture::Buffer */
26#include "RpUtils.h"     /* Rappture::Utils::progress() */
27#include "RpUnits.h"     /* RpUnits::convert() */
28#include "ruby.h"        /* VALUE, rb_*(), Data_*_Struct(), NUM2INT(),
29                            INT2NUM(), NUM2DBL(), STR2CSTR(), ANYARGS */
30
31/******************************************************************************
32 * File scope variables
33 ******************************************************************************/
34
35
36/* The new Rappture class */
37VALUE classRappture;
38
39
40extern "C" void Init_Rappture(void);
41
42/******************************************************************************
43 * RbRp_GetString()
44 *
45 * Implement the get() public method of the Rappture class.
46 * Return a Ruby String from the location path in the Rappture object self.
47 *
48 * The original plan was to convert the returned std::string to a Ruby String,
49 *  then try calling the to_f and to_i methods on the Ruby String, to see if
50 *  we should return a Float (double) or a Fixnum (integer).  The problem with
51 *  this approach is that these methods (to_f and to_i) return 0.0 and 0,
52 *  respectively, on error,  **without raising an Exception**, so there is no
53 *  way to  distinguish between an error and legitimate zero values.  The user
54 *  will have to call the appropriate Ruby conversion functions on the returned
55 *  String, for now.
56 ******************************************************************************/
57
58static VALUE
59RbRp_GetString(VALUE self, VALUE path)
60{
61   RpLibrary *lib;
62   std::string str;
63
64   /* Extract the pointer to the Rappture object, lib, from the Ruby object,
65      self */
66   Data_Get_Struct(self, RpLibrary, lib);
67
68   /* Read the data from path in lib as a C++ std::string. */
69   str = lib->getString(STR2CSTR(path));
70     
71   /* Return a Ruby VALUE */
72   return rb_str_new2(str.c_str());
73
74}  /* end RbRp_GetString */
75
76
77/******************************************************************************
78 * RbRp_GetData()
79 *
80 * Implement the getdata() public method of the Rappture class.
81 * Return a Ruby String from the location path in the Rappture object self.
82 ******************************************************************************/
83
84static VALUE
85RbRp_GetData(VALUE self, VALUE path)
86{
87   RpLibrary *lib;
88   Rappture::Buffer buf;
89
90   /* Extract the pointer to the Rappture object, lib, from the Ruby object,
91      self */
92   Data_Get_Struct(self, RpLibrary, lib);
93
94   /* Read the data from path in lib as a C++ std::string. */
95   buf = lib->getData(STR2CSTR(path));
96     
97   /* Return a Ruby VALUE */
98   return rb_str_new2(buf.bytes());
99
100}  /* end RbRp_GetData */
101
102
103/******************************************************************************
104 * RbRp_PutObject()
105 *
106 * Implement the put() public method of the Rappture class.
107 * Put a Ruby String, Float (double), or Fixnum (integer) object into the
108 *  location path in the Rappture object self.
109 * Return Qnil.
110 * On error, if RAISE_EXCEPTIONS is defined, raise a Ruby RuntimeError
111 *  Exception.
112 ******************************************************************************/
113
114static VALUE
115RbRp_PutObject(VALUE self, VALUE path, VALUE value, VALUE append)
116{
117#ifdef RAISE_EXCEPTIONS
118   /* Get the Ruby ID of the "to_s" method. */
119   ID id_to_s = rb_intern("to_s");
120
121   /* Call the "to_s" method on value to get a Ruby String representation of
122      the value, in case we need to compose an error message below.  The final
123      argument 0 indicates no further arguments.*/
124   VALUE rbStrName = rb_funcall(value, id_to_s, 0);
125#endif
126
127   RpLibrary *lib;
128   int intVal;
129
130   /* Extract the pointer to the Rappture object, lib, from the Ruby
131      object, self */
132   Data_Get_Struct(self, RpLibrary, lib);
133
134   /* Check the type of the Ruby object value.  If the type if one we are
135      prepared to handle, call the appropriate put() function.  If the type
136      is unexpected, raise a RuntimeError Exception, if RAISE_EXCEPTIONS is
137      defined.*/
138   switch (TYPE(value))
139   {
140      case T_STRING:
141         lib->put(STR2CSTR(path), STR2CSTR(value), "", NUM2INT(append));
142         break;
143      case T_FIXNUM:
144         intVal = NUM2INT(value);
145         lib->putData(STR2CSTR(path), (const char *)&intVal, sizeof(int),
146                      NUM2INT(append));
147         break;
148      case T_FLOAT:
149         lib->put(STR2CSTR(path), NUM2DBL(value), "", NUM2INT(append));
150         break;
151      default:
152#ifdef RAISE_EXCEPTIONS
153         rb_raise(rb_eRuntimeError,
154                  "Unable to put object %s to Rappture: unknown type",
155                  STR2CSTR(rbStrName));
156#endif
157         break;
158   }  /* end switch */
159
160   /* Return a Ruby VALUE */
161   return Qnil;
162
163}  /* end RbRp_PutObject */
164
165
166/******************************************************************************
167 * RbRp_PutData()
168 *
169 * Implement the putdata() public method of the Rappture class.
170 * Put a Ruby String into the location path in the Rappture object self.
171 * Return Qnil.
172 * On error, if RAISE_EXCEPTIONS is defined, raise a Ruby RuntimeError
173 *  Exception.
174 ******************************************************************************/
175
176static VALUE
177RbRp_PutData(VALUE self, VALUE path, VALUE value, VALUE append)
178{
179#ifdef RAISE_EXCEPTIONS
180   /* Get the Ruby ID of the "to_s" method. */
181   ID id_to_s = rb_intern("to_s");
182
183   /* Call the "to_s" method on value to get a Ruby String representation of
184      the value, in case we need to compose an error message below.  The final
185      argument 0 indicates no further arguments.*/
186   VALUE rbStrName = rb_funcall(value, id_to_s, 0);
187#endif
188
189   RpLibrary *lib;
190
191   /* Extract the pointer to the Rappture object, lib, from the Ruby
192      object, self */
193   Data_Get_Struct(self, RpLibrary, lib);
194
195   if (T_STRING == TYPE(value))
196   {
197      long int nbytes;
198      char *bytes = rb_str2cstr(value, &nbytes);
199
200      lib->putData(STR2CSTR(path), bytes, nbytes, NUM2INT(append));
201   }
202#ifdef RAISE_EXCEPTIONS
203   else
204   {
205      rb_raise(rb_eRuntimeError,
206               "Unable to put data \"%s\" to Rappture: unknown type",
207               STR2CSTR(rbStrName));
208   }
209#endif
210
211   /* Return a Ruby VALUE */
212   return Qnil;
213
214}  /* end RbRp_PutData */
215
216
217/******************************************************************************
218 * RbRp_PutFile()
219 *
220 * Implement the putfile() public method of the Rappture class.
221 * Put a file into the location path in the Rappture object self.
222 * Return Qnil.
223 * On error, if RAISE_EXCEPTIONS is defined, raise a Ruby RuntimeError
224 *  Exception.
225 ******************************************************************************/
226
227static VALUE
228RbRp_PutFile(VALUE self, VALUE path, VALUE filename, VALUE append,
229             VALUE compress)
230{
231#ifdef RAISE_EXCEPTIONS
232   /* Get the Ruby ID of the "to_s" method. */
233   ID id_to_s = rb_intern("to_s");
234
235   /* Call the "to_s" method on value to get a Ruby String representation of
236      the value, in case we need to compose an error message below.  The final
237      argument 0 indicates no further arguments.*/
238   VALUE rbStrName = rb_funcall(filename, id_to_s, 0);
239#endif
240
241   RpLibrary *lib;
242
243   /* Extract the pointer to the Rappture object, lib, from the Ruby
244      object, self */
245   Data_Get_Struct(self, RpLibrary, lib);
246
247   if (T_STRING == TYPE(filename))
248   {
249      VALUE ft = rb_const_get(rb_cObject, rb_intern("FileTest"));
250      ID id_filetest = rb_intern("file?");
251
252      if (Qtrue == rb_funcall(ft, id_filetest, 1, filename))  /* valid filename */
253      {
254         lib->putFile(STR2CSTR(path), STR2CSTR(filename), NUM2INT(compress),
255                      NUM2INT(append));
256      }
257#ifdef RAISE_EXCEPTIONS
258      else
259      {
260         rb_raise(rb_eRuntimeError, "%s is not a valid file",
261                  STR2CSTR(rbStrName));
262      }
263#endif
264   }
265#ifdef RAISE_EXCEPTIONS
266   else
267      rb_raise(rb_eRuntimeError, "Bad file name: %s", STR2CSTR(rbStrName));
268#endif
269
270   /* Return a Ruby VALUE */
271   return Qnil;
272
273}  /* end RbRp_PutFile */
274
275
276/******************************************************************************
277 * RbRp_Result()
278 *
279 * Implement the result() public method of the Rappture class.
280 * Write the XML of the Rappture object self to disk.
281 * Return Qnil.
282 ******************************************************************************/
283
284static VALUE
285RbRp_Result(VALUE self, VALUE status)
286{
287   RpLibrary *lib;
288
289   /* Extract the pointer to the Rappture object, lib, from the Ruby
290      object, self */
291   Data_Get_Struct(self, RpLibrary, lib);
292
293   /* Write the Rappture XML to disk. */
294   lib->result(NUM2INT(status));
295
296   /* Return a Ruby VALUE */
297   return Qnil;
298
299}  /* end RbRp_Result */
300
301
302/******************************************************************************
303 * RbRp_Xml()
304 *
305 * Impelement the xml() public method of the Rappture class.
306 * Return a Ruby String with the XML of the Rappture object self.
307 * Return Qnil on error, or, if RAISE_EXCEPTIONS is defined, raise a
308 *  RuntimeError Exception.
309 ******************************************************************************/
310   
311static VALUE
312RbRp_Xml(VALUE self)
313{
314   RpLibrary *lib;
315   std::string str;
316
317   /* Extract the pointer to the Rappture object, lib, from the Ruby
318      object, self */
319   Data_Get_Struct(self, RpLibrary, lib);
320
321   /* Get the Rappture object's XML */
322   str = lib->xml();
323
324   /* Return a Ruby VALUE */
325   if (str.empty())
326   {
327#ifdef RAISE_EXCEPTIONS
328      rb_raise(rb_eRuntimeError, "Unable to retrieve XML");
329#else
330      return Qnil;
331#endif
332   }
333   else
334      return rb_str_new2(str.c_str());
335
336}  /* end RbRp_Xml */
337
338
339/******************************************************************************
340 * RbRp_Progress()
341 *
342 * Implement the progress() public method for the Rappture class.
343 * Write a progress message to stdout, using the Ruby Fixnum percent and the
344 *  Ruby String message.
345 * Return Qnil.
346 ******************************************************************************/
347
348static VALUE
349RbRp_Progress(VALUE self, VALUE percent, VALUE message)
350{
351   /* Note: self is a dummy here */
352
353   /* Write the message */
354   (void)Rappture::Utils::progress(NUM2INT(percent), STR2CSTR(message));
355
356   /* Return a Ruby VALUE */
357   return Qnil;
358
359}  /* end RbRp_Progress */
360
361
362/******************************************************************************
363 * RbRp_Convert()
364 *
365 * Implement the convert() public method for the Rappture class.
366 * Convert the Ruby String fromVal, containing a numeric value and optional
367 *  units, to the units specified by the Ruby String toUnitsName.
368 * If the Ruby Fixnum showUnits is 1, return a Ruby String with units appended,
369 *  else return a Ruby Float (double).
370 * On error, return Qnil, or, if RAISE_EXCEPTIONS is defined, raise a
371 *  RuntimeError Exception.
372 ******************************************************************************/
373
374static VALUE
375RbRp_Convert(VALUE self, VALUE fromVal, VALUE toUnitsName, VALUE showUnits)
376{
377   VALUE retVal = Qnil;
378   RpLibrary *lib;
379   std::string strRetVal;
380   int result;
381
382   /* Extract the pointer to the Rappture object, lib, from the Ruby
383      object, self */
384   Data_Get_Struct(self, RpLibrary, lib);
385
386   /* Convert */
387   strRetVal = RpUnits::convert(STR2CSTR(fromVal), STR2CSTR(toUnitsName),
388                                NUM2INT(showUnits), &result);
389                                   
390   /* Return value */
391   if (0 == result)
392   {
393      const char *retCStr = strRetVal.c_str();
394
395      if (NUM2INT(showUnits))  /* Ruby String */
396         retVal = rb_str_new2(retCStr);
397      else                     /* Ruby Float */
398         retVal = rb_float_new(atof(retCStr));
399   }
400#ifdef RAISE_EXCEPTIONS
401   else
402      rb_raise(rb_eRuntimeError, "Unable to convert %s to %s",
403               STR2CSTR(fromVal), STR2CSTR(toUnitsName));
404#endif
405   return retVal;
406
407}  /* end RbRp_Convert */
408
409
410/******************************************************************************
411 * RbRp_Delete()
412 *
413 * Implement the destructor for the Rappture class.
414 * This function is registered with Ruby's garbage collector, which requires
415 *  the void * argument.
416 ******************************************************************************/
417   
418static void
419RbRp_Delete(void *ptr)
420{
421   delete ((RpLibrary *)ptr);
422
423}  /* end RbRp_Delete */
424
425
426/******************************************************************************
427 * RbRp_Init()
428 *
429 * Implement the initialize method, called from the new() method, for the
430 *  Rappture class.
431 * Set the instance variable @driver (the name of the driver XML file).
432 * Return self.
433 ******************************************************************************/
434
435static VALUE
436RbRp_Init(VALUE self, VALUE driver)
437{
438   /* Set instance variable */
439   rb_iv_set(self, "@driver", driver);
440
441   /* Return a Ruby VALUE */
442   return self;
443
444}  /* end RbRp_Init */
445
446
447/******************************************************************************
448 * RbRp_New()
449 *
450 * Implement the new() method for the Rappture class.
451 * Create a Rappture I/O object from the XML file driver.
452 * Create a new Ruby object.
453 * Associate the Rappture object and the Ruby object with the class value
454 *  classval, and garbage collection functions (mark and sweep).
455 * Pass driver to the initialize method.
456 * Return the new Ruby object.
457 * Note this method is not static.
458 ******************************************************************************/
459
460VALUE
461RbRp_New(VALUE classval, VALUE driver)
462{
463   /* Create the Rappture object from the XML driver file. */
464   RpLibrary *lib = new RpLibrary(STR2CSTR(driver));
465     
466   /* Data_Wrap_Struct() creates a new Ruby object which associates the
467      Rappture object, lib, with the class type classval, and registers 0
468      as the marking function and RbRp_Delete() as the freeing function for
469      Ruby's mark and sweep garbage collector. */
470   VALUE rbLib = Data_Wrap_Struct(classval, 0, RbRp_Delete, lib);
471
472   /* Call the initialize method; rb_obj_call_init() requires an argument
473      array. */
474   VALUE argv[1] = {driver};
475   rb_obj_call_init(rbLib, 1, argv);
476
477   /* Return a Ruby VALUE */
478   return rbLib;
479
480}  /* end RbRp_New */
481
482
483/******************************************************************************
484 * Init_Rappture()
485 *
486 * This is the first function called when Ruby loads the Rappture extension.
487 * Create the Rappture class, add constants, and register methods.
488 ******************************************************************************/
489
490void
491Init_Rappture(void)
492{
493   /* Create the Rappture class as a sublcass of Ruby's Object class. */
494   classRappture = rb_define_class("Rappture", rb_cObject);
495
496   /* Add constants to the Rappture class, accessible via, e.g.,
497      "Rappture::APPEND"*/
498   rb_define_const(classRappture, "APPEND", INT2NUM(RPLIB_APPEND));
499   rb_define_const(classRappture, "OVERWRITE", INT2NUM(RPLIB_OVERWRITE));
500   rb_define_const(classRappture, "UNITS_ON", INT2NUM(RPUNITS_UNITS_ON));
501   rb_define_const(classRappture, "UNITS_OFF", INT2NUM(RPUNITS_UNITS_OFF));
502   rb_define_const(classRappture, "COMPRESS", INT2NUM(RPLIB_COMPRESS));
503   rb_define_const(classRappture, "NO_COMPRESS", INT2NUM(RPLIB_NO_COMPRESS));
504
505   /* Function pointer cast necessary for C++ (but not C) */
506#  define RB_FUNC(func)  (VALUE (*)(ANYARGS))func
507
508   /* Register methods; last argument gives the expected number of arguments,
509      not counting self (i.e. number of arguments from Ruby code) */
510   rb_define_singleton_method(classRappture, "new", RB_FUNC(RbRp_New),    1);
511   rb_define_method(classRappture, "initialize", RB_FUNC(RbRp_Init),      1);
512   rb_define_method(classRappture, "get",        RB_FUNC(RbRp_GetString), 1);
513   rb_define_method(classRappture, "getdata",    RB_FUNC(RbRp_GetData),   1);
514   rb_define_method(classRappture, "put",        RB_FUNC(RbRp_PutObject), 3);
515   rb_define_method(classRappture, "putdata",    RB_FUNC(RbRp_PutData),   3);
516   rb_define_method(classRappture, "putfile",    RB_FUNC(RbRp_PutFile),   4);
517   rb_define_method(classRappture, "result",     RB_FUNC(RbRp_Result),    1);
518   rb_define_method(classRappture, "xml",        RB_FUNC(RbRp_Xml),       0);
519   rb_define_method(classRappture, "convert",    RB_FUNC(RbRp_Convert),   3);
520   rb_define_method(classRappture, "progress",   RB_FUNC(RbRp_Progress),  2);
521
522}  /* end Init_Rappture */
523
524/* TODO rpElement*(), rpChildren*() */
525
Note: See TracBrowser for help on using the repository browser.