ruby picture

RCR 202: Attach arbitrary data to methods defined in extensions

Submitted by Paul Brannan (Tue Jan 27 14:01:48 UTC 2004)

Abstract

It would be useful to be able to attach data to a method from C so that when that method gets called, it is possible to retrieve the data associated with the method. This involves adding two new functions, rb_add_data_to_function() and rb_get_function_data().

Problem

When defining methods from C, sometimes it is useful to have a single C function implement multiple Ruby methods. In order to have a single function implement multiple methods (and have slightly different behavior in each case), it is necessary to be able to attach data to the method. The usefulness of this becomes very apparent in C++ when template tricks are used to define Ruby methods.

It is possible in Ruby 1.6 to attach arbitrary data to a method using Module#define_method() and rb_iterate(). Note that this is an order of magnitide slower than a normal method.

It is possible in Ruby 1.8 to attach arbitrary data to a method like so:

  struct Data {
    int value;
  };
  VALUE foo(VALUE self) {
    struct Data * data = (struct Data *)(ruby_current_node->nd_entry);
    printf("%d\n", data->value);
  }
  NODE * node = NEW_CFUNC(foo, 0);
  node->nd_entry = (global_entry *)data;
  rb_add_method(rb_cFoo, rb_intern("foo"), node, NOEX_PUBLIC);</pre>

However, this code:
  <li>Assumes that the third element in the node is not used for anything (if
     it ever is, then this code will break).
  <li>Does not allow Ruby objects to be stored as data (they won't be properly
     marked when the GC starts).

  

Proposal

The following changes should be made:
  1. The NEW_CFUNC macro should be changed to accept a third argument which is a
       VALUE that can be returned to the user when his function is called.  By
       default this value will be nil.
    
  2. The garbage collector should be changed to mark this object when a CFUNC
       node is marked.
    
  3. There should be an additional function to add arbitrary data to a cfunc:
         void rb_add_data_to_function(VALUE klass, ID id, VALUE data);
       This function will look up the node for the given method and add data to
       the nd_value field for that method's node, if the method is a CFUNC,
       otherwise an exception should be raised.
    
  4. There should be an additional function to retrieve the data attached to the
       last cfunc called from the interpreter:
         VALUE rb_get_function_data();

The above code can now be rewritten as:

  struct Data {
    int value;
  };
  VALUE foo(VALUE self) {
    struct Data * data;
    Data_Get_Struct(rb_get_function_data(), rb_cObject, data);
    printf("%d\n", data->value);
  }
  rb_define_method(rb_cFoo, "foo", foo, 0);
  VALUE v = Data_Wrap_Struct(rb_cObject, 0, 0, data);
  rb_add_data_to_function(rb_cFoo, "foo", v);</pre>

  

Analysis

This solution allows data to be attached to a method (cfunc). The atached data stays with the method even if the method's name is changed, since it is attached to the node defining the method.

Implementation

ruby picture
Comments Current voting

I understand your demand, so that I will not reject this RCR. But this feature should be implemented by the different API, so that it can be reentrant. Attached data should be retrieved via some kind of struct that holds calling information.

--matz.


I'm not sure I understand why this solution isn't re-entrant. Can you elaborate?

I do like the idea of retrieving calling information via a struct. Such a struct could hold the name of the function that was called (if available) and the object it was called on, in addition to whatever Ruby object was attached to the function.

-- Paul Brannan


Strongly opposed 0
Opposed 0
Neutral 0
In favor 2
Strongly advocate 0
ruby picture
If you have registered at RCRchive, you may now sign in below. If you have not registered, you may sign up for a username and password. Registering enables you to submit new RCRs, and vote and leave comments on existing RCRs.
Your username:
Your password:

ruby picture

Powered by .