From 5425baa9061753356d450a3c736669341e1b1570 Mon Sep 17 00:00:00 2001 From: Jean-Philippe Andre Date: Wed, 22 Nov 2017 16:54:57 +0900 Subject: [PATCH] cxx: Add support for function pointers This was tested on the function pointer Efl.Ui.Format_Func_Cb --- src/bin/eolian_cxx/eolian_cxx.cc | 115 ++++++++++++++++++-- src/bindings/cxx/eo_cxx/eo_cxx_interop.hh | 20 +--- src/lib/eolian_cxx/grammar/converting_argument.hpp | 24 +--- .../eolian_cxx/grammar/function_declaration.hpp | 8 ++ src/lib/eolian_cxx/grammar/function_definition.hpp | 19 +++- src/lib/eolian_cxx/grammar/header.hpp | 1 + src/lib/eolian_cxx/grammar/klass_def.hpp | 60 +++++++++- src/lib/eolian_cxx/grammar/parameter.hpp | 29 ++++- .../grammar/type_function_declaration.hpp | 121 +++++++++++++++++++++ src/lib/eolian_cxx/grammar/types_definition.hpp | 38 +++++++ 10 files changed, 386 insertions(+), 49 deletions(-) create mode 100644 src/lib/eolian_cxx/grammar/type_function_declaration.hpp create mode 100644 src/lib/eolian_cxx/grammar/types_definition.hpp diff --git a/src/bin/eolian_cxx/eolian_cxx.cc b/src/bin/eolian_cxx/eolian_cxx.cc index 3fab179..b67d044 100644 --- a/src/bin/eolian_cxx/eolian_cxx.cc +++ b/src/bin/eolian_cxx/eolian_cxx.cc @@ -25,6 +25,7 @@ #include "grammar/klass_def.hpp" #include "grammar/header.hpp" #include "grammar/impl_header.hpp" +#include "grammar/types_definition.hpp" namespace eolian_cxx { @@ -58,7 +59,8 @@ opts_check(eolian_cxx::options_type const& opts) } static bool -generate(const Eolian_Class* klass, eolian_cxx::options_type const& opts) +generate(const Eolian_Class* klass, eolian_cxx::options_type const& opts, + std::string const& cpp_types_header) { std::string header_decl_file_name = opts.out_file.empty() ? (::eolian_class_file_get(klass) + std::string(".hh")) : opts.out_file; @@ -143,9 +145,9 @@ generate(const Eolian_Class* klass, eolian_cxx::options_type const& opts) } }; klass_function(klass); - + cpp_headers.erase(eolian_class_file_get(klass) + std::string(".hh")); - + std::string guard_name; as_generator(*(efl::eolian::grammar::string << "_") << efl::eolian::grammar::string << "_EO_HH") .generate(std::back_insert_iterator(guard_name) @@ -153,12 +155,13 @@ generate(const Eolian_Class* klass, eolian_cxx::options_type const& opts) , efl::eolian::grammar::context_null{}); std::tuple&, std::set& + , std::string const& , std::vector& , std::vector& , std::vector& , std::vector& > attributes - {guard_name, c_headers, cpp_headers, klasses, forward_klasses, klasses, klasses}; + {guard_name, c_headers, cpp_headers, cpp_types_header, klasses, forward_klasses, klasses, klasses}; if(opts.out_file == "-") { @@ -205,19 +208,117 @@ generate(const Eolian_Class* klass, eolian_cxx::options_type const& opts) return true; } +static bool +types_generate(std::string const& fname, options_type const& opts, + std::string& cpp_types_header) +{ + using namespace efl::eolian::grammar::attributes; + + std::vector functions; + Eina_Iterator *itr = eolian_declarations_get_by_file(fname.c_str()); + /* const */ Eolian_Declaration *decl; + + // Build list of functions with their parameters + while(::eina_iterator_next(itr, reinterpret_cast(&decl))) + { + Eolian_Declaration_Type dt = eolian_declaration_type_get(decl); + if (dt != EOLIAN_DECL_ALIAS) + continue; + + const Eolian_Typedecl *tp = eolian_declaration_data_type_get(decl); + if (!tp || eolian_typedecl_is_extern(tp)) + continue; + + if (::eolian_typedecl_type_get(tp) != EOLIAN_TYPEDECL_FUNCTION_POINTER) + continue; + + const Eolian_Function *func = eolian_typedecl_function_pointer_get(tp); + if (!func) return false; + + Eina_Iterator *param_itr = eolian_function_parameters_get(func); + std::vector params; + + /* const */ Eolian_Function_Parameter *param; + while (::eina_iterator_next(param_itr, reinterpret_cast(¶m))) + { + parameter_direction param_dir; + switch (eolian_parameter_direction_get(param)) + { + /* Note: Inverted on purpose, as the direction objects are + * passed is inverted (from C to C++ for function pointers). + * FIXME: This is probably not right in all cases. */ + case EOLIAN_IN_PARAM: param_dir = parameter_direction::out; break; + case EOLIAN_INOUT_PARAM: param_dir = parameter_direction::inout; break; + case EOLIAN_OUT_PARAM: param_dir = parameter_direction::in; break; + default: return false; + } + + const Eolian_Type *param_type_eolian = eolian_parameter_type_get(param); + type_def param_type(param_type_eolian, opts.unit, EOLIAN_C_TYPE_PARAM); + std::string param_name = eolian_parameter_name_get(param); + std::string param_c_type = eolian_type_c_type_get(param_type_eolian, EOLIAN_C_TYPE_PARAM); + parameter_def param_def(param_dir, param_type, param_name, param_c_type); + params.push_back(std::move(param_def)); + } + ::eina_iterator_free(param_itr); + + const Eolian_Type *ret_type_eolian = eolian_function_return_type_get(func, EOLIAN_FUNCTION_POINTER); + + type_def ret_type = void_; + if (ret_type_eolian) + ret_type = type_def(ret_type_eolian, opts.unit, EOLIAN_C_TYPE_RETURN); + + /* + // List namespaces. Not used as function_wrapper lives in efl::eolian. + std::vector namespaces; + Eina_Iterator *ns_itr = eolian_typedecl_namespaces_get(tp); + char *ns; + while (::eina_iterator_next(ns_itr, reinterpret_cast(&ns))) + namespaces.push_back(std::string(ns)); + ::eina_iterator_free(ns_itr); + */ + + std::string name = eolian_function_name_get(func); + std::string c_name = eolian_typedecl_full_name_get(tp); + std::replace(c_name.begin(), c_name.end(), '.', '_'); + bool beta = eolian_function_is_beta(func); + + function_def def(ret_type, name, params, c_name, beta, false, true); + functions.push_back(std::move(def)); + } + ::eina_iterator_free(itr); + + if (functions.empty()) + return true; + + std::stringstream sink; + + sink.write("\n", 1); + if (!efl::eolian::grammar::types_definition + .generate(std::ostream_iterator(sink), + functions, efl::eolian::grammar::context_null())) + return false; + + cpp_types_header = sink.str(); + + return true; +} + static void run(options_type const& opts) { if(!opts.main_header) { - const Eolian_Class *klass = NULL; + const Eolian_Class *klass = nullptr; char* dup = strdup(opts.in_files[0].c_str()); char* base = basename(dup); - klass = ::eolian_class_get_by_file(NULL, base); + std::string cpp_types_header; + klass = ::eolian_class_get_by_file(nullptr, base); free(dup); if (klass) { - if (!generate(klass, opts)) + if (!types_generate(base, opts, cpp_types_header) || + !generate(klass, opts, cpp_types_header)) { EINA_CXX_DOM_LOG_ERR(eolian_cxx::domain) << "Error generating: " << ::eolian_class_name_get(klass) diff --git a/src/bindings/cxx/eo_cxx/eo_cxx_interop.hh b/src/bindings/cxx/eo_cxx/eo_cxx_interop.hh index 662e5a7..9181c11 100644 --- a/src/bindings/cxx/eo_cxx/eo_cxx_interop.hh +++ b/src/bindings/cxx/eo_cxx/eo_cxx_interop.hh @@ -61,6 +61,8 @@ template <> struct out_traits { typedef void*& type; }; template struct out_traits> { typedef efl::shared_future& type; }; +template <> +struct out_traits { typedef efl::eina::strbuf_wrapper& type; }; template struct inout_traits { typedef T& type; }; @@ -291,23 +293,7 @@ auto convert_inout(V& object) -> decltype(impl::convert_inout_impl(object, impl: template T convert_to_c(V&& object); -template -void* data_function_ptr_to_c(T) -{ - return nullptr; -} - -template -F function_ptr_to_c() -{ - return nullptr; -} - -template -Eina_Free_Cb free_function_ptr_to_c() -{ - return nullptr; -} +template struct function_wrapper; namespace impl { diff --git a/src/lib/eolian_cxx/grammar/converting_argument.hpp b/src/lib/eolian_cxx/grammar/converting_argument.hpp index 230608f..eb7ef573 100644 --- a/src/lib/eolian_cxx/grammar/converting_argument.hpp +++ b/src/lib/eolian_cxx/grammar/converting_argument.hpp @@ -33,24 +33,12 @@ struct converting_argument_generator bool generate(OutputIterator sink, attributes::parameter_def const& param, Context const& ctx) const { attributes::qualifier_def qualifier = param.type.original_type.visit(attributes::get_qualifier_visitor{}); - bool is_function_ptr = param.type.original_type.visit(this->is_function_ptr); - if(is_function_ptr) - return as_generator - ( - attribute_reorder<-1, -1, 2, -1, -1, -1, -1> - ( - " ::efl::eolian::data_function_ptr_to_c<" << c_type - << ", " << parameter_type - << ">(" << string << ")" - - ", ::efl::eolian::function_ptr_to_c<" << c_type - << ", " << parameter_type - << ">()" - ", ::efl::eolian::free_function_ptr_to_c<" << c_type - << ", " << parameter_type - << ">()" - ) - ).generate(sink, param, ctx); + if (param.type.original_type.visit(this->is_function_ptr)) + { + // FIXME: This supports only one function pointer. + return as_generator("fw->data_to_c(), fw->func_to_c(), fw->free_to_c()") + .generate(sink, param, ctx); + } else return as_generator ( diff --git a/src/lib/eolian_cxx/grammar/function_declaration.hpp b/src/lib/eolian_cxx/grammar/function_declaration.hpp index e98fbf0..641ae65 100644 --- a/src/lib/eolian_cxx/grammar/function_declaration.hpp +++ b/src/lib/eolian_cxx/grammar/function_declaration.hpp @@ -1,3 +1,4 @@ + #ifndef EOLIAN_CXX_FUNCTION_DECLARATION_HH #define EOLIAN_CXX_FUNCTION_DECLARATION_HH @@ -42,6 +43,13 @@ struct function_declaration_generator !as_generator("#ifdef " << *(string << "_") << string << "_" << string << "_BETA\n") .generate(sink, std::make_tuple(_klass_name.namespaces, _klass_name.eolian_name, suffix), add_upper_case_context(ctx))) return false; + + std::string template_statement(f.template_statement()); + if (!template_statement.empty() && + !as_generator(template_statement << " ") + .generate(sink, attributes::unused, ctx)) + return false; + if(!as_generator ("::efl::eolian::return_traits<" << grammar::type(true) << ">::type " << string << "(" << (parameter % ", ") << ") const;\n") .generate(sink, std::make_tuple(f.return_type, escape_keyword(f.name), f.parameters), ctx)) diff --git a/src/lib/eolian_cxx/grammar/function_definition.hpp b/src/lib/eolian_cxx/grammar/function_definition.hpp index 50bd997..55f5621 100644 --- a/src/lib/eolian_cxx/grammar/function_definition.hpp +++ b/src/lib/eolian_cxx/grammar/function_definition.hpp @@ -17,6 +17,7 @@ #include "grammar/attribute_conditional.hpp" #include "grammar/attribute_reorder.hpp" #include "grammar/type_impl.hpp" +#include "grammar/eps.hpp" namespace efl { namespace eolian { namespace grammar { @@ -52,12 +53,24 @@ struct function_definition_generator !as_generator("#ifdef " << *(string << "_") << string << "_PROTECTED\n") .generate(sink, std::make_tuple(_klass_name.namespaces, _klass_name.eolian_name), add_upper_case_context(ctx))) return false; - + + std::string template_statement(f.template_statement()); + if (!template_statement.empty() && + !as_generator(template_statement << "\n") + .generate(sink, attributes::unused, ctx)) + return false; + if(!as_generator ("inline ::efl::eolian::return_traits<" << grammar::type(true) << ">::type " << string << "::" << string << "(" << (parameter % ", ") << ") const\n{\n") .generate(sink, std::make_tuple(f.return_type, _klass_name.eolian_name, escape_keyword(f.name), f.parameters), ctx)) return false; + std::vector opening_statements(f.opening_statements()); + if (!opening_statements.empty() && + !as_generator(scope_tab << *(string) << "\n") + .generate(sink, std::make_tuple(opening_statements), ctx)) + return false; + auto out_declaration = attribute_conditional([] (attributes::parameter_def const& p) -> bool { return p.direction == attributes::parameter_direction::out; }) @@ -118,8 +131,8 @@ struct function_definition_generator scope_tab << "::efl::eolian::assign_out<" << parameter_type << ", " << c_type << ( - attribute_conditional([] (attributes::type_def const& type) - { return type.original_type.visit(attributes::get_qualifier_visitor{}) & qualifier_info::is_own; }) + attribute_conditional([] (attributes::type_def const& typ) + { return typ.original_type.visit(attributes::get_qualifier_visitor{}) & qualifier_info::is_own; }) [ ", true" ] | eps diff --git a/src/lib/eolian_cxx/grammar/header.hpp b/src/lib/eolian_cxx/grammar/header.hpp index 63a6095..4f1df92 100644 --- a/src/lib/eolian_cxx/grammar/header.hpp +++ b/src/lib/eolian_cxx/grammar/header.hpp @@ -24,6 +24,7 @@ auto class_header = << "#include \n" "#include \n" << *header_include_directive // sequence + << string // extra header << *class_declaration // sequence | class << *class_forward_declaration // sequence | class << "\nnamespace eo_cxx {\n" diff --git a/src/lib/eolian_cxx/grammar/klass_def.hpp b/src/lib/eolian_cxx/grammar/klass_def.hpp index edfadc0..f7b12af 100644 --- a/src/lib/eolian_cxx/grammar/klass_def.hpp +++ b/src/lib/eolian_cxx/grammar/klass_def.hpp @@ -433,6 +433,7 @@ struct function_def std::string filename; bool is_beta; bool is_protected; + bool is_function_pointer; friend inline bool operator==(function_def const& lhs, function_def const& rhs) { @@ -442,17 +443,27 @@ struct function_def && lhs.c_name == rhs.c_name && lhs.filename == rhs.filename && lhs.is_beta == rhs.is_beta - && lhs.is_protected == rhs.is_protected; + && lhs.is_protected == rhs.is_protected + && lhs.is_function_pointer == rhs.is_function_pointer; } friend inline bool operator!=(function_def const& lhs, function_def const& rhs) { return !(lhs == rhs); } - + function_def() = default; function_def(type_def return_type, std::string name, std::vector parameters , std::string c_name, std::string filename, bool is_beta) : return_type(return_type), name(name), parameters(parameters), c_name(c_name), filename(filename), is_beta(is_beta) {} - function_def() = default; + function_def(type_def _return_type, std::string const& _name, + std::vector const& _parameters, + std::string const& _c_name, + bool _is_beta = false, + bool _is_protected = false, + bool _is_function_pointer = false) + : return_type(_return_type), name(_name), parameters(_parameters), + c_name(_c_name), is_beta(_is_beta), is_protected(_is_protected), + is_function_pointer(_is_function_pointer) {} + function_def( ::Eolian_Function const* function, Eolian_Function_Type type, Eolian_Unit const* unit) : return_type(void_) { @@ -517,6 +528,49 @@ struct function_def is_protected = eolian_function_scope_get(function, type) == EOLIAN_SCOPE_PROTECTED; is_protected = eolian_function_scope_get(function, type) == EOLIAN_SCOPE_PROTECTED; } + + std::string template_statement() const + { + std::string statement; + char template_typename = 'F'; + for (auto const& param : this->parameters) + { + attributes::regular_type_def const* typ = + efl::eina::get(¶m.type.original_type); + if (typ && typ->is_function_ptr) + { + char typenam[2] = { 0, }; + typenam[0] = template_typename++; + if (statement.empty()) + statement = std::string("template "; + } + + std::vector opening_statements() const + { + // FIXME: Supports only one function pointer + std::vector statements; + char template_typename = 'F'; + for (auto const& param : this->parameters) + { + attributes::regular_type_def const* typ = + efl::eina::get(¶m.type.original_type); + if (typ && typ->is_function_ptr) + { + char typenam[2] = { 0, }; + typenam[0] = template_typename++; + std::string statement = "auto fw = new ::efl::eolian::function_wrapper<"; + statement += param.c_type + ", " + typenam + ">(" + param.param_name + ");"; + statements.push_back(statement); + } + } + return statements; + } }; template <> diff --git a/src/lib/eolian_cxx/grammar/parameter.hpp b/src/lib/eolian_cxx/grammar/parameter.hpp index f9e625a..5d8d3ae 100644 --- a/src/lib/eolian_cxx/grammar/parameter.hpp +++ b/src/lib/eolian_cxx/grammar/parameter.hpp @@ -25,6 +25,12 @@ struct parameter_type_generator dir = "in"; break; } + + attributes::regular_type_def const* typ = + efl::eina::get(¶m.type.original_type); + if (typ && typ->is_function_ptr) + return as_generator("F").generate(sink, attributes::unused, context); + return as_generator ( " ::efl::eolian::" << string << "_traits<" @@ -44,6 +50,8 @@ struct attributes_needed : std::integral_constant @@ -59,9 +67,28 @@ template <> struct is_generator : std::true_type {}; namespace type_traits { template <> -struct attributes_needed : std::integral_constant {}; +struct attributes_needed : std::integral_constant {}; } parameter_generator const parameter = {}; + + +/* */ +struct parameter_as_argument_generator +{ + template + bool generate(OutputIterator sink, attributes::parameter_def const& param, Context const& context) const + { + return as_generator(parameter_type << "(" << string << ")").generate(sink, std::make_tuple(param, param.param_name), context); + } +}; + +template <> +struct is_eager_generator : std::true_type {}; +namespace type_traits { +template <> +struct attributes_needed : std::integral_constant {}; +} +parameter_as_argument_generator const parameter_as_argument = {}; } } } diff --git a/src/lib/eolian_cxx/grammar/type_function_declaration.hpp b/src/lib/eolian_cxx/grammar/type_function_declaration.hpp new file mode 100644 index 0000000..eccedf8 --- /dev/null +++ b/src/lib/eolian_cxx/grammar/type_function_declaration.hpp @@ -0,0 +1,121 @@ +#ifndef EOLIAN_CXX_TYPE_FUNCTION_DECLARATION_HH +#define EOLIAN_CXX_TYPE_FUNCTION_DECLARATION_HH + +#include "grammar/generator.hpp" +#include "grammar/klass_def.hpp" + +#include "grammar/string.hpp" +#include "grammar/indentation.hpp" +#include "grammar/list.hpp" +#include "grammar/alternative.hpp" +#include "grammar/type.hpp" +#include "grammar/parameter.hpp" +#include "grammar/keyword.hpp" +#include "grammar/converting_argument.hpp" +#include "grammar/eps.hpp" + +namespace efl { namespace eolian { namespace grammar { + +/** This generates the caller struct for function pointers. */ +struct type_function_declaration_generator { + type_function_declaration_generator() {} + + template + bool generate(OutputIterator sink, attributes::function_def const& f, Context const& ctx) const + { + std::string guard = f.c_name + "_defined"; + + if (!as_generator("#ifndef " << string << "\n" << + "#define " << string << "\n") + .generate(sink, std::make_tuple(guard, guard), add_upper_case_context(ctx))) + return false; + + // efl::eolian::function_wrapper + if (!as_generator("namespace efl { namespace eolian {\n") + .generate(sink, attributes::unused, add_lower_case_context(ctx))) + return false; + + if (!as_generator( + "template \n" + "struct function_wrapper<" << string << ", F> {\n" + << scope_tab << "function_wrapper(F cxx_func) : _cxx_func(cxx_func) {}\n" + ).generate(sink, f.c_name, ctx)) + return false; + + if (!as_generator( + scope_tab << "void *data_to_c() { return static_cast(this); }\n" + << scope_tab << string << " func_to_c() const { return &caller; }\n" + << scope_tab << "Eina_Free_Cb free_to_c() const { return &deleter; }\n" + << "private:\n" + << scope_tab << "F _cxx_func;\n" + << scope_tab << "static void deleter(void *data) {\n" + << scope_tab << scope_tab << "delete static_cast*>(data);\n" + << scope_tab << "}\n" + ).generate(sink, std::make_tuple(f.c_name, f.c_name), ctx)) + return false; + + std::vector c_args; + for (auto itr : f.parameters) + c_args.push_back(", " + itr.c_type + " " + itr.param_name); + if (!as_generator( + scope_tab << "static " << string << " caller(void *cxx_call_data" + << *(string) << ") {\n" + << scope_tab << scope_tab << "auto fw = static_cast*>(cxx_call_data);\n" + ).generate(sink, std::make_tuple(f.return_type.c_type, c_args, f.c_name), ctx)) + return false; + + if (f.return_type != attributes::void_ + && !as_generator(scope_tab << scope_tab << "auto __return_value =\n") + .generate(sink, attributes::unused, ctx)) + return false; + + if (!f.parameters.empty()) + { + std::vector params; + for (auto itr = f.parameters.begin() + 1; itr != f.parameters.end(); itr++) + params.push_back(*itr); + if (!as_generator( + scope_tab << scope_tab << "fw->_cxx_func(" << parameter_as_argument << *(", " << parameter_as_argument) << ");\n" + ).generate(sink, std::make_tuple(*f.parameters.begin(), params), ctx)) + return false; + } + + if (f.return_type != attributes::void_ + && !as_generator(scope_tab << scope_tab << "return ::efl::eolian::convert_to_c<" + << type << ">(__return_value);\n") + .generate(sink, f.return_type, ctx)) + return false; + + if (!as_generator(scope_tab << "}\n").generate(sink, attributes::unused, ctx)) + return false; + + if (!as_generator("};\n" + "} }\n" + "#endif\n\n") + .generate(sink, attributes::unused, ctx)) + return false; + + return true; + } +}; + +template <> +struct is_eager_generator : std::true_type {}; + +namespace type_traits { +template <> +struct attributes_needed : std::integral_constant {}; +} + +struct type_function_declaration_terminal +{ + type_function_declaration_generator operator()() const + { + return type_function_declaration_generator{}; + } +} const type_function_declaration = {}; + +} } } + +#endif diff --git a/src/lib/eolian_cxx/grammar/types_definition.hpp b/src/lib/eolian_cxx/grammar/types_definition.hpp new file mode 100644 index 0000000..e4c4073 --- /dev/null +++ b/src/lib/eolian_cxx/grammar/types_definition.hpp @@ -0,0 +1,38 @@ +#ifndef EOLIAN_CXX_TYPES_DEFINITION_HH +#define EOLIAN_CXX_TYPES_DEFINITION_HH + +#include "header_guards.hpp" +#include "eps.hpp" +#include "string.hpp" +#include "sequence.hpp" +#include "kleene.hpp" +#include "header_include_directive.hpp" +#include "type_function_declaration.hpp" + +namespace efl { namespace eolian { namespace grammar { + +struct types_definition_generator +{ + template + bool generate(OutputIterator sink, std::vector const& functions, Context const& ctx) const + { + if(!as_generator(*(type_function_declaration())) + .generate(sink, functions, ctx)) + return false; + return true; + } +}; + +template <> +struct is_eager_generator : std::true_type {}; + +namespace type_traits { +template <> +struct attributes_needed : std::integral_constant {}; +} + +types_definition_generator const types_definition = {}; + +} } } + +#endif -- 2.7.4