1
0
Fork 0
mirror of https://github.com/rubyjs/therubyracer synced 2023-03-27 23:21:42 -04:00
therubyracer/ext/v8/pointer.h
2015-06-29 22:42:36 -05:00

220 lines
6.7 KiB
C++

// -*- mode: c++ -*-
#ifndef RR_POINTER
#define RR_POINTER
namespace rr {
/**
* 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) {};
/**
* 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 {
this->pointer = NULL;
}
};
/**
* 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() {
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);
}
protected:
T* pointer;
};
/**
* Some C++ template nonsense.
*/
template <class T> VALUE Pointer<T>::Class;
}
#endif