Skip to content

Commit

Permalink
Add support for julia binding name customization with the julia_names
Browse files Browse the repository at this point in the history
configuration parameter and test cleanup.

Test cleanup:
- removed version pinning of tests which were failing with CxxWrap
1.5, since it is now fixed with the latest version, 1.6.
- added uuid in .wit
- run test with project `build` instead of temporary one, in order
  to used the CxxWrap version required by the test when it is imposed.
  • Loading branch information
grasph committed Jul 27, 2024
1 parent 1bd07c7 commit 5168a24
Show file tree
Hide file tree
Showing 57 changed files with 224 additions and 125 deletions.
13 changes: 13 additions & 0 deletions doc/config.md
Original file line number Diff line number Diff line change
Expand Up @@ -112,6 +112,19 @@ fields_and_variables = true
# given class.
#
inheritances = [ "" ]
# List of customized C++-to-Julia name mapping. Each element of the list must
# be a string with format <C++ fully qualified name> -> <julia name>.
#
# E. g., ["TDirectoryFile::Get -> Get_", "TDirectory::Get -> Get_" ] will
# request to name `Get_` the Julia bindings to the `Get` methods of C++ classes
# `TDirectoryFile` and `TDirectory`.
#
# This feature is useful to customize a julia binding: changing the name
# of a function, for instance by appending an underscore as in the above
# example, allows the implementation in Julia of a wrapper with the original
# function name, that can make the required customization.
julia_names = [ ]
```

### Extra options for the julia module code generation
Expand Down
26 changes: 19 additions & 7 deletions src/CodeTree.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -843,7 +843,7 @@ CodeTree::generate_accessor_cxx(std::ostream& o, const TypeRcd* type_rcd,
const CXCursor& cursor, bool getter_only,
int nindents){

FunctionWrapper helper(MethodRcd(cursor), type_rcd, type_map_,
FunctionWrapper helper(cxx_to_julia_, MethodRcd(cursor), type_rcd, type_map_,
cxxwrap_version_, "", "", nindents);

int ngens = 0;
Expand Down Expand Up @@ -971,7 +971,7 @@ CodeTree::method_cxx_decl(std::ostream& o, const MethodRcd& method,

TypeRcd* pTypeRcd = find_class_of_method(method.cursor);

FunctionWrapper wrapper(method, pTypeRcd, type_map_, cxxwrap_version_, varname,
FunctionWrapper wrapper(cxx_to_julia_, method, pTypeRcd, type_map_, cxxwrap_version_, varname,
classname, nindents, templated);

//FIXME: check that code below is needed. Should now be vetoed upstream
Expand Down Expand Up @@ -1278,7 +1278,7 @@ CodeTree::visit_global_function(CXCursor cursor){
}


FunctionWrapper wrapper(MethodRcd(cursor), nullptr, type_map_, cxxwrap_version_);
FunctionWrapper wrapper(cxx_to_julia_, MethodRcd(cursor), nullptr, type_map_, cxxwrap_version_);
if(in_veto_list(wrapper.signature())){
// if(std::find(veto_list_.begin(), veto_list_.end(), wrapper.signature()) != veto_list_.end()){
if(verbose > 0){
Expand Down Expand Up @@ -1732,7 +1732,7 @@ CodeTree::visit_member_function(CXCursor cursor){

TypeRcd* pTypeRcd = find_class_of_method(cursor);

FunctionWrapper wrapper(MethodRcd(cursor), pTypeRcd, type_map_, cxxwrap_version_);
FunctionWrapper wrapper(cxx_to_julia_, MethodRcd(cursor), pTypeRcd, type_map_, cxxwrap_version_);

if(in_veto_list(wrapper.signature())){
// if(std::find(veto_list_.begin(), veto_list_.end(), wrapper.signature()) != veto_list_.end()){
Expand Down Expand Up @@ -1830,8 +1830,8 @@ CodeTree::inform_missing_types(std::vector<CXType> missing_types,
};

if(verbose > 0){
std::string funcname = FunctionWrapper(methodRcd, classRcd, type_map_,
cxxwrap_version_).signature();
std::string funcname = FunctionWrapper(cxx_to_julia_, methodRcd, classRcd,
type_map_, cxxwrap_version_).signature();
std::cerr << "Warning: missing definition of type "
<< (missing_types.size() > 1 ? "s" : "")
<< " "
Expand Down Expand Up @@ -1930,7 +1930,7 @@ CodeTree::visit_class_constructor(CXCursor cursor){

TypeRcd* pTypeRcd = find_class_of_method(cursor);

FunctionWrapper wrapper(MethodRcd(cursor), pTypeRcd, type_map_,
FunctionWrapper wrapper(cxx_to_julia_, MethodRcd(cursor), pTypeRcd, type_map_,
cxxwrap_version_);

if(in_veto_list(wrapper.signature())){
Expand Down Expand Up @@ -3031,3 +3031,15 @@ void CodeTree::generate_project_file(std::ostream& o,
"CxxWrap = \"" << version_int_to_string(cxxwrap_version_, version_depth)
<< "\"\n";
}

void CodeTree::set_julia_names(const std::vector<std::string>& name_map){
cxx_to_julia_.clear();
std::regex re("\\s*->\\s");
for(const std::string& m: name_map){
std::sregex_token_iterator it{m.begin(), m.end(), re, -1};
std::vector<std::string> tokens{it, {}};
if(tokens.size() > 1){
cxx_to_julia_[tokens[0]] = tokens[1];
}
}
}
8 changes: 7 additions & 1 deletion src/CodeTree.h
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@
#include <iostream>
#include <filesystem>
#include <tuple>
#include <map>

//#include "Entity.h"
#include "TypeRcd.h"
Expand Down Expand Up @@ -82,7 +83,7 @@ namespace codetree{
std::vector<std::string> files_to_wrap_;
std::vector<std::string> files_to_wrap_fullpaths_;
std::vector<std::string> wrapped_methods_;

int n_classes_per_file_;
std::string out_cxx_dir_;
std::string out_jl_dir_;
Expand Down Expand Up @@ -275,6 +276,8 @@ namespace codetree{

void set_force_mode(bool forced){ out_open_mode_ = forced ? std::ios_base::out : std::ios_base::app; }

void set_julia_names(const std::vector<std::string>& name_map);

protected:

enum class accessor_mode_t {none, getter, both };
Expand Down Expand Up @@ -463,6 +466,7 @@ namespace codetree{
bool is_natively_supported(const std::string& type_fqn,
int* nparams = nullptr) const;


private:
std::string clang_resource_dir_;

Expand All @@ -476,6 +480,8 @@ namespace codetree{

std::string header_file_path_;

std::map<std::string, std::string> cxx_to_julia_;

std::string module_name_;

std::ios_base::openmode out_open_mode_;
Expand Down
172 changes: 100 additions & 72 deletions src/FunctionWrapper.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -480,13 +480,15 @@ FunctionWrapper::arg_decl(int iarg, bool argtypes_only) const{
}


FunctionWrapper::FunctionWrapper(const MethodRcd& method,
FunctionWrapper::FunctionWrapper(const std::map<std::string, std::string>& name_map,
const MethodRcd& method,
const TypeRcd* pTypeRcd,
const TypeMapper& type_map,
long cxxwrap_version,
std::string varname, std::string classname,
int nindents, bool templated):
method(method),
// name_map_(name_map),
varname_(varname),
classname(classname),
cxxwrap_version_(cxxwrap_version),
Expand Down Expand Up @@ -528,8 +530,6 @@ FunctionWrapper::FunctionWrapper(const MethodRcd& method,
name_cxx = fully_qualified_name(cursor);
}

std::string name_jl_suffix = jl_type_name(name_cxx);

is_static_ = clang_Cursor_getStorageClass(cursor) == CX_SC_Static;

if(name_cxx == "operator new" || name_cxx == "operator delete"
Expand All @@ -540,38 +540,35 @@ FunctionWrapper::FunctionWrapper(const MethodRcd& method,
varname_ = is_static_ ? "module_" : "t";
}

static std::regex opregex("(^|.*::)operator[[:space:]]*(.*)$");
std::cmatch m;
override_base_ = false;

//number of args including the implicit "this"
//of non-statis method
int noperands = clang_getNumArgTypes(method_type);
if((this->classname.size()) != 0 && !is_static_) noperands += 1;

if(std::regex_match(name_cxx.c_str(), m, opregex)){
name_jl_suffix = m[2];
}

//FIXME: check that julia = operator is not already mapped to c++ operator=
//by CxxWrap. In that case we won't need to define an assign function
if(name_jl_suffix == "="){
name_jl_suffix = "assign";
}

if(name_jl_suffix == "*" && noperands == 1){
name_jl_suffix = "getindex"; //Deferencing operator, *x -> x[]
override_base_ = true;
}

if(name_jl_suffix == "[]"){
name_jl_suffix = "getindex";
override_base_ = true;

//--------

auto full_name = class_prefix + name_cxx;
auto it = name_map.find(full_name);

std::string name_jl_suffix;
if(it != name_map.end()){ //custom name mapping
name_jl_suffix = it->second;
if(verbose > 0){
std::cerr << "Info name mapping: " << full_name << " mapped to " << name_jl_ << "\n";
}
} else{// automatic name mapping
if(verbose > 4){
std::cerr << "Info name mapping: no name customization for " << full_name
<< ", standard naming rules will be used.\n";
}
name_jl_suffix = get_name_jl_suffix(name_cxx, noperands);
}

if(name_jl_suffix == "+"){
//Base.+ can take an arbitrary number of arguments
//including zero, case that corrsponds to the prefix operator

if(name_jl_suffix == "getindex" || name_jl_suffix == "+"){
override_base_ = true;
}

Expand All @@ -584,51 +581,28 @@ FunctionWrapper::FunctionWrapper(const MethodRcd& method,
}

static std::vector<std::string> infix_base_ops = { "-", "*", "/",
"%", "[]", "&", "|", "^", ">>", ">>>", "<<", ">", "<", "<=", ">=",
"==", "!=", "<=>"};
"%", "[]", "&", "|", "xor", ">>", ">>>", "<<", ">", "<", "<=", ">=",
"==", "!=", "cmp"};

if(noperands == 2
&& std::find(infix_base_ops.begin(), infix_base_ops.end(),
name_jl_suffix) != infix_base_ops.end()){
override_base_ = true;
}

//TODO map cast operator to convert
//T A::operator R();

//C++ -> Julia operation name map for operators65;6203;1c
//When not in the list, operatorOP() is mapped to Base.OP()
std::vector<std::pair<std::string, std::string>> op_map = {
{"()", "paren"},
{"+=", "add!"},
{"-=", "sub!"},
{"*=", "mult!"},
{"/=", "fdiv!"},
{"%=", "rem!"},
{"^=", "xor!"},
{"|=", "or!"},
{"&=", "and!"},
{"<<=", "lshit!"},
{">>=", "rshit!"},
{"^", "xor"},
{"->", "arrow"},
{"->*", "arrowstar"},
{",", "comma"},
{"<=>", "cmp"},
{"--", "dec!"},
{"++", "inc!"},
{"&&", "logicaland"},//&& and ||, to which short-circuit evalution is applied
{"||", "logicalor"},//cannot be overloaded in Julia.
};

for(const auto& m: op_map){
if(name_jl_suffix == m.first){
name_jl_suffix = m.second;
if(is_static_){
if(templated_){
name_jl_ = jl_type_name(pTypeRcd->type_name + "::") + name_jl_suffix;
} else{
name_jl_ = jl_type_name(class_prefix) + name_jl_suffix;
}
} else{
//FIXME: Add prefix. The namespace?
name_jl_ = name_jl_suffix;
}



setindex_ = getindex_ = false;

if(name_cxx == "operator[]"){
getindex_ = true;

Expand All @@ -637,17 +611,6 @@ FunctionWrapper::FunctionWrapper(const MethodRcd& method,
setindex_ = true;
}
}

if(is_static_){
if(templated_){
name_jl_ = jl_type_name(pTypeRcd->type_name + "::") + name_jl_suffix;
} else{
name_jl_ = jl_type_name(class_prefix) + name_jl_suffix;
}
} else{
//FIXME: Add prefix. The namespace?
name_jl_ = name_jl_suffix;
}

bool argtype_mapped;
build_arg_lists(argtype_mapped);
Expand Down Expand Up @@ -789,3 +752,68 @@ std::string FunctionWrapper::fix_template_type(std::string type_name) const{

return fixed;
}

std::string FunctionWrapper::get_name_jl_suffix(const std::string& cxx_name,
int noperands) const{
auto name_jl_suffix = jl_type_name(name_cxx);


static std::regex opregex("(^|.*::)operator[[:space:]]*(.*)$");
std::cmatch m;

if(std::regex_match(name_cxx.c_str(), m, opregex)){
name_jl_suffix = m[2];
}

//FIXME: check that julia = operator is not already mapped to c++ operator=
//by CxxWrap. In that case we won't need to define an assign function
if(name_jl_suffix == "="){
name_jl_suffix = "assign";
}

if(name_jl_suffix == "*" && noperands == 1){
name_jl_suffix = "getindex"; //Deferencing operator, *x -> x[]
}

if(name_jl_suffix == "getindex"){
name_jl_suffix = "getindex";
}

if(name_jl_suffix == "+"){
//Base.+ can take an arbitrary number of arguments
//including zero, case that corrsponds to the prefix operator
}


//C++ -> Julia operation name map for operators
//When not in the list, operatorOP() is mapped to Base.OP()
std::vector<std::pair<std::string, std::string>> op_map = {
{"()", "paren"},
{"+=", "add!"},
{"-=", "sub!"},
{"*=", "mult!"},
{"/=", "fdiv!"},
{"%=", "rem!"},
{"^=", "xor!"},
{"|=", "or!"},
{"&=", "and!"},
{"<<=", "lshit!"},
{">>=", "rshit!"},
{"^", "xor"},
{"->", "arrow"},
{"->*", "arrowstar"},
{",", "comma"},
{"<=>", "cmp"},
{"--", "dec!"},
{"++", "inc!"},
{"&&", "logicaland"},//&& and ||, to which short-circuit evalution is applied
{"||", "logicalor"},//cannot be overloaded in Julia.
};

for(const auto& m: op_map){
if(name_jl_suffix == m.first){
name_jl_suffix = m.second;
}
}
return name_jl_suffix;
}
Loading

0 comments on commit 5168a24

Please sign in to comment.