Skip to content

Commit

Permalink
func_wrap reorganization, and small make_object fix
Browse files Browse the repository at this point in the history
git-svn-id: http://svn.dsource.org/projects/pyd/trunk@50 1df65b71-e716-0410-9316-ac55df2b1602
  • Loading branch information
KirkMcDonald authored and KirkMcDonald committed Dec 3, 2006
1 parent c71e2a4 commit b4a7dfd
Show file tree
Hide file tree
Showing 6 changed files with 127 additions and 98 deletions.
12 changes: 6 additions & 6 deletions infrastructure/pyd/class_wrap.d
Original file line number Diff line number Diff line change
Expand Up @@ -213,8 +213,8 @@ template wrapped_get(T, alias Fn) {
/// A generic wrapper around a "getter" property.
extern(C)
PyObject* func(PyObject* self, void* closure) {
// func_wrap already catches exceptions
return func_wrap!(Fn, 0, T, property_parts!(Fn).getter_type).func(self, null);
// method_wrap already catches exceptions
return method_wrap!(T, Fn, property_parts!(Fn).getter_type).func(self, null);
}
}

Expand All @@ -228,7 +228,7 @@ template wrapped_set(T, alias Fn) {
scope(exit) Py_DECREF(temp_tuple);
Py_INCREF(value);
PyTuple_SetItem(temp_tuple, 0, value);
PyObject* res = func_wrap!(Fn, 1, T, property_parts!(Fn).setter_type).func(self, temp_tuple);
PyObject* res = method_wrap!(T, Fn, property_parts!(Fn).setter_type).func(self, temp_tuple);
// If we get something back, we need to DECREF it.
if (res) Py_DECREF(res);
// If we don't, propagate the exception
Expand Down Expand Up @@ -261,13 +261,13 @@ template wrapped_class(T, char[] classname = symbolnameof!(T)) {
* fn_t = The type of the function. It is only useful to specify this
* if more than one function has the same name as this one.
*/
template def(alias fn, char[] name = symbolnameof!(fn), fn_t=typeof(&fn), uint MIN_ARGS = minArgs!(fn, fn_t)) {
template def(alias fn, char[] name = symbolnameof!(fn), fn_t=typeof(&fn)) {
pragma(msg, "class.def: " ~ name);
static void def() {
static PyMethodDef empty = { null, null, 0, null };
alias wrapped_method_list!(T) list;
list[length-1].ml_name = name ~ \0;
list[length-1].ml_meth = &func_wrap!(fn, MIN_ARGS, T, fn_t).func;
list[length-1].ml_meth = &method_wrap!(T, fn, fn_t).func;
list[length-1].ml_flags = METH_VARARGS;
list[length-1].ml_doc = "";
list ~= empty;
Expand Down Expand Up @@ -412,7 +412,7 @@ void finalize_class(CLS) (CLS cls) {
}
// opCall
static if (is(typeof(&T.opCall))) {
type.tp_call = cast(ternaryfunc)&func_wrap!(T.opCall, minArgs!(T.opCall), T).func;
type.tp_call = cast(ternaryfunc)&method_wrap!(T, T.opCall, typeof(&T.opCall)).func;
}

// If a ctor wasn't supplied, try the default.
Expand Down
13 changes: 11 additions & 2 deletions infrastructure/pyd/ctor_wrap.d
Original file line number Diff line number Diff line change
Expand Up @@ -68,8 +68,17 @@ template wrapped_ctors(T, C ...) {
foreach(i, arg; c) {
alias ParameterTypeTuple!(typeof(arg)) Ctor;
if (Ctor.length == len) {
alias call_ctor!(T, ParameterTypeTuple!(typeof(arg))) fn;
WrapPyObject_SetObj(self, py_call(&fn, args));
auto fn = &call_ctor!(T, ParameterTypeTuple!(typeof(arg)));
if (fn is null) {
PyErr_SetString(PyExc_RuntimeError, "Couldn't get pointer to class ctor redirect.");
return -1;
}
T t = applyPyTupleToDelegate(fn, args);
if (t is null) {
PyErr_SetString(PyExc_RuntimeError, "Class ctor redirect didn't return a class instance!");
return -1;
}
WrapPyObject_SetObj(self, t);
return 0;
}
}
Expand Down
2 changes: 1 addition & 1 deletion infrastructure/pyd/def.d
Original file line number Diff line number Diff line change
Expand Up @@ -79,7 +79,7 @@ template def(alias fn, char[] name = symbolnameof!(fn), fn_t=typeof(&fn), uint M
alias module_global_methods list;

list[length-1].ml_name = name ~ \0;
list[length-1].ml_meth = &func_wrap!(fn, MIN_ARGS, void, fn_t).func;
list[length-1].ml_meth = &function_wrap!(fn, MIN_ARGS, fn_t).func;
list[length-1].ml_flags = METH_VARARGS;
list[length-1].ml_doc = "";
list ~= empty;
Expand Down
182 changes: 101 additions & 81 deletions infrastructure/pyd/func_wrap.d
Original file line number Diff line number Diff line change
Expand Up @@ -34,9 +34,10 @@ private {

import std.string;
import std.traits;
import std.stdio;
}

// Builds a Python callable object from a delegate or function pointer.
// Builds a callable Python object from a delegate or function pointer.
PyObject* PydFunc_FromDelegate(T) (T dg) {
alias wrapped_class_type!(T) type;
alias wrapped_class_object!(T) obj;
Expand Down Expand Up @@ -90,38 +91,101 @@ void setWrongArgsError(int gotArgs, uint minArgs, uint maxArgs, char[] funcName=
PyErr_SetString(PyExc_TypeError, str ~ \0);
}

// Calls the passed function with the passed Python tuple.
ReturnType!(fn_t) py_call(fn_t, PY)(fn_t fn, PY* args) {
// Calls callable alias fn with PyTuple args.
ReturnType!(fn_t) applyPyTupleToAlias(alias fn, fn_t, uint MIN_ARGS) (PyObject* args) {
alias ParameterTypeTuple!(fn_t) T;
const uint MAX_ARGS = T.length;
alias ReturnType!(fn_t) RT;

int ARGS = 0;
int argCount = 0;
// This can make it more convenient to call this with 0 args.
if (args !is null) {
ARGS = PyObject_Length(args);
argCount = PyObject_Length(args);
}

// Sanity check!
if (ARGS != MAX_ARGS) {
setWrongArgsError(ARGS, MAX_ARGS, MAX_ARGS);
if (argCount < MIN_ARGS || argCount > MAX_ARGS) {
setWrongArgsError(argCount, MIN_ARGS, MAX_ARGS);
handle_exception();
}

static if (MIN_ARGS == 0) {
if (argCount == 0) {
return fn();
}
}
T t;
foreach(i, arg; t) {
const uint argNum = i+1;
if (i < argCount) {
t[i] = d_type!(typeof(arg))(PyTuple_GetItem(args, i));
}
static if (argNum >= MIN_ARGS && argNum <= MAX_ARGS) {
if (argNum == argCount) {
return fn(t[0 .. argNum]);
break;
}
}
}
// This should never get here.
throw new Exception("applyPyTupleToAlias reached end! argCount = " ~ toString(argCount));
static if (!is(RT == void))
return ReturnType!(fn_t).init;
}

// wraps applyPyTupleToAlias to return a PyObject*
PyObject* pyApplyToAlias(alias fn, fn_t, uint MIN_ARGS) (PyObject* args) {
static if (is(ReturnType!(fn_t) == void)) {
applyPyTupleToAlias!(fn, fn_t, MIN_ARGS)(args);
Py_INCREF(Py_None);
return Py_None;
} else {
return _py( applyPyTupleToAlias!(fn, fn_t, MIN_ARGS)(args) );
}
}

ReturnType!(dg_t) applyPyTupleToDelegate(dg_t) (dg_t dg, PyObject* args) {
alias ParameterTypeTuple!(dg_t) T;
const uint ARGS = T.length;
alias ReturnType!(dg_t) RT;

int argCount = 0;
// This can make it more convenient to call this with 0 args.
if (args !is null) {
argCount = PyObject_Length(args);
}

// Sanity check!
if (argCount != ARGS) {
setWrongArgsError(argCount, ARGS, ARGS);
handle_exception();
}

static if (ARGS == 0) {
if (argCount == 0) {
return dg();
}
}
T t;
foreach(i, arg; t) {
t[i] = d_type!(typeof(arg))(PyTuple_GetItem(args, i));
}
return dg(t);
}

static if (is(RT == void)) {
fn(t);
return;
// wraps applyPyTupleToDelegate to return a PyObject*
PyObject* pyApplyToDelegate(dg_t) (dg_t dg, PyObject* args) {
static if (is(ReturnType!(dg_t) == void)) {
applyPyTupleToDelegate(dg, args);
Py_INCREF(Py_None);
return Py_None;
} else {
return fn(t);
return _py( applyPyTupleToDelegate(dg, args) );
}
}

template wrapped_func_call(fn_t) {
const uint ARGS = ParameterTypeTuple!(fn_t).length;
alias ReturnType!(fn_t) RT;
// The entry for the tp_call slot of the PydFunc types.
// (Or: What gets called when you pass a delegate or function pointer to
Expand All @@ -136,89 +200,45 @@ template wrapped_func_call(fn_t) {
fn_t fn = (cast(wrapped_class_object!(fn_t)*)self).d_obj;

return exception_catcher({
static if (is(RT == void)) {
py_call(fn, args);
Py_INCREF(Py_None);
return Py_None;
} else {
return _py( py_call(fn, args) );
}
return pyApplyToDelegate(fn, args);
});
}
}

// This is a handy shortcut that allows us to wrap a function alias directly
// with a PyCFunction.
template func_wrap(alias real_fn, uint MIN_ARGS, C=void, fn_t=typeof(&real_fn)) {
// Wraps a function alias with a PyCFunction.
template function_wrap(alias real_fn, uint MIN_ARGS, fn_t=typeof(&real_fn)) {
alias ParameterTypeTuple!(fn_t) Info;
const uint MAX_ARGS = Info.length;
alias ReturnType!(fn_t) RT;

// Wraps py_call to return a PyObject*
PyObject* py_py_call(fn_t, PY)(fn_t fn, PY* args) {
static if (is(RT == void)) {
py_call(fn, args);
Py_INCREF(Py_None);
return Py_None;
} else {
return _py( py_call(fn, args) );
}
}

// Calls py_py_call with the proper function contained in a tuple
// returned from tuples.func_range.
PyObject* tuple_py_call(PY, T ...)(PY* args, T t) {
int argCount = 0;
if (args !is null)
argCount = PyObject_Length(args);

static if (MIN_ARGS == 0) {
if (argCount == 0)
return py_py_call(&firstArgs!(real_fn, 0, fn_t), args);
}
foreach (i, arg; t) {
if (ParameterTypeTuple!(typeof(arg)).length == argCount) {
return py_py_call(arg, args);
}
}
extern (C)
PyObject* func(PyObject* self, PyObject* args) {
return exception_catcher(delegate PyObject*() {
return pyApplyToAlias!(real_fn, fn_t, MIN_ARGS)(args);
});
}
}

extern (C)
// Wraps a member function alias with a PyCFunction.
template method_wrap(C, alias real_fn, fn_t=typeof(&real_fn)) {
alias ParameterTypeTuple!(fn_t) Info;
const uint ARGS = Info.length;
alias ReturnType!(fn_t) RT;
extern(C)
PyObject* func(PyObject* self, PyObject* args) {
// For some reason, D can't infer the return type of this function
// literal...
return exception_catcher(delegate PyObject*() {
// If C is specified, then this is a method call. We need to pull out
// the object in self and turn the member function alias real_fn
// into a delegate. This conversion is done with a dirty hack; see
// dg_convert.d.
static if (!is(C == void)) {
static assert (MIN_ARGS == MAX_ARGS, "Default arguments with member functions are not supported.");
// Didn't pass a "self" parameter! Ack!
if (self is null) {
PyErr_SetString(PyExc_TypeError, "Wrapped method didn't get a 'self' parameter.");
return null;
}
C instance = (cast(wrapped_class_object!(C)*)self).d_obj;
fn_to_dg!(fn_t) fn = dg_wrapper!(C, fn_t)(instance, &real_fn);
static if (is(ReturnType!(typeof(fn)) == void)) {
py_call(fn, args);
Py_INCREF(Py_None);
return Py_None;
} else {
return _py( py_call(fn, args) );
}
// If C is not specified, then this is just a normal function call.
} else {
alias defaultsTupleT!(real_fn, MIN_ARGS, fn_t).type T;
T t;
defaultsTuple!(real_fn, MIN_ARGS, fn_t)(delegate void(T tu) {
foreach(i, arg; tu) {
t[i] = arg;
}
});
return tuple_py_call(args, t);
// Didn't pass a "self" parameter! Ack!
if (self is null) {
PyErr_SetString(PyExc_TypeError, "Wrapped method didn't get a 'self' parameter.");
return null;
}
C instance = (cast(wrapped_class_object!(C)*)self).d_obj;
if (instance is null) {
PyErr_SetString(PyExc_ValueError, "Wrapped class instance is null!");
return null;
}
fn_to_dg!(fn_t) dg = dg_wrapper!(C, fn_t)(instance, &real_fn);
return pyApplyToDelegate(dg, args);
});
}
}
Expand Down
8 changes: 4 additions & 4 deletions infrastructure/pyd/make_object.d
Original file line number Diff line number Diff line change
Expand Up @@ -143,9 +143,11 @@ PyObject* _py(T) (T t) {
return WrapPyObject_FromObject(t);
}
// If it's not a wrapped type, fall through to the exception.
// This just passes the argument right back through without changing
// its reference count.
// The function expects to be passed a borrowed reference and return an
// owned reference. Thus, if passed a PyObject*, this will increment the
// reference count.
} else static if (is(T : PyObject*)) {
Py_INCREF(t);
return t;
}
PyErr_SetString(PyExc_RuntimeError, "D conversion function _py failed with type " ~ typeid(T).toString());
Expand Down Expand Up @@ -177,8 +179,6 @@ PyObject* PyTuple_FromItems(T ...)(T t) {
*
* Calling this with a PydObject will return back a reference to the very same
* PydObject.
*
* Calling this with a PyObject* will "steal" the reference.
*/
PydObject py(T) (T t) {
static if(is(T : PydObject)) {
Expand Down
8 changes: 4 additions & 4 deletions infrastructure/pyd/op_wrap.d
Original file line number Diff line number Diff line change
Expand Up @@ -146,8 +146,8 @@ template opfunc_binary_wrap(T, alias opfn) {
template opfunc_unary_wrap(T, alias opfn) {
extern(C)
PyObject* func(PyObject* self) {
// func_wrap takes care of exception handling
return func_wrap!(opfn, 0, T).func(self, null);
// method_wrap takes care of exception handling
return method_wrap!(T, opfn, typeof(&opfn)).func(self, null);
}
}

Expand Down Expand Up @@ -204,7 +204,7 @@ template opindex_mapping_pyfunc(T) {
setWrongArgsError(args, ARGS, ARGS);
return null;
}
return func_wrap!(T.opIndex, ARGS, T).func(self, key);
return method_wrap!(T, T.opIndex, typeof(&T.opIndex)).func(self, key);
}
}
}
Expand Down Expand Up @@ -236,7 +236,7 @@ template opindexassign_mapping_pyfunc(T) {
Py_INCREF(PyTuple_GetItem(key, i-1));
PyTuple_SetItem(temp, i, PyTuple_GetItem(key, i-1));
}
func_wrap!(T.opIndexAssign, ARGS, T).func(self, temp);
method_wrap!(T, T.opIndexAssign, typeof(&T.opIndexAssign)).func(self, temp);
return 0;
}
} else {
Expand Down

0 comments on commit b4a7dfd

Please sign in to comment.