eo: here comes reflection API

this adds support in eo to generate a reflection API. To get the actaul
reflection to the klass, the API efl_class_reflection_table_set needs to
be called, the table in the end can be generated by eolian. Reflection
API is inherited by the extended class. This means, if you have two
reflection tables, first, the most upperst is called, then the next
lower one is called.

For now this API accepts NULL setter or getter, and will ignore them
silently when they are called.

fix T7681

Differential Revision: https://phab.enlightenment.org/D7879
This commit is contained in:
Marcel Hollerbach 2019-02-05 15:40:41 +01:00
parent 0709bdea6f
commit 0f32bb9047
9 changed files with 218 additions and 6 deletions

View File

@ -149,7 +149,8 @@ tests/eo/suite/eo_test_value.c \
tests/eo/suite/eo_test_event.c \
tests/eo/suite/eo_test_threaded_calls.c \
tests/eo/suite/eo_test_init.c \
tests/eo/suite/eo_test_lifecycle.c
tests/eo/suite/eo_test_lifecycle.c \
tests/eo/suite/eo_test_reflection.c
tests_eo_eo_suite_CPPFLAGS = -I$(top_builddir)/src/lib/efl \
-DPACKAGE_BUILD_DIR=\"$(abs_top_builddir)\" \

View File

@ -826,6 +826,40 @@ struct _Efl_Class_Description
void (*class_constructor)(Efl_Class *klass); /**< The constructor of the class. */
void (*class_destructor)(Efl_Class *klass); /**< The destructor of the class. */
};
/**
* Setter type which is used to set an #Eina_Value, this function should access one particular property field
*/
typedef void (*Efl_Object_Property_Reflection_Setter)(Eo *obj, Eina_Value value);
/**
* Getter type which is used to get an #Eina_Value, this function should access one particular property field
*/
typedef Eina_Value (*Efl_Object_Property_Reflection_Getter)(Eo *obj);
/**
* @struct _Efl_Object_Property_Reflection
*
* This structure holds one line of the reflection table.
* The two fields get and set might be NULL,
* the property_name is a normal c string containing the name of the property
* that the get and set function changes.
*/
typedef struct _Efl_Object_Property_Reflection{
const char *property_name; /**< The name of the property */
Efl_Object_Property_Reflection_Setter set; /**< The function used to set a generic #Eina_Value on this property of the object. */
Efl_Object_Property_Reflection_Getter get; /**< The function used to retrieve a generic #Eina_Value from this property of the object. */
} Efl_Object_Property_Reflection;
/**
* @struct _Efl_Object_Property_Reflection_Ops
*
* This structure holds the reflection table and the size of this table.
*/
typedef struct _Efl_Object_Property_Reflection_Ops
{
const Efl_Object_Property_Reflection *table; /**< The reflection table. */
size_t count; /**< Number of table lines descriptions. */
} Efl_Object_Property_Reflection_Ops;
/**
* @typedef Efl_Class_Description
@ -860,10 +894,11 @@ EAPI const Efl_Class *efl_class_new(const Efl_Class_Description *desc, const Efl
* @return True on success, False otherwise.
*
* This should only be called from within the initializer function.
*
* The reflection_table contains a getter and setter per property name. Which are called when either
* efl_property_reflection_set() or efl_property_reflection_get() is called.
* @see #EFL_DEFINE_CLASS
*/
EAPI Eina_Bool efl_class_functions_set(const Efl_Class *klass_id, const Efl_Object_Ops *object_ops, const Efl_Object_Ops *class_ops, const void *reflection_table);
EAPI Eina_Bool efl_class_functions_set(const Efl_Class *klass_id, const Efl_Object_Ops *object_ops, const Efl_Object_Ops *class_ops, const Efl_Object_Property_Reflection_Ops *reflection_table);
/**
* @brief Override Eo functions of this object.
@ -1945,6 +1980,25 @@ EAPI Eina_Bool efl_manual_free(Eo *obj);
*/
EAPI Eina_Bool efl_destructed_is(const Eo *obj);
/**
* @brief Set the given #Eina_Value to the property with the specified \c property_name.
* @param obj The object to set the property on
* @param property_name The name of the property to modify.
* @param value The value to set, the value passed here will be flushed by the function
*
*/
EAPI void efl_property_reflection_set(Eo *obj, const char *property_name, Eina_Value value);
/**
* @brief Retrieve an #Eina_Value containing the current value of the property specified with \c property_name.
* @param obj The object to set the property on
* @param property_name The name of the property to get.
*
* @return The value that got returned by the actual property in form of a generic Eina_Value. The user of this API is owning the returned Value.
*/
EAPI Eina_Value efl_property_reflection_get(Eo *obj, const char *property_name);
/**
* @addtogroup Efl_Class_Class Eo's Class class.
* @{

View File

@ -819,7 +819,7 @@ _eo_class_funcs_set(Eo_Vtable *vtable, const Efl_Object_Ops *ops, const _Efl_Cla
}
EAPI Eina_Bool
efl_class_functions_set(const Efl_Class *klass_id, const Efl_Object_Ops *object_ops, const Efl_Object_Ops *class_ops, const void *reflection_table)
efl_class_functions_set(const Efl_Class *klass_id, const Efl_Object_Ops *object_ops, const Efl_Object_Ops *class_ops, const Efl_Object_Property_Reflection_Ops *reflection_table)
{
EO_CLASS_POINTER_GOTO(klass_id, klass, err_klass);
Efl_Object_Ops empty_ops = { 0 };
@ -832,6 +832,8 @@ efl_class_functions_set(const Efl_Class *klass_id, const Efl_Object_Ops *object_
if (!class_ops) class_ops = &empty_ops;
klass->reflection = reflection_table;
klass->ops_count = object_ops->count + class_ops->count;
klass->base_id = _eo_ops_last_id;
@ -3597,3 +3599,63 @@ static const Eina_Value_Type _EINA_VALUE_TYPE_OBJECT = {
};
EOAPI const Eina_Value_Type *EINA_VALUE_TYPE_OBJECT = &_EINA_VALUE_TYPE_OBJECT;
static const Efl_Object_Property_Reflection*
_efl_class_reflection_find(const _Efl_Class *klass, const char *property_name)
{
const _Efl_Class **klass_iter = klass->extensions;
const Efl_Object_Property_Reflection_Ops *ref = klass->reflection;
unsigned int i;
for (i = 0; ref && i < ref->count; ++i)
{
if (eina_streq(property_name, ref->table[i].property_name))
return &ref->table[i];
}
if (klass->parent)
{
const Efl_Object_Property_Reflection *ref;
ref = _efl_class_reflection_find(klass->parent, property_name);
if (ref) return ref;
}
for (; *klass_iter; klass_iter++)
{
return _efl_class_reflection_find(*klass_iter, property_name);
}
return NULL;
}
EAPI void
efl_property_reflection_set(Eo *obj_id, const char *property_name, Eina_Value value)
{
EO_OBJ_POINTER_GOTO(obj_id, obj, end);
const Efl_Object_Property_Reflection *reflection = _efl_class_reflection_find(obj->klass, property_name);
if (!reflection || !reflection->set) goto end;
reflection->set(obj_id, value);
EO_OBJ_DONE(obj_id);
return;
end:
eina_value_flush(&value);
EO_OBJ_DONE(obj_id);
}
EAPI Eina_Value
efl_property_reflection_get(Eo *obj_id, const char *property_name)
{
EO_OBJ_POINTER(obj_id, obj);
const Efl_Object_Property_Reflection *reflection = _efl_class_reflection_find(obj->klass, property_name);
if (!reflection || !reflection->get) goto end;
return reflection->get(obj_id);
end:
EO_OBJ_DONE(obj_id);
return EINA_VALUE_EMPTY;
}

View File

@ -185,6 +185,8 @@ struct _Efl_Class
const _Efl_Class **mro;
const Efl_Object_Property_Reflection_Ops *reflection;
/* cached object for faster allocation */
struct {
Eina_Trash *trash;

View File

@ -20,6 +20,7 @@ static const Efl_Test_Case etc[] = {
{ "Eo threaded eo calls", eo_test_threaded_calls },
{ "Eo event calls", eo_test_event},
{ "Eo lifecycle", eo_test_lifecycle},
{ "Eo Reflection", eo_test_reflection},
{ NULL, NULL }
};

View File

@ -12,4 +12,5 @@ void eo_test_value(TCase *tc);
void eo_test_threaded_calls(TCase *tc);
void eo_test_event(TCase *tc);
void eo_test_lifecycle(TCase *tc);
void eo_test_reflection(TCase *tc);
#endif /* _EO_SUITE_H */

View File

@ -23,6 +23,16 @@ _a_set(Eo *obj EINA_UNUSED, void *class_data, int a)
efl_event_callback_legacy_call(obj, EV_A_CHANGED, &pd->a);
}
static void
_a_set_reflect(Eo *obj, Eina_Value value)
{
int a;
eina_value_int_convert(&value, &a);
simple_a_set(obj, a);
eina_value_flush(&value);
}
static int
_a_get(Eo *obj EINA_UNUSED, void *class_data)
{
@ -31,6 +41,14 @@ _a_get(Eo *obj EINA_UNUSED, void *class_data)
return pd->a;
}
static Eina_Value
_a_get_reflect(Eo *obj)
{
int a = simple_a_get(obj);
return eina_value_int_init(a);
}
static Eina_Bool
_a_print(Eo *obj EINA_UNUSED, void *class_data)
{
@ -103,8 +121,14 @@ _class_initializer(Efl_Class *klass)
EFL_OPS_DEFINE(cops,
EFL_OBJECT_OP_FUNC(simple_class_hi_print, _class_hi_print),
);
static const Efl_Object_Property_Reflection reflection_table[] = {
{"simple_a", _a_set_reflect, _a_get_reflect},
};
static const Efl_Object_Property_Reflection_Ops ref_ops = {
reflection_table, EINA_C_ARRAY_LENGTH(reflection_table)
};
return efl_class_functions_set(klass, &ops, &cops, NULL);
return efl_class_functions_set(klass, &ops, &cops, &ref_ops);
}
static const Efl_Class_Description class_desc = {

View File

@ -0,0 +1,66 @@
#ifdef HAVE_CONFIG_H
# include "config.h"
#endif
#include <stdio.h>
#include <Eo.h>
#include "eo_suite.h"
#include "eo_test_class_simple.h"
EFL_START_TEST(eo_test_reflection_invalid)
{
Eina_Value numb_val = eina_value_int_init(1337);
Eo *simple = efl_new(SIMPLE_CLASS);
simple_a_set(simple, 22);
efl_property_reflection_set(simple, "simple_a_asdf", numb_val);
fail_if(efl_property_reflection_get(simple, "simple_a_invalid").type != EINA_VALUE_EMPTY.type);
}
EFL_END_TEST
EFL_START_TEST(eo_test_reflection_inherited)
{
const int numb = 42;
int number_ref;
Eina_Value numb_val = eina_value_int_init(numb);
Eo *simple = efl_new(SIMPLE3_CLASS);
simple_a_set(simple, 22);
efl_property_reflection_set(simple, "simple_a", numb_val);
ck_assert_int_eq(simple_a_get(simple), numb);
simple_a_set(simple, 22);
Eina_Value res = efl_property_reflection_get(simple, "simple_a");
eina_value_int_convert(&res, &number_ref);
ck_assert_int_eq(number_ref, 22);
}
EFL_END_TEST
EFL_START_TEST(eo_test_reflection_simple)
{
const int numb = 42;
int number_ref;
Eina_Value numb_val = eina_value_int_init(numb);
Eo *simple = efl_new(SIMPLE_CLASS);
simple_a_set(simple, 22);
efl_property_reflection_set(simple, "simple_a", numb_val);
ck_assert_int_eq(simple_a_get(simple), numb);
simple_a_set(simple, 22);
Eina_Value res = efl_property_reflection_get(simple, "simple_a");
eina_value_int_convert(&res, &number_ref);
ck_assert_int_eq(number_ref, 22);
}
EFL_END_TEST
void eo_test_reflection(TCase *tc)
{
tcase_add_test(tc, eo_test_reflection_simple);
tcase_add_test(tc, eo_test_reflection_inherited);
tcase_add_test(tc, eo_test_reflection_invalid);
}

View File

@ -17,7 +17,8 @@ eo_suite_src = [
'eo_test_event.c',
'eo_test_threaded_calls.c',
'eo_test_init.c',
'eo_test_lifecycle.c'
'eo_test_lifecycle.c',
'eo_test_reflection.c'
]
eo_suite = executable('eo_suite',