mkxp-z/binding/binding-util.h

525 lines
20 KiB
C
Raw Normal View History

2013-09-01 16:27:21 +02:00
/*
** binding-util.h
**
** This file is part of mkxp.
**
** Copyright (C) 2013 Jonas Kulla <Nyocurio@gmail.com>
**
** mkxp is free software: you can redistribute it and/or modify
** it under the terms of the GNU General Public License as published by
** the Free Software Foundation, either version 2 of the License, or
** (at your option) any later version.
**
** mkxp is distributed in the hope that it will be useful,
** but WITHOUT ANY WARRANTY; without even the implied warranty of
** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
** GNU General Public License for more details.
**
** You should have received a copy of the GNU General Public License
** along with mkxp. If not, see <http://www.gnu.org/licenses/>.
*/
#ifndef BINDING_UTIL_H
#define BINDING_UTIL_H
#include <ruby.h>
#ifndef LEGACY_RUBY
#include <ruby/version.h>
#else
#include <version.h>
#endif
2013-09-01 16:27:21 +02:00
2013-12-31 22:25:07 +01:00
#include "exception.h"
#ifdef RUBY_API_VERSION_MAJOR
#define RAPI_MAJOR RUBY_API_VERSION_MAJOR
#define RAPI_MINOR RUBY_API_VERSION_MINOR
#define RAPI_TEENY RUBY_API_VERSION_TEENY
#else
#define RAPI_MAJOR RUBY_VERSION_MAJOR
#define RAPI_MINOR RUBY_VERSION_MINOR
#define RAPI_TEENY RUBY_VERSION_TEENY
#endif
#define RAPI_FULL ((RAPI_MAJOR * 100) + (RAPI_MINOR * 10) + RAPI_TEENY)
2020-02-03 10:55:50 +01:00
enum RbException {
RGSS = 0,
Reset,
PHYSFS,
SDL,
MKXP,
2013-09-01 16:27:21 +02:00
2020-02-03 10:55:50 +01:00
ErrnoENOENT,
2013-09-01 16:27:21 +02:00
2020-02-03 10:55:50 +01:00
IOError,
2013-09-01 16:27:21 +02:00
2020-02-03 10:55:50 +01:00
TypeError,
ArgumentError,
2013-09-01 16:27:21 +02:00
2020-02-03 10:55:50 +01:00
RbExceptionsMax
2013-09-01 16:27:21 +02:00
};
2020-02-03 10:55:50 +01:00
struct RbData {
VALUE exc[RbExceptionsMax];
2013-09-01 16:27:21 +02:00
2020-02-03 10:55:50 +01:00
/* Input module (RGSS3) */
VALUE buttoncodeHash;
2020-02-03 10:55:50 +01:00
RbData();
~RbData();
2013-09-01 16:27:21 +02:00
};
RbData *getRbData();
struct Exception;
2020-02-03 10:55:50 +01:00
void raiseRbExc(const Exception &exc);
2013-09-01 16:27:21 +02:00
#if RAPI_FULL > 187
2020-02-03 10:55:50 +01:00
#define DECL_TYPE(Klass) extern rb_data_type_t Klass##Type
2013-09-01 16:27:21 +02:00
/* 2.1 has added a new field (flags) to rb_data_type_t */
#if RAPI_FULL >= 210
/* TODO: can mkxp use RUBY_TYPED_FREE_IMMEDIATELY here? */
#define DEF_TYPE_FLAGS 0
#else
#define DEF_TYPE_FLAGS
#endif
2019-08-01 20:55:45 +02:00
#endif
2013-09-01 16:27:21 +02:00
#if RAPI_MAJOR > 1
#if RAPI_FULL < 270
2020-02-03 10:55:50 +01:00
#define DEF_TYPE_CUSTOMNAME_AND_FREE(Klass, Name, Free) \
rb_data_type_t Klass##Type = { \
Name, {0, Free, 0, {0, 0}}, 0, 0, DEF_TYPE_FLAGS}
2020-02-26 17:05:46 +01:00
#else
#define DEF_TYPE_CUSTOMNAME_AND_FREE(Klass, Name, Free) \
rb_data_type_t Klass##Type = {Name, {0, Free, 0, 0, 0}, 0, 0, DEF_TYPE_FLAGS}
2020-02-26 17:05:46 +01:00
#endif
2020-02-03 10:55:50 +01:00
#define DEF_TYPE_CUSTOMFREE(Klass, Free) \
DEF_TYPE_CUSTOMNAME_AND_FREE(Klass, #Klass, Free)
2020-02-03 10:55:50 +01:00
#define DEF_TYPE_CUSTOMNAME(Klass, Name) \
DEF_TYPE_CUSTOMNAME_AND_FREE(Klass, Name, freeInstance<Klass>)
#define DEF_TYPE(Klass) DEF_TYPE_CUSTOMNAME(Klass, #Klass)
2019-07-30 08:13:36 +02:00
#endif
// Ruby 1.8 helper stuff
#if RAPI_FULL <= 187
2019-07-30 08:13:36 +02:00
#define RUBY_T_FIXNUM T_FIXNUM
#define RUBY_T_TRUE T_TRUE
#define RUBY_T_FALSE T_FALSE
#define RUBY_T_NIL T_NIL
#define RUBY_T_UNDEF T_UNDEF
#define RUBY_T_SYMBOL T_SYMBOL
#define RUBY_T_FLOAT T_FLOAT
#define RUBY_T_STRING T_STRING
#define RUBY_T_ARRAY T_ARRAY
#define RUBY_Qtrue Qtrue
#define RUBY_Qfalse Qfalse
#define RUBY_Qnil Qnil
#define RUBY_Qundef Qundef
#define RB_FIXNUM_P(obj) FIXNUM_P(obj)
#define RB_SYMBOL_P(obj) SYMBOL_P(obj)
2020-02-03 10:55:50 +01:00
#define RB_TYPE_P(obj, type) \
(((type) == RUBY_T_FIXNUM) \
? RB_FIXNUM_P(obj) \
: ((type) == RUBY_T_TRUE) \
? ((obj) == RUBY_Qtrue) \
: ((type) == RUBY_T_FALSE) \
? ((obj) == RUBY_Qfalse) \
: ((type) == RUBY_T_NIL) \
? ((obj) == RUBY_Qnil) \
: ((type) == RUBY_T_UNDEF) \
? ((obj) == RUBY_Qundef) \
: ((type) == RUBY_T_SYMBOL) \
? RB_SYMBOL_P(obj) \
: (!SPECIAL_CONST_P(obj) && \
BUILTIN_TYPE(obj) == (type)))
#define OBJ_INIT_COPY(a, b) rb_obj_init_copy(a, b)
#define DEF_ALLOCFUNC_CUSTOMFREE(type, free) \
static VALUE type##Allocate(VALUE klass) { \
return Data_Wrap_Struct(klass, 0, free, 0); \
}
2013-09-01 16:27:21 +02:00
2019-07-30 08:13:36 +02:00
#define DEF_ALLOCFUNC(type) DEF_ALLOCFUNC_CUSTOMFREE(type, freeInstance<type>)
#define rb_str_new_cstr rb_str_new2
#define PRIsVALUE "s"
#endif
// end
#if RAPI_FULL > 187
2020-02-03 10:55:50 +01:00
template <rb_data_type_t *rbType> static VALUE classAllocate(VALUE klass) {
/* 2.3 has changed the name of this function */
#if RAPI_FULL >= 230
2020-02-03 10:55:50 +01:00
return rb_data_typed_object_wrap(klass, 0, rbType);
#else
2020-02-03 10:55:50 +01:00
return rb_data_typed_object_alloc(klass, 0, rbType);
#endif
}
2019-07-30 08:13:36 +02:00
#endif
2020-02-03 10:55:50 +01:00
template <class C> static void freeInstance(void *inst) {
delete static_cast<C *>(inst);
2013-09-01 16:27:21 +02:00
}
2020-02-03 10:55:50 +01:00
void raiseDisposedAccess(VALUE self);
2020-02-03 10:55:50 +01:00
template <class C> inline C *getPrivateData(VALUE self) {
#if RAPI_FULL > 187
2020-02-03 10:55:50 +01:00
C *c = static_cast<C *>(RTYPEDDATA_DATA(self));
2019-07-30 08:13:36 +02:00
#else
2020-02-03 10:55:50 +01:00
C *c = static_cast<C *>(DATA_PTR(self));
2019-07-30 08:13:36 +02:00
#endif
2020-02-03 10:55:50 +01:00
return c;
2013-09-01 16:27:21 +02:00
}
2020-02-03 10:55:50 +01:00
template <class C>
2013-09-01 16:27:21 +02:00
static inline C *
#if RAPI_FULL > 187
getPrivateDataCheck(VALUE self, const rb_data_type_t &type)
2019-07-30 08:13:36 +02:00
#else
getPrivateDataCheck(VALUE self, const char *type)
#endif
2013-09-01 16:27:21 +02:00
{
#if RAPI_FULL <= 187
2020-02-03 10:55:50 +01:00
rb_check_type(self, T_DATA);
VALUE otherObj = rb_const_get(rb_cObject, rb_intern(type));
const char *ownname, *othername;
if (!rb_obj_is_kind_of(self, otherObj)) {
ownname = rb_obj_classname(self);
othername = rb_obj_classname(otherObj);
rb_raise(rb_eTypeError, "Can't convert %s into %s", othername, ownname);
}
void *obj = DATA_PTR(self);
2020-02-26 22:55:13 +01:00
#else
2020-02-26 22:50:53 +01:00
const char *ownname = rb_obj_classname(self);
if (!rb_typeddata_is_kind_of(self, &type))
rb_raise(rb_eTypeError, "Can't convert %s into %s", ownname,
type.wrap_struct_name);
2020-02-03 10:55:50 +01:00
2020-02-26 22:50:53 +01:00
void *obj = RTYPEDDATA_DATA(self);
2020-02-26 22:55:13 +01:00
#endif
2020-02-03 10:55:50 +01:00
return static_cast<C *>(obj);
2013-09-01 16:27:21 +02:00
}
2020-02-03 10:55:50 +01:00
static inline void setPrivateData(VALUE self, void *p) {
#if RAPI_FULL > 187
2020-02-03 10:55:50 +01:00
RTYPEDDATA_DATA(self) = p;
2019-07-30 08:13:36 +02:00
#else
2020-02-03 10:55:50 +01:00
DATA_PTR(self) = p;
2019-07-30 08:13:36 +02:00
#endif
2013-09-01 16:27:21 +02:00
}
inline VALUE
#if RAPI_FULL > 187
2020-02-03 10:55:50 +01:00
wrapObject(void *p, const rb_data_type_t &type, VALUE underKlass = rb_cObject)
2019-07-30 08:13:36 +02:00
#else
wrapObject(void *p, const char *type, VALUE underKlass = rb_cObject)
#endif
2013-09-01 16:27:21 +02:00
{
#if RAPI_FULL > 187
2020-02-03 10:55:50 +01:00
VALUE klass = rb_const_get(underKlass, rb_intern(type.wrap_struct_name));
2019-07-30 08:13:36 +02:00
#else
2020-02-03 10:55:50 +01:00
VALUE klass = rb_const_get(underKlass, rb_intern(type));
2019-07-30 08:13:36 +02:00
#endif
2020-02-03 10:55:50 +01:00
VALUE obj = rb_obj_alloc(klass);
2013-09-01 16:27:21 +02:00
2020-02-03 10:55:50 +01:00
setPrivateData(obj, p);
2013-09-01 16:27:21 +02:00
2020-02-03 10:55:50 +01:00
return obj;
2013-09-01 16:27:21 +02:00
}
2020-02-03 10:55:50 +01:00
inline VALUE wrapProperty(VALUE self, void *prop, const char *iv,
#if RAPI_FULL > 187
2020-02-03 10:55:50 +01:00
const rb_data_type_t &type,
2019-07-30 08:13:36 +02:00
#else
2020-02-03 10:55:50 +01:00
const char *type,
2019-07-30 08:13:36 +02:00
#endif
2020-02-03 10:55:50 +01:00
VALUE underKlass = rb_cObject) {
VALUE propObj = wrapObject(prop, type, underKlass);
2013-09-01 16:27:21 +02:00
2020-02-03 10:55:50 +01:00
rb_iv_set(self, iv, propObj);
2013-09-01 16:27:21 +02:00
2020-02-03 10:55:50 +01:00
return propObj;
2013-09-01 16:27:21 +02:00
}
/* Implemented: oSszfibn| */
2020-02-03 10:55:50 +01:00
int rb_get_args(int argc, VALUE *argv, const char *format, ...);
2013-09-01 16:27:21 +02:00
/* Always terminate 'rb_get_args' with this */
#ifndef NDEBUG
2020-02-03 10:55:50 +01:00
#define RB_ARG_END_VAL ((void *)-1)
#define RB_ARG_END , RB_ARG_END_VAL
#else
2020-02-03 10:55:50 +01:00
#define RB_ARG_END
#endif
2013-09-01 16:27:21 +02:00
typedef VALUE (*RubyMethod)(int argc, VALUE *argv, VALUE self);
2020-02-03 10:55:50 +01:00
static inline void _rb_define_method(VALUE klass, const char *name,
RubyMethod func) {
rb_define_method(klass, name, RUBY_METHOD_FUNC(func), -1);
2013-09-01 16:27:21 +02:00
}
2020-02-03 10:55:50 +01:00
static inline void rb_define_class_method(VALUE klass, const char *name,
RubyMethod func) {
rb_define_singleton_method(klass, name, RUBY_METHOD_FUNC(func), -1);
2013-09-01 16:27:21 +02:00
}
2020-02-03 10:55:50 +01:00
static inline void _rb_define_module_function(VALUE module, const char *name,
RubyMethod func) {
rb_define_module_function(module, name, RUBY_METHOD_FUNC(func), -1);
2013-09-01 16:27:21 +02:00
}
2020-02-03 10:55:50 +01:00
#define GUARD_EXC(exp) \
{ \
try { \
exp \
} catch (const Exception &exc) { \
raiseRbExc(exc); \
} \
}
2020-02-03 10:55:50 +01:00
template <class C>
static inline VALUE objectLoad(int argc, VALUE *argv, VALUE self) {
const char *data;
int dataLen;
rb_get_args(argc, argv, "s", &data, &dataLen RB_ARG_END);
2013-09-01 16:27:21 +02:00
2020-02-03 10:55:50 +01:00
VALUE obj = rb_obj_alloc(self);
2013-09-01 16:27:21 +02:00
2020-02-03 10:55:50 +01:00
C *c = 0;
2020-02-03 10:55:50 +01:00
GUARD_EXC(c = C::deserialize(data, dataLen););
2013-09-01 16:27:21 +02:00
2020-02-03 10:55:50 +01:00
setPrivateData(obj, c);
2013-09-01 16:27:21 +02:00
2020-02-03 10:55:50 +01:00
return obj;
2013-09-01 16:27:21 +02:00
}
2020-02-03 10:55:50 +01:00
static inline VALUE rb_bool_new(bool value) { return value ? Qtrue : Qfalse; }
2013-09-01 16:27:21 +02:00
2020-02-03 10:55:50 +01:00
inline void rb_float_arg(VALUE arg, double *out, int argPos = 0) {
switch (rb_type(arg)) {
case RUBY_T_FLOAT:
*out = RFLOAT_VALUE(arg);
break;
case RUBY_T_FIXNUM:
*out = FIX2INT(arg);
break;
default:
rb_raise(rb_eTypeError, "Argument %d: Expected float", argPos);
}
}
2020-02-03 10:55:50 +01:00
inline void rb_int_arg(VALUE arg, int *out, int argPos = 0) {
switch (rb_type(arg)) {
case RUBY_T_FLOAT:
// FIXME check int range?
*out = NUM2LONG(arg);
break;
case RUBY_T_FIXNUM:
*out = FIX2INT(arg);
break;
default:
rb_raise(rb_eTypeError, "Argument %d: Expected fixnum", argPos);
}
}
2020-02-03 10:55:50 +01:00
inline void rb_bool_arg(VALUE arg, bool *out, int argPos = 0) {
switch (rb_type(arg)) {
case RUBY_T_TRUE:
*out = true;
break;
case RUBY_T_FALSE:
case RUBY_T_NIL:
*out = false;
break;
default:
rb_raise(rb_eTypeError, "Argument %d: Expected bool", argPos);
}
}
2020-02-03 10:55:50 +01:00
inline void rb_check_argc(int actual, int expected) {
if (actual != expected)
rb_raise(rb_eArgError, "wrong number of arguments (%d for %d)", actual,
expected);
}
#if RAPI_FULL <= 187
2020-02-03 10:55:50 +01:00
static inline void rb_error_arity(int argc, int min, int max) {
if (argc > max || argc < min)
rb_raise(rb_eArgError, "Finish me! rb_error_arity()"); // TODO
2019-07-30 08:13:36 +02:00
}
2020-02-03 10:55:50 +01:00
static inline VALUE rb_sprintf(const char *fmt, ...) {
return rb_str_new2("Finish me! rb_sprintf()"); // TODO
2019-07-30 08:13:36 +02:00
}
2020-02-03 10:55:50 +01:00
static inline VALUE rb_str_catf(VALUE obj, const char *fmt, ...) {
return rb_str_new2("Finish me! rb_str_catf()"); // TODO
2019-07-30 08:13:36 +02:00
}
2020-02-03 10:55:50 +01:00
static inline VALUE rb_file_open_str(VALUE filename, const char *mode) {
return rb_funcall(rb_cFile, rb_intern("open"), 2, filename,
rb_str_new2(mode));
2019-07-30 08:13:36 +02:00
}
#endif
2020-02-03 10:55:50 +01:00
#define RB_METHOD(name) static VALUE name(int argc, VALUE *argv, VALUE self)
#define RB_UNUSED_PARAM \
{ \
(void)argc; \
(void)argv; \
(void)self; \
}
#define MARSH_LOAD_FUN(Typ) \
RB_METHOD(Typ##Load) { return objectLoad<Typ>(argc, argv, self); }
#define INITCOPY_FUN(Klass) \
RB_METHOD(Klass##InitializeCopy) { \
VALUE origObj; \
rb_get_args(argc, argv, "o", &origObj RB_ARG_END); \
if (!OBJ_INIT_COPY(self, origObj)) /* When would this fail??*/ \
return self; \
Klass *orig = getPrivateData<Klass>(origObj); \
Klass *k = 0; \
GUARD_EXC(k = new Klass(*orig);) \
setPrivateData(self, k); \
return self; \
}
2013-09-01 16:27:21 +02:00
/* Object property which is copied by reference, with allowed NIL
* FIXME: Getter assumes prop is disposable,
* because self.disposed? is not checked in this case.
* Should make this more clear */
#if RAPI_FULL > 187
2020-02-03 10:55:50 +01:00
#define DEF_PROP_OBJ_REF(Klass, PropKlass, PropName, prop_iv) \
RB_METHOD(Klass##Get##PropName) { \
RB_UNUSED_PARAM; \
return rb_iv_get(self, prop_iv); \
} \
RB_METHOD(Klass##Set##PropName) { \
RB_UNUSED_PARAM; \
rb_check_argc(argc, 1); \
Klass *k = getPrivateData<Klass>(self); \
VALUE propObj = *argv; \
PropKlass *prop; \
if (NIL_P(propObj)) \
prop = 0; \
else \
prop = getPrivateDataCheck<PropKlass>(propObj, PropKlass##Type); \
GUARD_EXC(k->set##PropName(prop);) \
rb_iv_set(self, prop_iv, propObj); \
return propObj; \
}
2019-07-30 08:13:36 +02:00
#else
2020-02-03 10:55:50 +01:00
#define DEF_PROP_OBJ_REF(Klass, PropKlass, PropName, prop_iv) \
RB_METHOD(Klass##Get##PropName) { \
RB_UNUSED_PARAM; \
return rb_iv_get(self, prop_iv); \
} \
RB_METHOD(Klass##Set##PropName) { \
RB_UNUSED_PARAM; \
rb_check_argc(argc, 1); \
Klass *k = getPrivateData<Klass>(self); \
VALUE propObj = *argv; \
PropKlass *prop; \
if (NIL_P(propObj)) \
prop = 0; \
else \
prop = getPrivateDataCheck<PropKlass>(propObj, #PropKlass); \
GUARD_EXC(k->set##PropName(prop);) \
rb_iv_set(self, prop_iv, propObj); \
return propObj; \
}
2019-07-30 08:13:36 +02:00
#endif
2013-09-01 16:27:21 +02:00
/* Object property which is copied by value, not reference */
#if RAPI_FULL > 187
2020-02-03 10:55:50 +01:00
#define DEF_PROP_OBJ_VAL(Klass, PropKlass, PropName, prop_iv) \
RB_METHOD(Klass##Get##PropName) { \
RB_UNUSED_PARAM; \
checkDisposed<Klass>(self); \
return rb_iv_get(self, prop_iv); \
} \
RB_METHOD(Klass##Set##PropName) { \
rb_check_argc(argc, 1); \
Klass *k = getPrivateData<Klass>(self); \
VALUE propObj = *argv; \
PropKlass *prop; \
prop = getPrivateDataCheck<PropKlass>(propObj, PropKlass##Type); \
GUARD_EXC(k->set##PropName(*prop);) \
return propObj; \
}
2019-07-30 08:13:36 +02:00
#else
2020-02-03 10:55:50 +01:00
#define DEF_PROP_OBJ_VAL(Klass, PropKlass, PropName, prop_iv) \
RB_METHOD(Klass##Get##PropName) { \
RB_UNUSED_PARAM; \
checkDisposed<Klass>(self); \
return rb_iv_get(self, prop_iv); \
} \
RB_METHOD(Klass##Set##PropName) { \
rb_check_argc(argc, 1); \
Klass *k = getPrivateData<Klass>(self); \
VALUE propObj = *argv; \
PropKlass *prop; \
prop = getPrivateDataCheck<PropKlass>(propObj, #PropKlass); \
GUARD_EXC(k->set##PropName(*prop);) \
return propObj; \
}
2019-07-30 08:13:36 +02:00
#endif
2013-09-01 16:27:21 +02:00
2020-02-03 10:55:50 +01:00
#define DEF_PROP(Klass, type, PropName, arg_fun, value_fun) \
RB_METHOD(Klass##Get##PropName) { \
RB_UNUSED_PARAM; \
Klass *k = getPrivateData<Klass>(self); \
type value = 0; \
GUARD_EXC(value = k->get##PropName();) \
return value_fun(value); \
} \
RB_METHOD(Klass##Set##PropName) { \
rb_check_argc(argc, 1); \
Klass *k = getPrivateData<Klass>(self); \
type value; \
rb_##arg_fun##_arg(*argv, &value); \
GUARD_EXC(k->set##PropName(value);) \
return *argv; \
}
#define DEF_PROP_I(Klass, PropName) \
DEF_PROP(Klass, int, PropName, int, rb_fix_new)
#define DEF_PROP_F(Klass, PropName) \
DEF_PROP(Klass, double, PropName, float, rb_float_new)
#define DEF_PROP_B(Klass, PropName) \
DEF_PROP(Klass, bool, PropName, bool, rb_bool_new)
#define INIT_PROP_BIND(Klass, PropName, prop_name_s) \
{ \
_rb_define_method(klass, prop_name_s, Klass##Get##PropName); \
_rb_define_method(klass, prop_name_s "=", Klass##Set##PropName); \
}
2013-09-01 16:27:21 +02:00
#endif // BINDING_UTIL_H