mirror of
https://github.com/rubyjs/therubyracer
synced 2023-03-27 23:21:42 -04:00
add documentation for pointer class
This commit is contained in:
parent
d1009c1429
commit
902a6f25c3
1 changed files with 173 additions and 28 deletions
201
ext/v8/pointer.h
201
ext/v8/pointer.h
|
@ -1,38 +1,122 @@
|
|||
// -*- mode: c++ -*-
|
||||
#ifndef RR_POINTER
|
||||
#define RR_POINTER
|
||||
|
||||
namespace rr {
|
||||
|
||||
/**
|
||||
* A pointer to V8 object managed by Ruby
|
||||
*
|
||||
* You deal with V8 objects as either pointers or handles.
|
||||
* While handles are managed by the V8 garbage collector, pointers
|
||||
* must be explicitly created and destroyed by your code.
|
||||
*
|
||||
* The pointer class provides a handly way to wrap V8 pointers
|
||||
* into Ruby objects so that they will be deleted when the
|
||||
* Ruby object is garbage collected. Automatic type coercion is
|
||||
* used to make wrapping and unwrapping painless.
|
||||
*
|
||||
* To create Ruby VALUE:
|
||||
*
|
||||
* Pointer<v8::ScriptOrigin> ptr(new v8::ScriptOrigin());
|
||||
* VALUE value = ptr; //automatically wraps in Data_Wrap_Struct
|
||||
*
|
||||
* Conversely, the pointer can be unwrapped from a struct
|
||||
* created in this way and the underlying methods can be
|
||||
* invoked:
|
||||
*
|
||||
* VALUE value = ...;
|
||||
* Pointer<v8::ScriptOrigin> ptr(value);
|
||||
* ptr->CallMethod();
|
||||
*/
|
||||
template <class T>
|
||||
class Pointer {
|
||||
* Conversion between a native C/C++ pointer and a Ruby Object.
|
||||
*
|
||||
* Use this class When you have a native C/C++ pointer that you want
|
||||
* to make appear as a Ruby object. It will take care of all the
|
||||
* book keeping like registering GC callbacks. It is also used to
|
||||
* access from C/C++ a pointer that you had previously passed to Ruby.
|
||||
*
|
||||
* Suppose we have a C++ class `Greeter`.
|
||||
*
|
||||
* class Greeter {
|
||||
* public:
|
||||
* void sayHello() {
|
||||
* println("Hello");
|
||||
* }
|
||||
* }
|
||||
*
|
||||
* and that we want to pass an instance of that class to the
|
||||
* following Ruby method:
|
||||
*
|
||||
* class Greet
|
||||
* def self.greet(greeter)
|
||||
* //we want greeter to wrap our C++ object.
|
||||
* greeter.say_hello()
|
||||
* end
|
||||
* end
|
||||
*
|
||||
* We could do that from C by using a `Pointer` specified for
|
||||
* `Greeter`:
|
||||
*
|
||||
* Greeter* greeter = new Greeter();
|
||||
*
|
||||
* // get reference to Ruby `Greet` class from C
|
||||
* VALUE GreeterClass = rb_define_class("Greet");
|
||||
* Pointer<Greeter> native_greeter(greeter);
|
||||
*
|
||||
* //now we can pass it to Ruby's `Greeter#new`
|
||||
* rb_funcall(GreeterClass, rb_intern("greet"), 1, native_greeter);
|
||||
*
|
||||
* Once it is passed to Ruby, it is NO LONGER YOURS. It's lifecycle
|
||||
* now belongs to Ruby, and you must not delete the pointer by
|
||||
* hand.
|
||||
*
|
||||
* If we were to run the example above it would actually fail
|
||||
* because by default, the Ruby Class of the native pointer is
|
||||
* actually an opaque `Object`, and there is no method `say_hello`
|
||||
* on `Object`. In order to make it work, we have to specify which
|
||||
* class we want Ruby to treat our pointer as. We do that by setting
|
||||
* the static Pointer<T>::Class variable.
|
||||
*
|
||||
* Pointer<Greeter>::Class = rb_define_class("NativeGreeter");
|
||||
*
|
||||
* Now, when we pass `Pointer<Greeter>(new Greeter())` to Ruby code,
|
||||
* it will appear to have the class `NativeGreeter`. We can now
|
||||
* define our `say_hello` method on that class in order to call the
|
||||
* underlying C++ method. We can construct a `Pointer<Greeter>` from
|
||||
* an instance of `NativeGreeter` and use it to automatically unwrap
|
||||
* the C++ `Greeter` object and call its methods:
|
||||
*
|
||||
*
|
||||
* static VALUE say_hello(VALUE self) {
|
||||
* //unwrap the native `Greeter` and call its `sayHello()`
|
||||
* Pointer<Greeter>(self)->sayHello();
|
||||
* return Qnil;
|
||||
* }
|
||||
* VALUE NativeGreeter = Pointer<Greeter>::Class;
|
||||
* rb_define_method(NativeGreeter, "say_hello", (VALUE (*)(...))&say_hello, 0);
|
||||
*
|
||||
* Notice how we were able to just call the `sayHello()` method
|
||||
* directly on the instance of Pointer<Greeter>? That's because it
|
||||
* will automatically unbox and dereference the pointer so that you
|
||||
* can automatically forward calls to the underlying object it wraps.
|
||||
*
|
||||
* Most of the time The Ruby Racer codebase uses subclassing to
|
||||
* specify a Pointer. For example, `rr:Isolate ` looks like this:
|
||||
*
|
||||
* class Isolate : public Poiter<v8::Isolate> {
|
||||
* //....
|
||||
* }
|
||||
*
|
||||
* which means that it can be used in all the cases above without
|
||||
* any template specifier:
|
||||
*
|
||||
* //wrap
|
||||
* Isolate isolate(new v8::Isolate());
|
||||
*
|
||||
* //unwrap
|
||||
* VALUE rubyIsolate = getRubyObjectContainingIsolate();
|
||||
* Isolate isolate(rubyIsolate);
|
||||
*
|
||||
* //Ruby class for Isolate
|
||||
* Isolate::Class //=> V8::C::Isolate
|
||||
*
|
||||
*/
|
||||
template <class T> class Pointer {
|
||||
public:
|
||||
/**
|
||||
* Construct a `Pointer` from a C/C++ pointer. This is normally
|
||||
* done in order to convert it into a Ruby `VALUE`. E.g.
|
||||
*
|
||||
* Greeter* greeter = new Greeter();
|
||||
* rb_funcall(Pointer<Greeter>(greeter), rb_intern("to_s"));
|
||||
*/
|
||||
inline Pointer(T* t) : pointer(t) {};
|
||||
inline Pointer(VALUE v) {
|
||||
|
||||
/**
|
||||
* Construct a Pointer from a C/C++ pointer that was previously
|
||||
* passed to Ruby. Use this access and call methods on the
|
||||
* underlying pointer:
|
||||
*
|
||||
* Pointer<Greeter>(rubyValue)->sayHello();
|
||||
*/
|
||||
inline Pointer(VALUE v) : {
|
||||
if (RTEST(v)) {
|
||||
this->unwrap(v);
|
||||
} else {
|
||||
|
@ -40,26 +124,84 @@ namespace rr {
|
|||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Enable transparent pointer dereferencing via the `*` operator:
|
||||
*
|
||||
* *Pointer<Greeter>(new Greeter()).sayHello();
|
||||
*/
|
||||
inline operator T*() {
|
||||
return pointer;
|
||||
}
|
||||
|
||||
/**
|
||||
* Enable transparent pointer dereferencing via the `->` operator:
|
||||
*
|
||||
* Pointer<Greeter>(new Greeter())->sayHello();
|
||||
*/
|
||||
inline T* operator ->() {
|
||||
return pointer;
|
||||
}
|
||||
|
||||
/**
|
||||
* Coerce this Pointer into a VALUE. Once this happens, either by
|
||||
* assigning it to variable of type VALUE, casting it to (VALUE),
|
||||
* or passing it to a function that takes a VALUE as a parameter,
|
||||
* this pointer now belongs to Ruby, and you should not `delete`
|
||||
* it.
|
||||
*/
|
||||
inline operator VALUE() {
|
||||
return Data_Wrap_Struct(Class, 0, &release, pointer);
|
||||
VALUE RubyClass = Class ? Class : rb_cObject;
|
||||
return Data_Wrap_Struct(RubyClass, 0, &release, pointer);
|
||||
}
|
||||
|
||||
/**
|
||||
* Subclasses implement this to uwrap their particular
|
||||
* datastructure. For example, the implementation for `Isolate`
|
||||
* looks like:
|
||||
*
|
||||
* void Pointer<v8::Isolate>::unwrap(VALUE value) {
|
||||
* Data_Get_Struct(value, class v8::Isolate, pointer);
|
||||
* }
|
||||
*
|
||||
* TODO: I would really like to get rid of this and it
|
||||
* seems like it ought to be templatable, but I couldn't figure
|
||||
* out a way to get the C++ compiler to play ball. Basically, it
|
||||
* seems like there ought to be a generic function like:
|
||||
*
|
||||
* void inline unwrap(VALUE value) {
|
||||
* Dat_Get_Struct(value, class T, pointer);
|
||||
* }
|
||||
*
|
||||
* but it wouldn't work for me.
|
||||
*/
|
||||
void unwrap(VALUE value);
|
||||
|
||||
|
||||
/**
|
||||
* A static method to delete this pointer. It is implemented as a
|
||||
* static method so that a function pointer to it can be passed to
|
||||
* Ruby so that it can be called from Ruby when the object is
|
||||
* garbage collected.
|
||||
* TODO: rename to `destroy()`
|
||||
*/
|
||||
static void release(T* pointer) {
|
||||
delete pointer;
|
||||
}
|
||||
|
||||
/**
|
||||
* Storage for the Class variable. This is where you write to
|
||||
* determine what Ruby class this Pointer will have:
|
||||
*
|
||||
* Pointer<Greeter>::Class = rb_define_class("NativeGreeter");
|
||||
*/
|
||||
static VALUE Class;
|
||||
|
||||
/**
|
||||
* See if two `Pointer<T>`s are equal by comparing their memory
|
||||
* addresses.
|
||||
*
|
||||
* TODO: overload the `==` operator
|
||||
*/
|
||||
static inline VALUE PointerEquals(VALUE self, VALUE other) {
|
||||
return Bool(Pointer<T>(self).pointer == Pointer<T>(other).pointer);
|
||||
}
|
||||
|
@ -68,6 +210,9 @@ namespace rr {
|
|||
T* pointer;
|
||||
};
|
||||
|
||||
/**
|
||||
* Some C++ template nonsense.
|
||||
*/
|
||||
template <class T> VALUE Pointer<T>::Class;
|
||||
|
||||
}
|
||||
|
|
Loading…
Add table
Reference in a new issue