|
| 1 | +// -*- mode: c++ -*- |
| 2 | +#ifndef RR_WRAPPER_H |
| 3 | +#define RR_WRAPPER_H |
| 4 | + |
| 5 | +namespace rr { |
| 6 | + |
| 7 | + /** |
| 8 | + * A wrapper provides the ability to create a temporary reference to |
| 9 | + * a stack-allocated value so that it can be passed to ruby. This is |
| 10 | + * normally the case for a callback parameter, where you will be |
| 11 | + * calling into Ruby from C++. Ruby should not call methods on this |
| 12 | + * object outside the scope of the function call. E.g. |
| 13 | + * |
| 14 | + * class MyObjectWrapper : public Wrapper<MyObject> { |
| 15 | + * // [...] |
| 16 | + * }; |
| 17 | + * |
| 18 | + * MyObject foo; |
| 19 | + * rb_funcall(object, rb_intern("count"), 1, MyObjectWrapper(foo)); |
| 20 | + * // ruby should not use anymore, it might crash |
| 21 | + * |
| 22 | + * Note: This seems dangerous in practice, but since it is only ever |
| 23 | + * used by the low-level api, wrapper objects do not escape into the |
| 24 | + * wild, and so aren't used beyond their normal lifetime on the C++ |
| 25 | + * stack. |
| 26 | + * |
| 27 | + * We may add a destructor that invalidates the pointer, |
| 28 | + * and would throw an exception if you tried to use this object at |
| 29 | + * some after it leaves scope. |
| 30 | + */ |
| 31 | + template <class T> |
| 32 | + class Wrapper { |
| 33 | + public: |
| 34 | + /** |
| 35 | + * The Ruby Class of this Wrapper |
| 36 | + */ |
| 37 | + static VALUE Class; |
| 38 | + |
| 39 | + /** |
| 40 | + * Package up this wrapper so that it can be accessed from |
| 41 | + * Ruby. Use this just before passing to a ruby call. E.g. |
| 42 | + * |
| 43 | + * MyObject object; |
| 44 | + * rb_funcall(thing, rb_intern("take"), 1, MyObjectWrapper(object)) |
| 45 | + */ |
| 46 | + Wrapper(T content) : container(new Container(content)) {} |
| 47 | + |
| 48 | + /** |
| 49 | + * Access the underlying object when it has been passed from Ruby |
| 50 | + * to a C++ function. E.g. |
| 51 | + * |
| 52 | + * SomeFunction(VALUE self) { |
| 53 | + * MyObjectWrapper object(self); |
| 54 | + * } |
| 55 | + */ |
| 56 | + Wrapper(VALUE self) : container(Container::unwrap(self)) {} |
| 57 | + |
| 58 | + /** |
| 59 | + * Access the wrapped value via pointer dereference: |
| 60 | + * |
| 61 | + * MyObjectWrapper object(self); |
| 62 | + * object->SomeMethod(); |
| 63 | + */ |
| 64 | + inline T* operator ->() { |
| 65 | + return &container->content; |
| 66 | + } |
| 67 | + |
| 68 | + /** |
| 69 | + * Convert this Wrapper into a Ruby VALUE so that it can be |
| 70 | + * transparently passed to methods expecting a VALUE. This means |
| 71 | + * you can do things like: |
| 72 | + * |
| 73 | + * MyObjectWrapper wrapper(self); |
| 74 | + * rb_inspect(wrapper); |
| 75 | + * |
| 76 | + * Even though MyObjectWrapper is not a VALUE |
| 77 | + */ |
| 78 | + inline operator VALUE() { |
| 79 | + return Container::wrap(container, Class); |
| 80 | + } |
| 81 | + |
| 82 | + /** |
| 83 | + * This is the struct that will be held inside the Ruby T_DATA |
| 84 | + * object. It mainly serves to keep a reference to the stack value. |
| 85 | + */ |
| 86 | + struct Container { |
| 87 | + |
| 88 | + Container(T content_) : content(content_) {} |
| 89 | + |
| 90 | + static inline VALUE wrap(Container* container, VALUE rb_class) { |
| 91 | + return Data_Wrap_Struct(rb_class, 0, &destroy, container); |
| 92 | + } |
| 93 | + |
| 94 | + static inline Container* unwrap(VALUE object) { |
| 95 | + Container* container; |
| 96 | + Data_Get_Struct(object, struct Container, container); |
| 97 | + return container; |
| 98 | + } |
| 99 | + |
| 100 | + static void destroy(Container* container) { |
| 101 | + delete container; |
| 102 | + } |
| 103 | + T content; |
| 104 | + }; |
| 105 | + |
| 106 | + Container* container; |
| 107 | + }; |
| 108 | + |
| 109 | + template <class T> |
| 110 | + VALUE Wrapper<T>::Class; |
| 111 | +} |
| 112 | + |
| 113 | +#endif /* RR_WRAPPER_H */ |
0 commit comments