cxx: Add support for function pointers

This was tested on the function pointer Efl.Ui.Format_Func_Cb
This commit is contained in:
Jean-Philippe Andre 2017-11-22 16:54:57 +09:00
parent a597084db7
commit 5425baa906
10 changed files with 386 additions and 49 deletions

View File

@ -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<std::string>(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::string, std::set<std::string>&, std::set<std::string>&
, std::string const&
, std::vector<efl::eolian::grammar::attributes::klass_def>&
, std::vector<efl::eolian::grammar::attributes::klass_def>&
, std::vector<efl::eolian::grammar::attributes::klass_def>&
, std::vector<efl::eolian::grammar::attributes::klass_def>&
> 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<function_def> 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<void**>(&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<parameter_def> params;
/* const */ Eolian_Function_Parameter *param;
while (::eina_iterator_next(param_itr, reinterpret_cast<void **>(&param)))
{
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<std::string> namespaces;
Eina_Iterator *ns_itr = eolian_typedecl_namespaces_get(tp);
char *ns;
while (::eina_iterator_next(ns_itr, reinterpret_cast<void**>(&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<char>(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)

View File

@ -61,6 +61,8 @@ template <>
struct out_traits<void*> { typedef void*& type; };
template <typename T>
struct out_traits<efl::shared_future<T>> { typedef efl::shared_future<T>& type; };
template <>
struct out_traits<efl::eina::strbuf> { typedef efl::eina::strbuf_wrapper& type; };
template <typename T>
struct inout_traits { typedef T& type; };
@ -291,23 +293,7 @@ auto convert_inout(V& object) -> decltype(impl::convert_inout_impl(object, impl:
template <typename T, typename U, bool Own = false, typename V>
T convert_to_c(V&& object);
template <typename F, typename T>
void* data_function_ptr_to_c(T)
{
return nullptr;
}
template <typename F, typename T>
F function_ptr_to_c()
{
return nullptr;
}
template <typename F, typename T>
Eina_Free_Cb free_function_ptr_to_c()
{
return nullptr;
}
template <typename U, typename F, typename V=void> struct function_wrapper;
namespace impl {

View File

@ -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
(

View File

@ -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))

View File

@ -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<std::string> 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

View File

@ -24,6 +24,7 @@ auto class_header =
<< "#include <Eina.hh>\n"
"#include <Eo.hh>\n"
<< *header_include_directive // sequence<string>
<< string // extra header <string>
<< *class_declaration // sequence<class> | class
<< *class_forward_declaration // sequence<class> | class
<< "\nnamespace eo_cxx {\n"

View File

@ -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<parameter_def> 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<parameter_def> 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<attributes::regular_type_def>(&param.type.original_type);
if (typ && typ->is_function_ptr)
{
char typenam[2] = { 0, };
typenam[0] = template_typename++;
if (statement.empty())
statement = std::string("template <typename ") + typenam;
else
statement += std::string(", typename ") + typenam;
}
}
if (statement.empty()) return statement;
else return statement + ">";
}
std::vector<std::string> opening_statements() const
{
// FIXME: Supports only one function pointer
std::vector<std::string> statements;
char template_typename = 'F';
for (auto const& param : this->parameters)
{
attributes::regular_type_def const* typ =
efl::eina::get<attributes::regular_type_def>(&param.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 <>

View File

@ -25,6 +25,12 @@ struct parameter_type_generator
dir = "in";
break;
}
attributes::regular_type_def const* typ =
efl::eina::get<attributes::regular_type_def>(&param.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<parameter_type_generator> : std::integral_constant<int,
parameter_type_generator const parameter_type = {};
/* */
struct parameter_generator
{
template <typename OutputIterator, typename Context>
@ -59,9 +67,28 @@ template <>
struct is_generator<parameter_generator> : std::true_type {};
namespace type_traits {
template <>
struct attributes_needed<parameter_generator> : std::integral_constant<int, 1> {};
struct attributes_needed<parameter_generator> : std::integral_constant<int, 1> {};
}
parameter_generator const parameter = {};
/* */
struct parameter_as_argument_generator
{
template <typename OutputIterator, typename Context>
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<parameter_as_argument_generator> : std::true_type {};
namespace type_traits {
template <>
struct attributes_needed<parameter_as_argument_generator> : std::integral_constant<int, 1> {};
}
parameter_as_argument_generator const parameter_as_argument = {};
} } }

View File

@ -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 <typename OutputIterator, typename Context>
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<T, F>
if (!as_generator("namespace efl { namespace eolian {\n")
.generate(sink, attributes::unused, add_lower_case_context(ctx)))
return false;
if (!as_generator(
"template <typename F>\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<void *>(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<function_wrapper<" << string << ", F>*>(data);\n"
<< scope_tab << "}\n"
).generate(sink, std::make_tuple(f.c_name, f.c_name), ctx))
return false;
std::vector<std::string> 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<function_wrapper<"
<< string << ", F>*>(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<attributes::parameter_def> 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<type_function_declaration_generator> : std::true_type {};
namespace type_traits {
template <>
struct attributes_needed<type_function_declaration_generator> : std::integral_constant<int, 1> {};
}
struct type_function_declaration_terminal
{
type_function_declaration_generator operator()() const
{
return type_function_declaration_generator{};
}
} const type_function_declaration = {};
} } }
#endif

View File

@ -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 <typename OutputIterator, typename Context>
bool generate(OutputIterator sink, std::vector<attributes::function_def> 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<types_definition_generator> : std::true_type {};
namespace type_traits {
template <>
struct attributes_needed<types_definition_generator> : std::integral_constant<int, 1> {};
}
types_definition_generator const types_definition = {};
} } }
#endif