forked from enlightenment/efl
eolian-mono: Add support for multi-value properties with tuples
Summary: T8133 Reviewers: woohyun, segfaultxavi, felipealmeida Reviewed By: felipealmeida Subscribers: cedric, #reviewers, #committers Tags: #efl Differential Revision: https://phab.enlightenment.org/D9577
This commit is contained in:
parent
8aae656b80
commit
1aa05ab41b
|
@ -6,10 +6,12 @@
|
|||
#include "grammar/generator.hpp"
|
||||
#include "grammar/klass_def.hpp"
|
||||
|
||||
#include "grammar/kleene.hpp"
|
||||
#include "grammar/indentation.hpp"
|
||||
#include "grammar/list.hpp"
|
||||
#include "grammar/alternative.hpp"
|
||||
#include "grammar/attribute_reorder.hpp"
|
||||
#include "grammar/counter.hpp"
|
||||
#include "logging.hh"
|
||||
#include "type.hh"
|
||||
#include "name_helpers.hh"
|
||||
|
@ -292,6 +294,11 @@ struct property_wrapper_definition_generator
|
|||
template<typename OutputIterator, typename Context>
|
||||
bool generate(OutputIterator sink, attributes::property_def const& property, Context const& context) const
|
||||
{
|
||||
using efl::eolian::grammar::attribute_reorder;
|
||||
using efl::eolian::grammar::counter;
|
||||
using efl::eolian::grammar::attributes::parameter_direction;
|
||||
using efl::eolian::grammar::attributes::parameter_def;
|
||||
|
||||
if (blacklist::is_property_blacklisted(property, *implementing_klass, context))
|
||||
return true;
|
||||
|
||||
|
@ -304,7 +311,7 @@ struct property_wrapper_definition_generator
|
|||
return true;
|
||||
|
||||
auto get_params = property.getter.is_engaged() ? property.getter->parameters.size() : 0;
|
||||
auto set_params = property.setter.is_engaged() ? property.setter->parameters.size() : 0;
|
||||
//auto set_params = property.setter.is_engaged() ? property.setter->parameters.size() : 0;
|
||||
|
||||
// C# properties must have a single value.
|
||||
//
|
||||
|
@ -312,16 +319,62 @@ struct property_wrapper_definition_generator
|
|||
// meaning they should have 0 parameters.
|
||||
//
|
||||
// For setters, we ignore the return type - usually boolean.
|
||||
if (get_params > 0 || set_params > 1)
|
||||
// if (get_params > 0 || set_params > 1)
|
||||
// return true;
|
||||
|
||||
if (property.getter
|
||||
&& std::find_if (property.getter->parameters.begin()
|
||||
, property.getter->parameters.end()
|
||||
, [] (parameter_def const& p)
|
||||
{
|
||||
return p.direction != parameter_direction::out;
|
||||
}) != property.getter->parameters.end())
|
||||
return true;
|
||||
if (property.setter
|
||||
&& std::find_if (property.setter->parameters.begin()
|
||||
, property.setter->parameters.end()
|
||||
, [] (parameter_def const& p)
|
||||
{
|
||||
return p.direction != parameter_direction::in;
|
||||
}) != property.setter->parameters.end())
|
||||
return true;
|
||||
|
||||
attributes::type_def prop_type;
|
||||
if (property.getter && property.setter)
|
||||
{
|
||||
if (get_params != 0 && property.setter->parameters.size() != property.getter->parameters.size())
|
||||
return true;
|
||||
}
|
||||
|
||||
if (property.getter.is_engaged())
|
||||
prop_type = property.getter->return_type;
|
||||
else if (property.setter.is_engaged())
|
||||
prop_type = property.setter->parameters[0].type;
|
||||
else
|
||||
std::vector<attributes::parameter_def> parameters;
|
||||
|
||||
if (property.setter.is_engaged())
|
||||
{
|
||||
std::transform (property.setter->parameters.begin(), property.setter->parameters.end()
|
||||
, std::back_inserter(parameters)
|
||||
, [] (parameter_def p) -> parameter_def
|
||||
{
|
||||
//p.direction = efl::eolian::attributes::parameter_direction::in;
|
||||
return p;
|
||||
});
|
||||
}
|
||||
else if (property.getter.is_engaged())
|
||||
{
|
||||
// if getter has parameters, then we ignore return type, otherwise
|
||||
// we use the return type.
|
||||
if (get_params == 0)
|
||||
parameters.push_back({parameter_direction::in
|
||||
, property.getter->return_type, "propertyResult", {}
|
||||
, property.getter->unit});
|
||||
else
|
||||
std::transform (property.getter->parameters.begin(), property.getter->parameters.end()
|
||||
, std::back_inserter(parameters)
|
||||
, [] (parameter_def p) -> parameter_def
|
||||
{
|
||||
p.direction = parameter_direction::in;
|
||||
return p;
|
||||
});
|
||||
}
|
||||
else
|
||||
{
|
||||
EINA_CXX_DOM_LOG_ERR(eolian_mono::domain) << "Property must have either a getter or a setter." << std::endl;
|
||||
return false;
|
||||
|
@ -365,21 +418,88 @@ struct property_wrapper_definition_generator
|
|||
set_scope = "";
|
||||
}
|
||||
|
||||
if (!as_generator(
|
||||
documentation(1)
|
||||
<< scope_tab << scope << (is_static ? "static " : "") << type(true) << " " << managed_name << " {\n"
|
||||
).generate(sink, std::make_tuple(property, prop_type), context))
|
||||
return false;
|
||||
if (parameters.size() == 1)
|
||||
{
|
||||
if (!as_generator(
|
||||
documentation(1)
|
||||
<< scope_tab << scope << (is_static ? "static " : "") << type(true) << " " << managed_name << " {\n"
|
||||
).generate(sink, std::make_tuple(property, parameters[0].type), context))
|
||||
return false;
|
||||
}
|
||||
else
|
||||
{
|
||||
if (!as_generator
|
||||
(
|
||||
documentation(1)
|
||||
<< scope_tab << scope << (is_static ? "static (" : "(")
|
||||
<< (attribute_reorder<1, -1>(type(true) /*<< " " << argument*/) % ", ") << ") "
|
||||
<< managed_name << " {\n"
|
||||
).generate(sink, std::make_tuple(property, parameters), context))
|
||||
return false;
|
||||
}
|
||||
|
||||
if (property.getter.is_engaged())
|
||||
if (!as_generator(scope_tab << scope_tab << get_scope << "get " << (interface ? ";" : "{ return " + name_helpers::managed_method_name(*property.getter) + "(); }") << "\n"
|
||||
if (property.getter.is_engaged() && interface)
|
||||
{
|
||||
if (!as_generator(scope_tab << scope_tab << set_scope << "get;\n"
|
||||
).generate(sink, attributes::unused, context))
|
||||
return false;
|
||||
}
|
||||
else if (property.getter.is_engaged() && get_params == 0/*parameters.size() == 1 && property.getter.is_engaged()*/)
|
||||
{
|
||||
if (!as_generator
|
||||
(scope_tab << scope_tab << get_scope
|
||||
<< "get " << "{ return " + name_helpers::managed_method_name(*property.getter) + "(); }\n"
|
||||
).generate(sink, attributes::unused, context))
|
||||
return false;
|
||||
}
|
||||
else if (parameters.size() >= 1 && property.getter)
|
||||
{
|
||||
if (!as_generator
|
||||
(scope_tab << scope_tab << get_scope << "get "
|
||||
<< "{\n"
|
||||
<< *attribute_reorder<1, -1, 1>
|
||||
(scope_tab(3) << type(true) << " _out_"
|
||||
<< argument(false) << " = default(" << type(true) << ");\n"
|
||||
)
|
||||
<< scope_tab(3) << name_helpers::managed_method_name(*property.getter)
|
||||
<< "(" << (("out _out_" << argument(false)) % ",") << ");\n"
|
||||
<< scope_tab(3) << "return (" << (("_out_"<< argument(false)) % ",") << ");\n"
|
||||
<< scope_tab(2) << "}" << "\n"
|
||||
).generate(sink, std::make_tuple(parameters, parameters, parameters), context))
|
||||
return false;
|
||||
}
|
||||
// else if (parameters.size() == 1)
|
||||
// {
|
||||
// if (!as_generator
|
||||
// (scope_tab << scope_tab << get_scope << "get "
|
||||
// << "{\n"
|
||||
// << *attribute_reorder<1, -1, 1>(scope_tab(3) << type(true) << " _out_" << argument(false) << " = default(" << type(true) << ");\n")
|
||||
// << scope_tab(3) << name_helpers::managed_method_name(*property.getter)
|
||||
// << "(" << (("out _out_" << argument(false)) % ",") << ");\n"
|
||||
// << scope_tab(3) << "return " << (("_out_"<< argument(false)) % ",") << ";\n"
|
||||
// << scope_tab(2) << "}" << "\n"
|
||||
// ).generate(sink, std::make_tuple(parameters, parameters, parameters), context))
|
||||
// return false;
|
||||
// }
|
||||
|
||||
if (property.setter.is_engaged())
|
||||
if (!as_generator(scope_tab << scope_tab << set_scope << "set " << (interface ? ";" : "{ " + name_helpers::managed_method_name(*property.setter) + "(" + dir_mod + "value); }") << "\n"
|
||||
if (property.setter.is_engaged() && interface)
|
||||
{
|
||||
if (!as_generator(scope_tab << scope_tab << set_scope << "set;\n"
|
||||
).generate(sink, attributes::unused, context))
|
||||
return false;
|
||||
}
|
||||
else if (parameters.size() == 1 && property.setter.is_engaged())
|
||||
{
|
||||
if (!as_generator(scope_tab << scope_tab << set_scope << "set " << "{ " + name_helpers::managed_method_name(*property.setter) + "(" + dir_mod + "value); }\n"
|
||||
).generate(sink, attributes::unused, context))
|
||||
return false;
|
||||
}
|
||||
else if (parameters.size() > 1 && property.setter.is_engaged())
|
||||
{
|
||||
if (!as_generator(scope_tab << scope_tab << set_scope << "set " << ("{ " + name_helpers::managed_method_name(*property.setter) + "(" + dir_mod) << ((" value.Item" << counter(1)) % ", ") << "); }" << "\n"
|
||||
).generate(sink, parameters, context))
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!as_generator(scope_tab << "}\n").generate(sink, attributes::unused, context))
|
||||
return false;
|
||||
|
|
|
@ -60,6 +60,7 @@ inline std::string escape_keyword(std::string const& name)
|
|||
|| is_equal(name, "string")
|
||||
|| is_equal(name, "internal")
|
||||
|| is_equal(name, "fixed")
|
||||
|| is_equal(name, "var")
|
||||
|| is_equal(name, "base"))
|
||||
return "kw_" + name;
|
||||
|
||||
|
|
|
@ -488,23 +488,31 @@ struct marshall_parameter_generator
|
|||
// FIXME This seems to be used only in the else branch of the native function definition. Is it really needed?
|
||||
struct argument_generator
|
||||
{
|
||||
bool generate_direction;
|
||||
argument_generator () : generate_direction(true) {}
|
||||
argument_generator (bool r) : generate_direction(r) {}
|
||||
|
||||
template <typename OutputIterator, typename Context>
|
||||
bool generate(OutputIterator sink, attributes::parameter_def const& param, Context const& context) const
|
||||
{
|
||||
std::string param_name = escape_keyword(param.param_name);
|
||||
std::string direction = marshall_direction_modifier(param);
|
||||
|
||||
if (!param.type.original_type.visit(is_fp_visitor{}))
|
||||
return as_generator(
|
||||
direction << param_name
|
||||
).generate(sink, attributes::unused, context);
|
||||
|
||||
return as_generator(
|
||||
param_name << "_data, " << param_name << ", " << param_name << "_free_cb"
|
||||
).generate(sink, attributes::unused, context);
|
||||
if (generate_direction && !param.type.original_type.visit(is_fp_visitor{}))
|
||||
return as_generator(direction << param_name).generate(sink, attributes::unused, context);
|
||||
if (!generate_direction && !param.type.original_type.visit(is_fp_visitor{}))
|
||||
return as_generator(param_name).generate(sink, attributes::unused, context);
|
||||
else
|
||||
return as_generator
|
||||
(param_name << "_data, " << param_name << ", " << param_name << "_free_cb"
|
||||
).generate(sink, attributes::unused, context);
|
||||
|
||||
}
|
||||
|
||||
argument_generator operator ()(bool r) const
|
||||
{
|
||||
return {r};
|
||||
}
|
||||
} const argument {};
|
||||
|
||||
struct native_argument_invocation_generator
|
||||
|
|
|
@ -0,0 +1,59 @@
|
|||
#ifndef EOLIAN_CXX_COUNTER_HH_HH
|
||||
#define EOLIAN_CXX_COUNTER_HH_HH
|
||||
|
||||
#include <cstdlib>
|
||||
#include <cstring>
|
||||
|
||||
#include "grammar/generator.hpp"
|
||||
#include "grammar/attributes.hpp"
|
||||
#include "grammar/case.hpp"
|
||||
#include "grammar/integral.hpp"
|
||||
|
||||
namespace efl { namespace eolian { namespace grammar {
|
||||
|
||||
namespace detail {
|
||||
|
||||
}
|
||||
|
||||
struct counter_generator
|
||||
{
|
||||
std::shared_ptr<std::size_t> count;
|
||||
|
||||
template <typename OutputIterator, typename Attribute, typename Context>
|
||||
bool generate(OutputIterator sink, Attribute const&, Context const&) const
|
||||
{
|
||||
detail::generate_integral(sink, *count);
|
||||
++*count;
|
||||
return true;
|
||||
}
|
||||
};
|
||||
|
||||
struct counter_terminal
|
||||
{
|
||||
counter_generator operator()(std::size_t initial) const
|
||||
{
|
||||
return {std::shared_ptr<std::size_t>{new std::size_t{initial}}};
|
||||
}
|
||||
} const counter = {};
|
||||
|
||||
counter_generator as_generator(counter_terminal) { return {std::shared_ptr<std::size_t>{new std::size_t{0u}}}; }
|
||||
|
||||
template <>
|
||||
struct is_eager_generator<counter_generator> : std::true_type {};
|
||||
template <>
|
||||
struct is_eager_generator<counter_terminal> : std::true_type {};
|
||||
template <>
|
||||
struct is_generator<counter_terminal> : std::true_type {};
|
||||
template <>
|
||||
struct is_generator<counter_generator> : std::true_type {};
|
||||
|
||||
namespace type_traits {
|
||||
template <>
|
||||
struct attributes_needed<counter_generator> : std::integral_constant<int, 0> {};
|
||||
template <>
|
||||
struct attributes_needed<counter_terminal> : std::integral_constant<int, 0> {};
|
||||
}
|
||||
|
||||
} } }
|
||||
|
||||
#endif
|
|
@ -348,6 +348,14 @@ class TestCsharpProperties
|
|||
iface.IfaceProp = val;
|
||||
Test.AssertEquals(val, iface.IfaceProp);
|
||||
}
|
||||
|
||||
public static void test_csharp_multi_valued_prop()
|
||||
{
|
||||
var obj = new Dummy.TestObject();
|
||||
obj.MultiValuedProp = (1, 2);
|
||||
var ret = obj.MultiValuedProp;
|
||||
Test.AssertEquals(ret, (1, 2));
|
||||
}
|
||||
}
|
||||
|
||||
class TestEoGrandChildrenFinalize
|
||||
|
|
|
@ -16,6 +16,8 @@ typedef struct Dummy_Test_Object_Data
|
|||
int iface_prop;
|
||||
Eo *provider;
|
||||
Eo *iface_provider;
|
||||
int prop1;
|
||||
int prop2;
|
||||
|
||||
// Containers passed to C# as iterator/accessors
|
||||
Eina_Array *out_array;
|
||||
|
@ -4570,6 +4572,18 @@ Dummy_MyInt _dummy_test_object_bypass_typedef(EINA_UNUSED Eo *obj, EINA_UNUSED D
|
|||
return data;
|
||||
}
|
||||
|
||||
void _dummy_test_object_multi_valued_prop_get(Eo const* obj EINA_UNUSED, Dummy_Test_Object_Data* pd, int* prop1, int* prop2)
|
||||
{
|
||||
*prop1 = pd->prop1;
|
||||
*prop2 = pd->prop2;
|
||||
}
|
||||
|
||||
void _dummy_test_object_multi_valued_prop_set(Eo* obj EINA_UNUSED, Dummy_Test_Object_Data* pd, int prop1, int prop2)
|
||||
{
|
||||
pd->prop1 = prop1;
|
||||
pd->prop2 = prop2;
|
||||
}
|
||||
|
||||
/* Class Properties */
|
||||
static int _dummy_test_object_klass_prop = 0;
|
||||
|
||||
|
|
|
@ -1569,6 +1569,15 @@ class Dummy.Test_Object extends Efl.Object implements Dummy.Test_Iface {
|
|||
return: Dummy.MyInt;
|
||||
}
|
||||
|
||||
@property multi_valued_prop {
|
||||
get {}
|
||||
set {}
|
||||
values {
|
||||
prop1: int;
|
||||
prop2: int;
|
||||
}
|
||||
}
|
||||
|
||||
@property klass_prop @static {
|
||||
get {}
|
||||
set {}
|
||||
|
|
Loading…
Reference in New Issue