eo: Add new API efl_cast
authorJean-Philippe Andre <jp.andre@samsung.com>
Tue, 18 Apr 2017 11:41:11 +0000 (20:41 +0900)
committerJean-Philippe Andre <jp.andre@samsung.com>
Wed, 19 Apr 2017 02:15:30 +0000 (11:15 +0900)
This is similar to efl_super but the specified class is the one
we want to call the function on. This is similar to dynamic_cast<>
in C++.

Note: both efl_super() and efl_cast() need documentation!

This is an experimental feature.

Fixes T5311

@feature

Maniphest Tasks: T5311

Differential Revision: https://phab.enlightenment.org/D4797

src/lib/eo/Eo.h
src/lib/eo/eo.c
src/tests/eo/suite/eo_test_general.c

index bbd53ff..9d0cb3b 100644 (file)
@@ -927,6 +927,7 @@ EAPI void _efl_object_call_end(Efl_Object_Op_Call_Data *call);
 EAPI Eo * _efl_add_end(Eo *obj, Eina_Bool is_ref, Eina_Bool is_fallback);
 
 EAPI Eo *efl_super(const Eo *obj, const Efl_Class *cur_klass);
+EAPI Eo *efl_cast(const Eo *obj, const Efl_Class *cur_klass);
 
 /*****************************************************************************/
 
index 5410696..bfa950b 100644 (file)
@@ -343,7 +343,8 @@ _eo_op_desc_name_get(const Efl_Op_Description *desc)
 }
 
 static inline const op_type_funcs *
-_eo_kls_itr_next(const _Efl_Class *orig_kls, const _Efl_Class *cur_klass, Efl_Object_Op op)
+_eo_kls_itr_next(const _Efl_Class *orig_kls, const _Efl_Class *cur_klass,
+                 Efl_Object_Op op, Eina_Bool super)
 {
    const _Efl_Class **kls_itr = NULL;
 
@@ -354,7 +355,7 @@ _eo_kls_itr_next(const _Efl_Class *orig_kls, const _Efl_Class *cur_klass, Efl_Ob
 
    if (*kls_itr)
      {
-        kls_itr++;
+        if (super) kls_itr++;
         while (*kls_itr)
           {
              const op_type_funcs *fsrc = _vtable_func_get(&(*kls_itr)->vtable, op);
@@ -374,8 +375,8 @@ _eo_kls_itr_next(const _Efl_Class *orig_kls, const _Efl_Class *cur_klass, Efl_Ob
 
 static EFL_FUNC_TLS _Efl_Class *_super_klass = NULL;
 
-EAPI Eo *
-efl_super(const Eo *eo_id, const Efl_Class *cur_klass)
+static Eo *
+_efl_super_cast(const Eo *eo_id, const Efl_Class *cur_klass, Eina_Bool super)
 {
    EO_CLASS_POINTER_GOTO(cur_klass, super_klass, err);
 
@@ -389,13 +390,14 @@ efl_super(const Eo *eo_id, const Efl_Class *cur_klass)
 
    EO_OBJ_POINTER_RETURN_VAL(eo_id, obj, NULL);
    obj->cur_klass = super_klass;
-   obj->super = EINA_TRUE;
+   obj->super = super;
    EO_OBJ_DONE(eo_id);
 
    return (Eo *) eo_id;
 
 do_klass:
    // efl_super(Class) is extremely rarely used, so TLS write is fine
+   EINA_SAFETY_ON_FALSE_RETURN_VAL(super, NULL);
    _super_klass = super_klass;
    return (Eo *) eo_id;
 
@@ -412,6 +414,18 @@ err_obj_hierarchy:
 #endif
 }
 
+EAPI Eo *
+efl_super(const Eo *eo_id, const Efl_Class *cur_klass)
+{
+   return _efl_super_cast(eo_id, cur_klass, EINA_TRUE);
+}
+
+EAPI Eo *
+efl_cast(const Eo *eo_id, const Efl_Class *cur_klass)
+{
+   return _efl_super_cast(eo_id, cur_klass, EINA_FALSE);
+}
+
 EAPI Eina_Bool
 _efl_object_call_resolve(Eo *eo_id, const char *func_name, Efl_Object_Op_Call_Data *call, Efl_Object_Call_Cache *cache, const char *file, int line)
 {
@@ -422,6 +436,7 @@ _efl_object_call_resolve(Eo *eo_id, const char *func_name, Efl_Object_Op_Call_Da
    const op_type_funcs *func;
    Eina_Bool is_obj;
    Eina_Bool is_override = EINA_FALSE;
+   Eina_Bool super = EINA_TRUE;
 
    if (EINA_UNLIKELY(!eo_id)) return EINA_FALSE;
 
@@ -439,10 +454,11 @@ _efl_object_call_resolve(Eo *eo_id, const char *func_name, Efl_Object_Op_Call_Da
         if (_obj->cur_klass)
           {
              cur_klass = _obj->cur_klass;
+             super = _obj->super;
              _obj->cur_klass = NULL;
           }
 
-        if (_obj_is_override(obj) && cur_klass &&
+        if (_obj_is_override(obj) && cur_klass && super &&
             (_eo_class_id_get(cur_klass) == EFL_OBJECT_OVERRIDE_CLASS))
           {
              /* Doing a efl_super(obj, EFL_OBJECT_OVERRIDE_CLASS) should result in calling
@@ -603,7 +619,7 @@ err:
    // yes - special "move out of hot path" code blobs with goto's for
    // speed reasons to have intr prefetches work better and miss less
 ok_cur_klass:
-   func = _eo_kls_itr_next(klass, cur_klass, cache->op);
+   func = _eo_kls_itr_next(klass, cur_klass, cache->op, super);
    if (!func) goto end;
    klass = func->src;
    goto ok_cur_klass_back;
index b956eae..8798e2a 100644 (file)
@@ -1584,6 +1584,102 @@ START_TEST(eo_domain)
 }
 END_TEST
 
+
+static int
+_inherit_value_1(Eo *obj EINA_UNUSED, void *pd EINA_UNUSED)
+{
+   return 1;
+}
+
+static int
+_inherit_value_2(Eo *obj EINA_UNUSED, void *pd EINA_UNUSED)
+{
+   return 2;
+}
+
+EFL_FUNC_BODY(inherit_value, int, 0);
+
+static Eina_Bool
+_cast_inherit_class_initializer_1(Efl_Class *klass)
+{
+   EFL_OPS_DEFINE(ops, EFL_OBJECT_OP_FUNC(inherit_value, _inherit_value_1), );
+   return efl_class_functions_set(klass, &ops, NULL);
+}
+
+static Eina_Bool
+_cast_inherit_class_initializer_2(Efl_Class *klass)
+{
+   EFL_OPS_DEFINE(ops, EFL_OBJECT_OP_FUNC(inherit_value, _inherit_value_2), );
+   return efl_class_functions_set(klass, &ops, NULL);
+}
+
+START_TEST(efl_cast_test)
+{
+   efl_object_init();
+
+   static const Efl_Class_Description class_desc_1 = {
+        EO_VERSION,
+        "FirstInherit",
+        EFL_CLASS_TYPE_REGULAR,
+        0,
+        _cast_inherit_class_initializer_1,
+        NULL,
+        NULL
+   };
+
+   static const Efl_Class_Description class_desc_2 = {
+        EO_VERSION,
+        "SecondInherit",
+        EFL_CLASS_TYPE_REGULAR,
+        0,
+        _cast_inherit_class_initializer_2,
+        NULL,
+        NULL
+   };
+
+   const Efl_Class *klass1 = efl_class_new(&class_desc_1, SIMPLE_CLASS, NULL);
+   fail_if(!klass1);
+
+   const Efl_Class *klass2 = efl_class_new(&class_desc_2, klass1, NULL);
+   fail_if(!klass2);
+
+   Eo *obj;
+
+   // Testing normal calls
+   obj = efl_add(SIMPLE_CLASS, NULL);
+   fail_if(!obj);
+   ck_assert_int_eq(inherit_value(obj), 0);
+   efl_unref(obj);
+
+   obj = efl_add(klass1, NULL);
+   fail_if(!obj);
+   ck_assert_int_eq(inherit_value(obj), 1);
+   efl_unref(obj);
+
+   obj = efl_add(klass2, NULL);
+   fail_if(!obj);
+   ck_assert_int_eq(inherit_value(obj), 2);
+   efl_unref(obj);
+
+   // Testing efl_super
+   obj = efl_add(klass2, NULL);
+   fail_if(!obj);
+   ck_assert_int_eq(inherit_value(efl_super(obj, klass2)), 1);
+   ck_assert_int_eq(inherit_value(efl_super(obj, klass1)), 0);
+   efl_unref(obj);
+
+   // Testing efl_cast
+   obj = efl_add(klass2, NULL);
+   fail_if(!obj);
+   ck_assert_int_eq(inherit_value(efl_cast(obj, klass2)), 2);
+   ck_assert_int_eq(inherit_value(efl_cast(obj, klass1)), 1);
+   ck_assert_int_eq(inherit_value(efl_cast(obj, SIMPLE_CLASS)), 0);
+   efl_unref(obj);
+
+   efl_object_shutdown();
+}
+END_TEST
+
 void eo_test_general(TCase *tc)
 {
    tcase_add_test(tc, eo_simple);
@@ -1607,4 +1703,5 @@ void eo_test_general(TCase *tc)
    tcase_add_test(tc, eo_comment);
    tcase_add_test(tc, eo_rec_interface);
    tcase_add_test(tc, eo_domain);
+   tcase_add_test(tc, efl_cast_test);
 }