efl-mono: Add object type support for Eina.Value

Summary:
C# `Eina.Value` now has builtin support for `EINA_VALUE_TYPE_OBJECT`.

To avoid ambiguity with the `Set` method overloads, explicit casting
operators were used for wrapping/unwrapping `Efl.Object` instead of
implicit ones like for other value types.

Thus, to initialize an `Eina.Value` from an object, you can use the
following:

`var v = (Eina.Value)myObj;`

Reviewers: felipealmeida, vitor.sousa, segfaultxavi, Jaehyun_Cho

Reviewed By: Jaehyun_Cho

Subscribers: cedric, #reviewers, #committers

Tags: #efl

Differential Revision: https://phab.enlightenment.org/D9164
This commit is contained in:
Felipe Magno de Almeida 2019-06-25 19:11:23 +09:00 committed by Jaehyun Cho
parent 0a0f3d5bfe
commit 907bdad065
3 changed files with 197 additions and 0 deletions

View File

@ -182,6 +182,10 @@ static internal class UnsafeNativeMethods
[return: MarshalAsAttribute(UnmanagedType.U1)]
internal static extern bool eina_value_container_append_wrapper_string(IntPtr handle, string data);
[DllImport(efl.Libs.CustomExports)]
[return: MarshalAsAttribute(UnmanagedType.U1)]
internal static extern bool eina_value_container_append_wrapper_ptr(IntPtr handle, IntPtr data);
[DllImport(efl.Libs.CustomExports)]
[return: MarshalAsAttribute(UnmanagedType.U1)]
internal static extern bool eina_value_container_append_wrapper_char(IntPtr handle, sbyte data);
@ -278,6 +282,10 @@ static internal class UnsafeNativeMethods
[return: MarshalAsAttribute(UnmanagedType.U1)]
internal static extern bool eina_value_container_set_wrapper_string(IntPtr handle, int index, string value);
[DllImport(efl.Libs.CustomExports)]
[return: MarshalAsAttribute(UnmanagedType.U1)]
internal static extern bool eina_value_container_set_wrapper_ptr(IntPtr handle, int index, IntPtr value);
[DllImport(efl.Libs.CustomExports)]
[return: MarshalAsAttribute(UnmanagedType.U1)]
internal static extern bool eina_value_container_set_wrapper_uchar(IntPtr handle, int index, byte value);
@ -378,6 +386,10 @@ static internal class UnsafeNativeMethods
[return: MarshalAsAttribute(UnmanagedType.U1)]
internal static extern bool eina_value_optional_pset(IntPtr handle, IntPtr subtype, ref string value);
[DllImport(efl.Libs.Eina)]
[return: MarshalAsAttribute(UnmanagedType.U1)]
internal static extern bool eina_value_optional_pset(IntPtr handle, IntPtr subtype, ref IntPtr value);
[DllImport(efl.Libs.Eina)]
[return: MarshalAsAttribute(UnmanagedType.U1)]
internal static extern bool eina_value_optional_pset(IntPtr handle, IntPtr subtype, IntPtr value);
@ -507,6 +519,10 @@ static internal class UnsafeNativeMethods
// Error
[DllImport(efl.Libs.CustomExports)]
internal static extern IntPtr type_error();
// Error
[DllImport(efl.Libs.CustomExports)]
internal static extern IntPtr type_object();
}
}
@ -613,6 +629,7 @@ public enum ValueType
Optional,
/// <summary>Error values.</summary>
Error,
Object,
/// <summary>Empty values.</summary>
Empty,
}
@ -675,6 +692,11 @@ static class ValueTypeMethods
return val == ValueType.Error;
}
public static bool IsObject(this ValueType val)
{
return val == ValueType.Object;
}
/// <summary>Returns the Marshal.SizeOf for the given ValueType native structure.</summary>
public static int MarshalSizeOf(this ValueType val)
{
@ -771,6 +793,9 @@ static class ValueTypeBridge
ManagedToNative.Add(ValueType.Error, type_error());
NativeToManaged.Add(type_error(), ValueType.Error);
ManagedToNative.Add(ValueType.Object, type_object());
NativeToManaged.Add(type_object(), ValueType.Object);
ManagedToNative.Add(ValueType.Empty, IntPtr.Zero);
NativeToManaged.Add(IntPtr.Zero, ValueType.Empty);
@ -1354,6 +1379,32 @@ public class Value : IDisposable, IComparable<Value>, IEquatable<Value>
return b;
}
// Efl.Object conversions are made explicit to avoid ambiguity between
// Set(Efl.Object) and Set(Value) when dealing with classes derived from
// Efl.Object.
/// <summary>Explicit conversion from EFL objects.</summary>
public static explicit operator Value(Efl.Object obj)
{
var v = new Eina.Value(ValueType.Object);
if (!v.Set(obj))
{
throw new InvalidOperationException("Couldn't set value.");
}
return v;
}
/// <summary>Explicit conversion from Value to Efl.Objects.</summary>
public static explicit operator Efl.Object(Value v)
{
Efl.Object obj;
if (!v.Get(out obj))
{
throw new InvalidOperationException("Couldn't get value.");
}
return obj;
}
/// <summary>Creates an Value instance from a given array description.</summary>
private static Value FromArrayDesc(Eina.EinaNative.Value_Array arrayDesc)
{
@ -1789,6 +1840,22 @@ public class Value : IDisposable, IComparable<Value>, IEquatable<Value>
return eina_value_set_wrapper_int(this.Handle, error_code);
}
/// <summary>Stores the given object.</summary>
public bool Set(Efl.Object value)
{
// FIXME Implement me
SanityChecks();
if (this.Optional)
{
IntPtr ptr = value.NativeHandle;
return eina_value_optional_pset(this.Handle,
ValueTypeBridge.GetNative(ValueType.Object),
ref ptr);
}
return eina_value_set_wrapper_ptr(this.Handle, value.NativeHandle);
}
/// <summary>Stores the given value into this value. The target value must be an optional.</summary>
public bool Set(Value value)
{
@ -2017,6 +2084,36 @@ public class Value : IDisposable, IComparable<Value>, IEquatable<Value>
return ret;
}
/// <summary>Gets the currently stored value as an <see cref="Efl.Object"/>.</summary>
public bool Get(out Efl.Object obj)
{
// FIXME Implement me
SanityChecks();
IntPtr ptr;
bool ret;
if (this.Optional)
{
ret = eina_value_optional_pget(this.Handle, out ptr);
}
else
{
ret = eina_value_get_wrapper(this.Handle, out ptr);
}
if (ret)
{
obj = (Efl.Object) Efl.Eo.Globals.CreateWrapperFor(ptr);
}
else
{
obj = null;
}
return ret;
}
/// <summary>Gets the currently stored value as an complex (e.g. container) Eina.Value.</summary>
public bool Get(out Value value)
{
@ -2281,6 +2378,11 @@ public class Value : IDisposable, IComparable<Value>, IEquatable<Value>
string x = Convert.ToString(o);
return eina_value_container_append_wrapper_string(this.Handle, x);
}
case ValueType.Object:
{
var x = (Efl.Object) o;
return eina_value_container_append_wrapper_ptr(this.Handle, x.NativeHandle);
}
}
return false;
@ -2374,6 +2476,12 @@ public class Value : IDisposable, IComparable<Value>, IEquatable<Value>
eina_value_container_get_wrapper(this.Handle, i, out ptr);
return Eina.StringConversion.NativeUtf8ToManagedString(ptr);
}
case ValueType.Object:
{
IntPtr ptr = IntPtr.Zero;
eina_value_container_get_wrapper(this.Handle, i, out ptr);
return Efl.Eo.Globals.CreateWrapperFor(ptr);
}
default:
throw new InvalidOperationException("Subtype not supported.");
@ -2463,6 +2571,12 @@ public class Value : IDisposable, IComparable<Value>, IEquatable<Value>
eina_value_container_set_wrapper_string(this.Handle, i, x);
break;
}
case ValueType.Object:
{
Efl.Object x = (Efl.Object)value;
eina_value_container_set_wrapper_ptr(this.Handle, i, x.NativeHandle);
break;
}
}
}
}

View File

@ -403,6 +403,9 @@ EAPI const Eina_Value_Type *type_list() {
EAPI const Eina_Value_Type *type_error() {
return EINA_VALUE_TYPE_ERROR;
}
EAPI const Eina_Value_Type *type_object() {
return EINA_VALUE_TYPE_OBJECT;
}
EAPI const Eina_Value_Type *type_optional() {
return EINA_VALUE_TYPE_OPTIONAL;

View File

@ -146,6 +146,31 @@ public static class TestEinaValue {
}
}
public static void TestObjectSimple()
{
using (Eina.Value v = new Eina.Value(Eina.ValueType.Object))
{
var obj = new Dummy.TestObject();
Test.Assert(v.Set(obj));
Efl.Object target;
Test.Assert(v.Get(out target));
Test.AssertEquals(target, obj);
}
}
// Efl.Object conversions are made explicit to avoid ambiguity between
// Set(Efl.Object) and Set(Value) when dealing with classes derived from
// Efl.Object.
public static void TestObjectImplicit()
{
var obj = new Dummy.TestObject();
var v = (Eina.Value)obj;
Test.AssertEquals(v.GetValueType(), Eina.ValueType.Object);
Efl.Object target = (Efl.Object)v;
Test.AssertEquals(target, obj);
}
public static void TestSetWrongType()
{
using (Eina.Value v = new Eina.Value(Eina.ValueType.String)) {
@ -259,6 +284,37 @@ public static class TestEinaValue {
Test.AssertEquals(expected, actual);
}
}
public static void TestValueOptionalObject()
{
using (Eina.Value a = new Eina.Value(Eina.ValueType.Object)) {
Test.Assert(!a.Optional);
BoolRet dummy = () => a.OptionalEmpty;
Test.AssertRaises<Eina.InvalidValueTypeException>(() => dummy());
}
using (Eina.Value a = new Eina.Value(Eina.ValueType.Optional)) {
Test.Assert(a.Optional);
Test.Assert(a.OptionalEmpty); // By default, optional values are empty
// Sets expectation
Efl.Object expected = new Dummy.TestObject();
Test.Assert(a.Set(expected));
Test.Assert(a.Optional);
Test.Assert(!a.OptionalEmpty);
Test.Assert(a.Reset());
Test.Assert(a.OptionalEmpty);
Test.Assert(a.Set(expected));
Test.Assert(!a.OptionalEmpty);
Efl.Object received = null;
Test.Assert(a.Get(out received));
Test.AssertEquals(expected, received);
}
}
public static void TestValueOptionalArrays()
{
using (Eina.Value a = new Eina.Value(Eina.ValueType.Optional))
@ -763,6 +819,30 @@ public static class TestEinaValue {
}
}
public static void TestValueArrayOfObjects()
{
using(Eina.Value array = new Eina.Value(Eina.ValueType.Array, Eina.ValueType.Object)) {
var a = new Dummy.TestObject();
var b = new Dummy.TestObject();
Test.Assert(array.Append(a));
Test.Assert(array.Append(b));
Test.AssertEquals((Efl.Object)array[0], a);
Test.AssertEquals((Efl.Object)array[1], b);
var c = new Dummy.TestObject();
array[0] = c;
array[1] = b;
Test.AssertEquals((Efl.Object)array[0], c);
Test.AssertEquals((Efl.Object)array[1], b);
}
}
public static void TestArrayOutOfBounds() {
using(Eina.Value array = new Eina.Value(Eina.ValueType.Array, Eina.ValueType.Int32)) {
object placeholder = null;