Commit a4e4318b authored by John (J5) Palmieri's avatar John (J5) Palmieri

refactor in/out marshalling to be to_py/from_py

* in/out make sense from a C perspective but when you get to the
   python layers it makes more sense to label them as to_py and
   from_py to denote which way we are marshalling
 * this helps clear up the difference between callbacks which
   call into python and invoked functions which call into C
 * in the callback case we marshal in values to Python objects
   and out values to C types but in the invoke case we do the
   reverse.  Dealing with to_py/from_py makes the code much more
   resuable and consistant

https://bugzilla.gnome.org/show_bug.cgi?id=658362
parent 45b0fcff
......@@ -65,10 +65,10 @@ _gi_la_SOURCES = \
pygi-invoke-state-struct.h \
pygi-cache.h \
pygi-cache.c \
pygi-marshal-in.c \
pygi-marshal-in.h \
pygi-marshal-out.c \
pygi-marshal-out.h \
pygi-marshal-from-py.c \
pygi-marshal-from-py.h \
pygi-marshal-to-py.c \
pygi-marshal-to-py.h \
pygi-marshal-cleanup.c \
pygi-marshal-cleanup.h
_gi_la_CFLAGS = \
......
This diff is collapsed.
......@@ -32,16 +32,16 @@ G_BEGIN_DECLS
typedef struct _PyGICallableCache PyGICallableCache;
typedef struct _PyGIArgCache PyGIArgCache;
typedef gboolean (*PyGIMarshalInFunc) (PyGIInvokeState *state,
PyGICallableCache *callable_cache,
PyGIArgCache *arg_cache,
PyObject *py_arg,
GIArgument *arg);
typedef gboolean (*PyGIMarshalFromPyFunc) (PyGIInvokeState *state,
PyGICallableCache *callable_cache,
PyGIArgCache *arg_cache,
PyObject *py_arg,
GIArgument *arg);
typedef PyObject *(*PyGIMarshalOutFunc) (PyGIInvokeState *state,
PyGICallableCache *callable_cache,
PyGIArgCache *arg_cache,
GIArgument *arg);
typedef PyObject *(*PyGIMarshalToPyFunc) (PyGIInvokeState *state,
PyGICallableCache *callable_cache,
PyGIArgCache *arg_cache,
GIArgument *arg);
typedef void (*PyGIMarshalCleanupFunc) (PyGIInvokeState *state,
PyGIArgCache *arg_cache,
......@@ -72,14 +72,28 @@ typedef enum {
* PyGIFunctionType enum consolidates them into one enumeration for ease of
* branching and debugging.
*/
typedef enum {
PYGI_FUNCTION_TYPE_FUNCTION,
PYGI_FUNCTION_TYPE_METHOD,
PYGI_FUNCTION_TYPE_CONSTRUCTOR,
PYGI_FUNCTION_TYPE_VFUNC,
PYGI_FUNCTION_TYPE_CALLBACK
typedef enum {
PYGI_FUNCTION_TYPE_FUNCTION,
PYGI_FUNCTION_TYPE_METHOD,
PYGI_FUNCTION_TYPE_CONSTRUCTOR,
PYGI_FUNCTION_TYPE_VFUNC,
PYGI_FUNCTION_TYPE_CALLBACK
} PyGIFunctionType;
/*
* In PyGI IN and OUT arguments mean different things depending on the context
* of the callable (e.g. is it a callback that is being called from C or a
* function that is being called from python). We don't as much care if the
* parameter is an IN or OUT C parameter, than we do if the parameter is being
* marshalled into Python or from Python.
*/
typedef enum {
PYGI_DIRECTION_TO_PYTHON,
PYGI_DIRECTION_FROM_PYTHON,
PYGI_DIRECTION_BIDIRECTIONAL
} PyGIDirection;
struct _PyGIArgCache
{
const gchar *arg_name;
......@@ -90,16 +104,16 @@ struct _PyGIArgCache
gboolean is_skipped;
gboolean allow_none;
GIDirection direction;
PyGIDirection direction;
GITransfer transfer;
GITypeTag type_tag;
GITypeInfo *type_info;
PyGIMarshalInFunc in_marshaller;
PyGIMarshalOutFunc out_marshaller;
PyGIMarshalFromPyFunc from_py_marshaller;
PyGIMarshalToPyFunc to_py_marshaller;
PyGIMarshalCleanupFunc in_cleanup;
PyGIMarshalCleanupFunc out_cleanup;
PyGIMarshalCleanupFunc from_py_cleanup;
PyGIMarshalCleanupFunc to_py_cleanup;
GDestroyNotify destroy_notify;
......@@ -152,14 +166,14 @@ struct _PyGICallableCache
PyGIArgCache *return_cache;
PyGIArgCache **args_cache;
GSList *out_args;
GSList *to_py_args;
GSList *arg_name_list; /* for keyword arg matching */
GHashTable *arg_name_hash;
/* counts */
gssize n_in_args;
gssize n_out_args;
gssize n_out_child_args;
gssize n_from_py_args;
gssize n_to_py_args;
gssize n_to_py_child_args;
gssize n_args;
gssize n_py_args;
......
......@@ -43,17 +43,17 @@ _invoke_callable (PyGIInvokeState *state,
retval = g_vfunc_info_invoke ( callable_info,
state->implementor_gtype,
state->in_args,
cache->n_in_args,
cache->n_from_py_args,
state->out_args,
cache->n_out_args,
cache->n_to_py_args,
&state->return_arg,
&error);
else
retval = g_function_info_invoke ( callable_info,
state->in_args,
cache->n_in_args,
cache->n_from_py_args,
state->out_args,
cache->n_out_args,
cache->n_to_py_args,
&state->return_arg,
&error);
pyg_end_allow_threads;
......@@ -67,7 +67,7 @@ _invoke_callable (PyGIInvokeState *state,
* We eventually should marshal directly to FFI so we no longer
* have to use the reference implementation
*/
pygi_marshal_cleanup_args_in_marshal_success (state, cache);
pygi_marshal_cleanup_args_from_py_marshal_success (state, cache);
return FALSE;
}
......@@ -76,7 +76,7 @@ _invoke_callable (PyGIInvokeState *state,
if (pyglib_error_check (&(state->error))) {
/* even though we errored out, the call itself was successful,
so we assume the call processed all of the parameters */
pygi_marshal_cleanup_args_in_marshal_success (state, cache);
pygi_marshal_cleanup_args_from_py_marshal_success (state, cache);
return FALSE;
}
}
......@@ -299,20 +299,20 @@ _invoke_state_init_from_callable_cache (PyGIInvokeState *state,
return FALSE;
}
state->in_args = g_slice_alloc0 (cache->n_in_args * sizeof(GIArgument));
if (state->in_args == NULL && cache->n_in_args != 0) {
state->in_args = g_slice_alloc0 (cache->n_from_py_args * sizeof(GIArgument));
if (state->in_args == NULL && cache->n_from_py_args != 0) {
PyErr_NoMemory ();
return FALSE;
}
state->out_values = g_slice_alloc0 (cache->n_out_args * sizeof(GIArgument));
if (state->out_values == NULL && cache->n_out_args != 0) {
state->out_values = g_slice_alloc0 (cache->n_to_py_args * sizeof(GIArgument));
if (state->out_values == NULL && cache->n_to_py_args != 0) {
PyErr_NoMemory ();
return FALSE;
}
state->out_args = g_slice_alloc0 (cache->n_out_args * sizeof(GIArgument));
if (state->out_args == NULL && cache->n_out_args != 0) {
state->out_args = g_slice_alloc0 (cache->n_to_py_args * sizeof(GIArgument));
if (state->out_args == NULL && cache->n_to_py_args != 0) {
PyErr_NoMemory ();
return FALSE;
}
......@@ -326,9 +326,9 @@ static inline void
_invoke_state_clear (PyGIInvokeState *state, PyGICallableCache *cache)
{
g_slice_free1 (cache->n_args * sizeof(GIArgument *), state->args);
g_slice_free1 (cache->n_in_args * sizeof(GIArgument), state->in_args);
g_slice_free1 (cache->n_out_args * sizeof(GIArgument), state->out_args);
g_slice_free1 (cache->n_out_args * sizeof(GIArgument), state->out_values);
g_slice_free1 (cache->n_from_py_args * sizeof(GIArgument), state->in_args);
g_slice_free1 (cache->n_to_py_args * sizeof(GIArgument), state->out_args);
g_slice_free1 (cache->n_to_py_args * sizeof(GIArgument), state->out_values);
Py_XDECREF (state->py_in_args);
}
......@@ -396,7 +396,7 @@ _invoke_marshal_in_args (PyGIInvokeState *state, PyGICallableCache *cache)
PyObject *py_arg = NULL;
switch (arg_cache->direction) {
case GI_DIRECTION_IN:
case PYGI_DIRECTION_FROM_PYTHON:
state->args[i] = &(state->in_args[in_count]);
in_count++;
......@@ -413,9 +413,9 @@ _invoke_marshal_in_args (PyGIInvokeState *state, PyGICallableCache *cache)
/* clean up all of the args we have already marshalled,
* since invoke will not be called
*/
pygi_marshal_cleanup_args_in_parameter_fail (state,
cache,
i - 1);
pygi_marshal_cleanup_args_from_py_parameter_fail (state,
cache,
i - 1);
return FALSE;
}
......@@ -424,7 +424,7 @@ _invoke_marshal_in_args (PyGIInvokeState *state, PyGICallableCache *cache)
arg_cache->py_arg_index);
break;
case GI_DIRECTION_INOUT:
case PYGI_DIRECTION_BIDIRECTIONAL:
/* this will be filled in if it is an child value */
if (state->in_args[in_count].v_pointer != NULL)
state->out_values[out_count] = state->in_args[in_count];
......@@ -439,9 +439,9 @@ _invoke_marshal_in_args (PyGIInvokeState *state, PyGICallableCache *cache)
cache->name,
cache->n_py_args,
state->n_py_in_args);
pygi_marshal_cleanup_args_in_parameter_fail (state,
cache,
i - 1);
pygi_marshal_cleanup_args_from_py_parameter_fail (state,
cache,
i - 1);
return FALSE;
}
......@@ -449,15 +449,15 @@ _invoke_marshal_in_args (PyGIInvokeState *state, PyGICallableCache *cache)
PyTuple_GET_ITEM (state->py_in_args,
arg_cache->py_arg_index);
}
case GI_DIRECTION_OUT:
case PYGI_DIRECTION_TO_PYTHON:
if (arg_cache->is_caller_allocates) {
if (!_caller_alloc (state, arg_cache, i, out_count)) {
PyErr_Format (PyExc_TypeError,
"Could not caller allocate argument %zd of callable %s",
i, cache->name);
pygi_marshal_cleanup_args_in_parameter_fail (state,
cache,
i - 1);
pygi_marshal_cleanup_args_from_py_parameter_fail (state,
cache,
i - 1);
return FALSE;
}
} else {
......@@ -469,26 +469,26 @@ _invoke_marshal_in_args (PyGIInvokeState *state, PyGICallableCache *cache)
}
c_arg = state->args[i];
if (arg_cache->in_marshaller != NULL) {
if (arg_cache->from_py_marshaller != NULL) {
if (!arg_cache->allow_none && py_arg == Py_None) {
PyErr_Format (PyExc_TypeError,
"Argument %i does not allow None as a value",
i);
pygi_marshal_cleanup_args_in_parameter_fail (state,
cache,
i - 1);
pygi_marshal_cleanup_args_from_py_parameter_fail (state,
cache,
i - 1);
return FALSE;
}
gboolean success = arg_cache->in_marshaller (state,
cache,
arg_cache,
py_arg,
c_arg);
if (!success) {
pygi_marshal_cleanup_args_in_parameter_fail (state,
gboolean success = arg_cache->from_py_marshaller (state,
cache,
i - 1);
arg_cache,
py_arg,
c_arg);
if (!success) {
pygi_marshal_cleanup_args_from_py_parameter_fail (state,
cache,
i - 1);
return FALSE;
}
......@@ -504,7 +504,7 @@ _invoke_marshal_out_args (PyGIInvokeState *state, PyGICallableCache *cache)
{
PyObject *py_out = NULL;
PyObject *py_return = NULL;
gssize total_out_args = cache->n_out_args;
gssize total_out_args = cache->n_to_py_args;
gboolean has_return = FALSE;
if (cache->return_cache) {
......@@ -518,10 +518,10 @@ _invoke_marshal_out_args (PyGIInvokeState *state, PyGICallableCache *cache)
}
}
py_return = cache->return_cache->out_marshaller ( state,
cache,
cache->return_cache,
&state->return_arg);
py_return = cache->return_cache->to_py_marshaller ( state,
cache,
cache->return_cache,
&state->return_arg);
if (py_return == NULL) {
pygi_marshal_cleanup_args_return_fail (state,
cache);
......@@ -535,39 +535,39 @@ _invoke_marshal_out_args (PyGIInvokeState *state, PyGICallableCache *cache)
}
} else {
if (cache->return_cache->transfer == GI_TRANSFER_EVERYTHING) {
PyGIMarshalCleanupFunc out_cleanup =
cache->return_cache->out_cleanup;
if (out_cleanup != NULL)
out_cleanup ( state,
cache->return_cache,
&state->return_arg,
FALSE);
PyGIMarshalCleanupFunc to_py_cleanup =
cache->return_cache->to_py_cleanup;
if (to_py_cleanup != NULL)
to_py_cleanup ( state,
cache->return_cache,
&state->return_arg,
FALSE);
}
}
}
total_out_args -= cache->n_out_child_args;
total_out_args -= cache->n_to_py_child_args;
if (cache->n_out_args - cache->n_out_child_args == 0) {
if (cache->n_to_py_args - cache->n_to_py_child_args == 0) {
py_out = py_return;
} else if (total_out_args == 1) {
/* if we get here there is one out arg an no return */
PyGIArgCache *arg_cache = (PyGIArgCache *)cache->out_args->data;
py_out = arg_cache->out_marshaller (state,
cache,
arg_cache,
state->args[arg_cache->c_arg_index]);
PyGIArgCache *arg_cache = (PyGIArgCache *)cache->to_py_args->data;
py_out = arg_cache->to_py_marshaller (state,
cache,
arg_cache,
state->args[arg_cache->c_arg_index]);
if (py_out == NULL) {
pygi_marshal_cleanup_args_out_parameter_fail (state,
cache,
0);
pygi_marshal_cleanup_args_to_py_parameter_fail (state,
cache,
0);
return NULL;
}
} else {
gssize py_arg_index = 0;
GSList *cache_item = cache->out_args;
GSList *cache_item = cache->to_py_args;
/* return a tuple */
py_out = PyTuple_New (total_out_args);
if (has_return) {
......@@ -577,18 +577,18 @@ _invoke_marshal_out_args (PyGIInvokeState *state, PyGICallableCache *cache)
for(; py_arg_index < total_out_args; py_arg_index++) {
PyGIArgCache *arg_cache = (PyGIArgCache *)cache_item->data;
PyObject *py_obj = arg_cache->out_marshaller (state,
cache,
arg_cache,
state->args[arg_cache->c_arg_index]);
PyObject *py_obj = arg_cache->to_py_marshaller (state,
cache,
arg_cache,
state->args[arg_cache->c_arg_index]);
if (py_obj == NULL) {
if (has_return)
py_arg_index--;
pygi_marshal_cleanup_args_out_parameter_fail (state,
cache,
py_arg_index);
pygi_marshal_cleanup_args_to_py_parameter_fail (state,
cache,
py_arg_index);
Py_DECREF (py_out);
return NULL;
}
......@@ -623,11 +623,11 @@ _wrap_g_callable_info_invoke (PyGIBaseInfo *self,
if (!_invoke_callable (&state, self->cache, self->info))
goto err;
pygi_marshal_cleanup_args_in_marshal_success (&state, self->cache);
pygi_marshal_cleanup_args_from_py_marshal_success (&state, self->cache);
ret = _invoke_marshal_out_args (&state, self->cache);
if (ret)
pygi_marshal_cleanup_args_out_marshal_success (&state, self->cache);
pygi_marshal_cleanup_args_to_py_marshal_success (&state, self->cache);
err:
_invoke_state_clear (&state, self->cache);
return ret;
......
......@@ -66,42 +66,42 @@ _cleanup_caller_allocates (PyGIInvokeState *state,
* stage (either success or failure)
*
* The in stage must call one of these cleanup functions:
* - pygi_marshal_cleanup_args_in_marshal_success
* - pygi_marshal_cleanup_args_from_py_marshal_success
* (continue to out stage)
* - pygi_marshal_cleanup_args_in_parameter_fail
* - pygi_marshal_cleanup_args_from_py_parameter_fail
* (final, exit from invoke)
*
* The out stage must call one of these cleanup functions which are all final:
* - pygi_marshal_cleanup_args_out_marshal_success
* - pygi_marshal_cleanup_args_to_py_marshal_success
* - pygi_marshal_cleanup_args_return_fail
* - pygi_marshal_cleanup_args_out_parameter_fail
* - pygi_marshal_cleanup_args_to_py_parameter_fail
*
**/
void
pygi_marshal_cleanup_args_in_marshal_success (PyGIInvokeState *state,
PyGICallableCache *cache)
pygi_marshal_cleanup_args_from_py_marshal_success (PyGIInvokeState *state,
PyGICallableCache *cache)
{
gssize i;
/* For in success, call cleanup for all GI_DIRECTION_IN values only. */
for (i = 0; i < cache->n_args; i++) {
PyGIArgCache *arg_cache = cache->args_cache[i];
PyGIMarshalCleanupFunc cleanup_func = arg_cache->in_cleanup;
PyGIMarshalCleanupFunc cleanup_func = arg_cache->from_py_cleanup;
if (cleanup_func &&
arg_cache->direction == GI_DIRECTION_IN &&
arg_cache->direction == PYGI_DIRECTION_FROM_PYTHON &&
state->args[i]->v_pointer != NULL)
cleanup_func (state, arg_cache, state->args[i]->v_pointer, TRUE);
}
}
void
pygi_marshal_cleanup_args_out_marshal_success (PyGIInvokeState *state,
PyGICallableCache *cache)
pygi_marshal_cleanup_args_to_py_marshal_success (PyGIInvokeState *state,
PyGICallableCache *cache)
{
/* clean up the return if available */
if (cache->return_cache != NULL) {
PyGIMarshalCleanupFunc cleanup_func = cache->return_cache->out_cleanup;
PyGIMarshalCleanupFunc cleanup_func = cache->return_cache->to_py_cleanup;
if (cleanup_func && state->return_arg.v_pointer != NULL)
cleanup_func (state,
cache->return_cache,
......@@ -110,10 +110,10 @@ pygi_marshal_cleanup_args_out_marshal_success (PyGIInvokeState *state,
}
/* Now clean up args */
GSList *cache_item = cache->out_args;
GSList *cache_item = cache->to_py_args;
while (cache_item) {
PyGIArgCache *arg_cache = (PyGIArgCache *) cache_item->data;
PyGIMarshalCleanupFunc cleanup_func = arg_cache->out_cleanup;
PyGIMarshalCleanupFunc cleanup_func = arg_cache->to_py_cleanup;
gpointer data = state->args[arg_cache->c_arg_index]->v_pointer;
if (cleanup_func != NULL && data != NULL)
......@@ -127,9 +127,9 @@ pygi_marshal_cleanup_args_out_marshal_success (PyGIInvokeState *state,
}
void
pygi_marshal_cleanup_args_in_parameter_fail (PyGIInvokeState *state,
PyGICallableCache *cache,
gssize failed_arg_index)
pygi_marshal_cleanup_args_from_py_parameter_fail (PyGIInvokeState *state,
PyGICallableCache *cache,
gssize failed_arg_index)
{
gssize i;
......@@ -137,11 +137,11 @@ pygi_marshal_cleanup_args_in_parameter_fail (PyGIInvokeState *state,
for (i = 0; i < cache->n_args && i <= failed_arg_index; i++) {
PyGIArgCache *arg_cache = cache->args_cache[i];
PyGIMarshalCleanupFunc cleanup_func = arg_cache->in_cleanup;
PyGIMarshalCleanupFunc cleanup_func = arg_cache->from_py_cleanup;
gpointer data = state->args[i]->v_pointer;
if (cleanup_func &&
arg_cache->direction == GI_DIRECTION_IN &&
arg_cache->direction == PYGI_DIRECTION_FROM_PYTHON &&
data != NULL) {
cleanup_func (state,
arg_cache,
......@@ -164,9 +164,9 @@ pygi_marshal_cleanup_args_return_fail (PyGIInvokeState *state,
}
void
pygi_marshal_cleanup_args_out_parameter_fail (PyGIInvokeState *state,
pygi_marshal_cleanup_args_to_py_parameter_fail (PyGIInvokeState *state,
PyGICallableCache *cache,
gssize failed_out_arg_index)
gssize failed_to_py_arg_index)
{
state->failed = TRUE;
}
......@@ -181,10 +181,10 @@ _pygi_marshal_cleanup_closure_unref (PyGIInvokeState *state,
}
void
_pygi_marshal_cleanup_in_utf8 (PyGIInvokeState *state,
PyGIArgCache *arg_cache,
gpointer data,
gboolean was_processed)
_pygi_marshal_cleanup_from_py_utf8 (PyGIInvokeState *state,
PyGIArgCache *arg_cache,
gpointer data,
gboolean was_processed)
{
/* We strdup strings so always free if we have processed this
parameter for input */
......@@ -193,10 +193,10 @@ _pygi_marshal_cleanup_in_utf8 (PyGIInvokeState *state,
}
void
_pygi_marshal_cleanup_out_utf8 (PyGIInvokeState *state,
PyGIArgCache *arg_cache,
gpointer data,
gboolean was_processed)
_pygi_marshal_cleanup_to_py_utf8 (PyGIInvokeState *state,
PyGIArgCache *arg_cache,
gpointer data,
gboolean was_processed)
{
/* Python copies the string so we need to free it
if the interface is transfering ownership,
......@@ -206,10 +206,10 @@ _pygi_marshal_cleanup_out_utf8 (PyGIInvokeState *state,
}
void
_pygi_marshal_cleanup_in_interface_object (PyGIInvokeState *state,
PyGIArgCache *arg_cache,
gpointer data,
gboolean was_processed)
_pygi_marshal_cleanup_from_py_interface_object (PyGIInvokeState *state,
PyGIArgCache *arg_cache,
gpointer data,
gboolean was_processed)
{
/* If we processed the parameter but fail before invoking the method,
we need to remove the ref we added */
......@@ -219,10 +219,10 @@ _pygi_marshal_cleanup_in_interface_object (PyGIInvokeState *state,
}
void
_pygi_marshal_cleanup_out_interface_object (PyGIInvokeState *state,
PyGIArgCache *arg_cache,
gpointer data,
gboolean was_processed)
_pygi_marshal_cleanup_to_py_interface_object (PyGIInvokeState *state,
PyGIArgCache *arg_cache,
gpointer data,
gboolean was_processed)
{
/* If we error out and the object is not marshalled into a PyGObject
we must take care of removing the ref */
......@@ -231,10 +231,10 @@ _pygi_marshal_cleanup_out_interface_object (PyGIInvokeState *state,
}
void
_pygi_marshal_cleanup_in_interface_struct_gvalue (PyGIInvokeState *state,
PyGIArgCache *arg_cache,
gpointer data,
gboolean was_processed)
_pygi_marshal_cleanup_from_py_interface_struct_gvalue (PyGIInvokeState *state,
PyGIArgCache *arg_cache,
gpointer data,
gboolean was_processed)
{
if (was_processed) {
PyObject *py_arg = PyTuple_GET_ITEM (state->py_in_args,
......@@ -250,10 +250,10 @@ _pygi_marshal_cleanup_in_interface_struct_gvalue (PyGIInvokeState *state,
}
void
_pygi_marshal_cleanup_in_interface_struct_foreign (PyGIInvokeState *state,
PyGIArgCache *arg_cache,
gpointer data,
gboolean was_processed)
_pygi_marshal_cleanup_from_py_interface_struct_foreign (PyGIInvokeState *state,
PyGIArgCache *arg_cache,
gpointer data,
gboolean was_processed)
{
if (state->failed && was_processed)
pygi_struct_foreign_release (
......@@ -262,10 +262,10 @@ _pygi_marshal_cleanup_in_interface_struct_foreign (PyGIInvokeState *state,
}
void
_pygi_marshal_cleanup_out_interface_struct_foreign (PyGIInvokeState *state,
PyGIArgCache *arg_cache,
gpointer data,
gboolean was_processed)
_pygi_marshal_cleanup_to_py_interface_struct_foreign (PyGIInvokeState *state,
PyGIArgCache *arg_cache,
gpointer data,
gboolean was_processed)
{
if (!was_processed && arg_cache->transfer == GI_TRANSFER_EVERYTHING)
pygi_struct_foreign_release (
......@@ -274,10 +274,10 @@ _pygi_marshal_cleanup_out_interface_struct_foreign (PyGIInvokeState *state,
}