Skip to content

Commit bf8c9dc

Browse files
committed
Merge branch 'develop' of https://github.com/metacall/core into develop
2 parents 431cc58 + 59615cc commit bf8c9dc

File tree

8 files changed

+193
-7
lines changed

8 files changed

+193
-7
lines changed

source/loaders/py_loader/source/py_loader_impl.c

Lines changed: 43 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -102,6 +102,8 @@ static int py_loader_impl_discover_func_args_count(PyObject * func);
102102

103103
static int py_loader_impl_discover_func(loader_impl impl, PyObject * func, function f);
104104

105+
static void py_loader_impl_value_owner_finalize(value v, void * owner);
106+
105107
static PyMethodDef py_loader_impl_function_type_invoke_defs[] =
106108
{
107109
{
@@ -126,6 +128,21 @@ static struct PyModuleDef py_loader_impl_function_type_invoke_module =
126128
NULL
127129
};
128130

131+
static void * py_loader_impl_value_ownership = NULL;
132+
133+
void py_loader_impl_value_owner_finalize(value v, void * owner)
134+
{
135+
type_id id = value_type_id(v);
136+
137+
if (owner == &py_loader_impl_value_ownership)
138+
{
139+
if (id == TYPE_PTR)
140+
{
141+
Py_XDECREF(value_to_ptr(v));
142+
}
143+
}
144+
}
145+
129146
int type_py_interface_create(type t, type_impl impl)
130147
{
131148
(void)t;
@@ -498,7 +515,19 @@ value py_loader_impl_capi_to_value(loader_impl impl, PyObject * obj, type_id id)
498515
}
499516
else
500517
{
501-
log_write("metacall", LOG_LEVEL_ERROR, "Unrecognized python type");
518+
/* Return the value as opaque pointer */
519+
v = value_create_ptr(obj);
520+
521+
/* Set up the ownership to python loader */
522+
value_own(v, &py_loader_impl_value_ownership);
523+
524+
/* Create reference to the value so it does not get garbage collected */
525+
Py_INCREF(obj);
526+
527+
/* Set up finalizer in order to free the value */
528+
value_finalizer(v, &py_loader_impl_value_owner_finalize);
529+
530+
log_write("metacall", LOG_LEVEL_WARNING, "Unrecognized python type");
502531
}
503532

504533
return v;
@@ -598,13 +627,21 @@ PyObject * py_loader_impl_value_to_capi(loader_impl impl, loader_impl_py py_impl
598627
{
599628
void * ptr = value_to_ptr(v);
600629

601-
#if PY_MAJOR_VERSION == 2
630+
if (value_owner(v) == &py_loader_impl_value_ownership)
631+
{
632+
return ptr;
633+
}
634+
else
635+
{
636+
#if PY_MAJOR_VERSION == 2
602637

603-
/* TODO */
638+
/* TODO */
639+
640+
#elif PY_MAJOR_VERSION == 3
641+
return PyCapsule_New(ptr, NULL, NULL);
642+
#endif
643+
}
604644

605-
#elif PY_MAJOR_VERSION == 3
606-
return PyCapsule_New(ptr, NULL, NULL);
607-
#endif
608645
}
609646
else if (id == TYPE_FUNCTION)
610647
{

source/metacall/include/metacall/metacall.h

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -270,6 +270,25 @@ METACALL_API int metacall_load_from_configuration(const char * path, void ** han
270270
*/
271271
METACALL_API void * metacallv(const char * name, void * args[]);
272272

273+
/**
274+
* @brief
275+
* Call a function anonymously by handle @handle value array @args
276+
* This function allows to avoid name collisions when calling functions by name
277+
*
278+
* @param[in] handle
279+
* Handle where the function belongs
280+
*
281+
* @param[in] name
282+
* Name of the function
283+
*
284+
* @param[in] args
285+
* Array of pointers to data
286+
*
287+
* @return
288+
* Pointer to value containing the result of the call
289+
*/
290+
METACALL_API void * metacallhv(void * handle, const char * name, void * args[]);
291+
273292
/**
274293
* @brief
275294
* Call a function anonymously by variable arguments @va_args

source/metacall/source/metacall.c

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -264,6 +264,17 @@ void * metacallv(const char * name, void * args[])
264264
return metacallfv(loader_get(name), args);
265265
}
266266

267+
void * metacallhv(void * handle, const char * name, void * args[])
268+
{
269+
(void)handle;
270+
(void)name;
271+
(void)args;
272+
273+
/* TODO */
274+
275+
return NULL;
276+
}
277+
267278
void * metacall(const char * name, ...)
268279
{
269280
function f = loader_get(name);

source/reflect/include/reflect/reflect_value.h

Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -43,6 +43,8 @@ extern "C" {
4343

4444
typedef void * value;
4545

46+
typedef void (*value_finalizer_cb)(value, void *);
47+
4648
/* -- Methods -- */
4749

4850
/**
@@ -114,6 +116,44 @@ REFLECT_API void value_ref_inc(value v);
114116
*/
115117
REFLECT_API void value_ref_dec(value v);
116118

119+
/**
120+
* @brief
121+
* Returns the owner of the value, useful for lifecycles
122+
*
123+
* @param[in] v
124+
* Reference to the value
125+
*
126+
* @return
127+
* Pointer to the owner of the value,
128+
* null means the value is not owned by anybody
129+
*/
130+
REFLECT_API void * value_owner(value v);
131+
132+
/**
133+
* @brief
134+
* Set up the value ownership, overwrites the previous owner
135+
*
136+
* @param[in] v
137+
* Reference to the value
138+
*
139+
* @param[in] owner
140+
* Reference to the new owner
141+
*/
142+
REFLECT_API void value_own(value v, void * owner);
143+
144+
/**
145+
* @brief
146+
* Set up the value finalizer, a callback that
147+
* will be executed when the value life ends
148+
*
149+
* @param[in] v
150+
* Reference to the value
151+
*
152+
* @param[in] finalizer
153+
* Reference to the callback
154+
*/
155+
REFLECT_API void value_finalizer(value v, value_finalizer_cb finalizer);
156+
117157
/**
118158
* @brief
119159
* Get pointer reference to value data

source/reflect/source/reflect_value.c

Lines changed: 41 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,8 @@ struct value_impl_type
3939
{
4040
size_t bytes;
4141
size_t ref_count;
42+
void * owner;
43+
value_finalizer_cb finalizer;
4244
};
4345

4446
/* -- Private Methods -- */
@@ -77,8 +79,9 @@ value value_alloc(size_t bytes)
7779
}
7880

7981
impl->bytes = bytes;
80-
8182
impl->ref_count = 1;
83+
impl->owner = NULL;
84+
impl->finalizer = NULL;
8285

8386
return (value)(((uintptr_t)impl) + sizeof(struct value_impl_type));
8487
}
@@ -150,6 +153,38 @@ void value_ref_dec(value v)
150153
}
151154
}
152155

156+
void * value_owner(value v)
157+
{
158+
value_impl impl = value_descriptor(v);
159+
160+
if (impl == NULL)
161+
{
162+
return NULL;
163+
}
164+
165+
return impl->owner;
166+
}
167+
168+
void value_own(value v, void * owner)
169+
{
170+
value_impl impl = value_descriptor(v);
171+
172+
if (impl != NULL)
173+
{
174+
impl->owner = owner;
175+
}
176+
}
177+
178+
void value_finalizer(value v, value_finalizer_cb finalizer)
179+
{
180+
value_impl impl = value_descriptor(v);
181+
182+
if (impl != NULL)
183+
{
184+
impl->finalizer = finalizer;
185+
}
186+
}
187+
153188
void * value_data(value v)
154189
{
155190
if (v == NULL)
@@ -195,6 +230,11 @@ void value_destroy(value v)
195230

196231
if (impl != NULL && impl->ref_count <= 1)
197232
{
233+
if (impl->finalizer != NULL)
234+
{
235+
impl->finalizer(v, impl->owner);
236+
}
237+
198238
free(impl);
199239
}
200240
}

source/scripts/python/function/source/function.py

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -37,3 +37,13 @@ def function_chain(x):
3737
def function_factorial(x):
3838
return lambda n: 1 if n == 0 else n * x(x)(n - 1);
3939

40+
class MyClass:
41+
def f(self):
42+
return 'hello world';
43+
44+
def function_capsule_new_class():
45+
return MyClass();
46+
47+
def function_capsule_method(klass):
48+
print('Executing class method with instance passed as opaque pointer:', klass.f());
49+
return klass.f();

source/tests/metacall_function_test/source/metacall_function_test.cpp

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -160,6 +160,33 @@ TEST_F(metacall_function_test, DefaultConstructor)
160160
EXPECT_EQ((void *) NULL, (void *) metacall_value_to_null(ret));
161161

162162
metacall_value_destroy(ret);
163+
164+
/* TODO: This is a workaround to achieve class / object callbacks between languages. */
165+
/* It provides interoperatibility but without proper reflection. */
166+
/* Enough to implement callbacks with opaque pointers between languages. */
167+
168+
ret = metacallv("function_capsule_new_class", metacall_null_args);
169+
170+
EXPECT_NE((void *) NULL, (void *) ret);
171+
172+
EXPECT_EQ((enum metacall_value_id) METACALL_PTR, (enum metacall_value_id) metacall_value_id(ret));
173+
174+
void * function_capsule_method_args[] =
175+
{
176+
ret
177+
};
178+
179+
ret = metacallv("function_capsule_method", function_capsule_method_args);
180+
181+
EXPECT_NE((void *) NULL, (void *) ret);
182+
183+
EXPECT_EQ((enum metacall_value_id) METACALL_STRING, (enum metacall_value_id) metacall_value_id(ret));
184+
185+
EXPECT_EQ((int) 0, (int) strcmp("hello world", metacall_value_to_string(ret)));
186+
187+
metacall_value_destroy(ret);
188+
189+
metacall_value_destroy(function_capsule_method_args[0]);
163190
}
164191
#endif /* OPTION_BUILD_LOADERS_PY */
165192

source/tests/metacall_python_class_test/source/metacall_python_class_test.cpp

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -43,6 +43,8 @@ TEST_F(metacall_python_class_test, DefaultConstructor)
4343
};
4444

4545
EXPECT_EQ((int) 0, (int) metacall_load_from_file("py", py_scripts, sizeof(py_scripts) / sizeof(py_scripts[0]), NULL));
46+
47+
/* TODO: Implement properly class and object reflection and methods */
4648
}
4749
#endif /* OPTION_BUILD_LOADERS_PY */
4850

0 commit comments

Comments
 (0)