Skip to content

Commit

Permalink
Merge master
Browse files Browse the repository at this point in the history
  • Loading branch information
kliegeois committed Oct 17, 2023
2 parents 55bb0dd + 184120d commit 62c47f7
Show file tree
Hide file tree
Showing 66 changed files with 702 additions and 184 deletions.
12 changes: 8 additions & 4 deletions CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,6 @@ CMAKE_MINIMUM_REQUIRED(VERSION 2.8.0)
PROJECT(binder CXX C)
SET(VERSION 1.0.0)
option(STATIC "Statically compile Binder. See `documentation/install.rst` for more information." OFF)
#This actually will work even with C++11
set(CMAKE_CXX_STANDARD 14)
set(CMAKE_VERBOSE_MAKEFILE ON)
#So far there are exceptions in config.cpp
set(LLVM_REQUIRES_EH ON)
Expand All @@ -29,7 +27,7 @@ MESSAGE(STATUS "binder: Using CMAKE_MODULE_PATH=${CMAKE_MODULE_PATH} to search
find_package(Clang CONFIG QUIET PATHS /usr/lib64/cmake/clang /usr/lib/cmake/clang /usr/share/clang/cmake )
find_package(LLVM CONFIG QUIET PATHS /usr/lib64/cmake/clang /usr/lib/cmake/llvm /usr/share/llvm/cmake )
if (Clang_FOUND AND LLVM_FOUND AND NOT LLVMCONFIG )
set(CMAKE_MODULE_PATH "${LLVM_CMAKE_DIR};${CMAKE_MODULE_PATH}")
set(CMAKE_MODULE_PATH "${LLVM_CMAKE_DIR};${CMAKE_MODULE_PATH}")
MESSAGE(STATUS "binder: cmake configurations llvm and clang were found. LLVM_CMAKE_DIR=${LLVM_CMAKE_DIR}")
include(AddLLVM)
include(LLVMConfig)
Expand Down Expand Up @@ -113,6 +111,12 @@ MESSAGE(STATUS "binder: LibClang_INCLUDE_DIR, the location of headers is ${LibC
MESSAGE(STATUS "binder: LLVM_VERSION_MAJOR=${LLVM_VERSION_MAJOR}")
MESSAGE(STATUS "binder: LLVM_VERSION_MINOR=${LLVM_VERSION_MINOR}")
MESSAGE(STATUS "binder: LLVM_VERSION_PATCH=${LLVM_VERSION_PATCH}")
if( ${LLVM_VERSION_MAJOR} GREATER_EQUAL 16 )
# LLVM 16 introduces the use of C++ features from C++17
set(CMAKE_CXX_STANDARD 17)
else()
set(CMAKE_CXX_STANDARD 14)
endif()
macro(add_clang_executable name)
add_executable( ${name} ${ARGN} )
# set_target_properties(${name} PROPERTIES FOLDER "Clang executables")
Expand All @@ -126,7 +130,7 @@ endmacro(add_clang_executable)
include_directories(source)
add_subdirectory(source)
if (BINDER_ENABLE_TEST)
if(${CMAKE_VERSION} VERSION_LESS "3.0.0")
if(${CMAKE_VERSION} VERSION_LESS "3.0.0")
message("You are running cmake version ${CMAKE_VERSION}.")
message("The testing suite will be disabled as it requires cmake 3.0.0 or higher.")
else()
Expand Down
2 changes: 1 addition & 1 deletion build-and-run-tests.py
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,7 @@ def main(args):

source_path = os.path.abspath('.')

if not Options.binder: Options.binder = build.install_llvm_tool('binder', source_path+'/source', source_path + '/build', Options.binder_debug, jobs=Options.jobs, gcc_install_prefix=Options.gcc_install_prefix)
if not Options.binder: Options.binder = build.install_llvm_tool('binder', source_path+'/source', source_path + '/build', Options.binder_debug, jobs=Options.jobs, gcc_install_prefix=Options.gcc_install_prefix, compiler=Options.compiler)

if not Options.pybind11: Options.pybind11 = build.install_pybind11(source_path + '/build')

Expand Down
10 changes: 5 additions & 5 deletions build.py
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@

_python_version_ = '{}.{}'.format(sys.version_info.major, sys.version_info.minor) # should be formatted: 2.7, 3.5, 3.6, ...

_pybind11_version_ = '32c4d7e17f267e10e71138a78d559b1eef17c909'
_pybind11_version_ = 'aa304c9c7d725ffb9d10af08a3b34cb372307020'


def execute(message, command_line, return_='status', until_successes=False, terminate_on_failure=True, silent=False, silence_output=False):
Expand Down Expand Up @@ -81,10 +81,10 @@ def get_compiler_family():
return 'unknown'


def get_cmake_compiler_options():
def get_cmake_compiler_options(compiler):
''' Get cmake compiler flags from Options.compiler '''
if Platform == "linux" and Options.compiler == 'clang': return ' -DCMAKE_C_COMPILER=`which clang` -DCMAKE_CXX_COMPILER=`which clang++`'
if Platform == "linux" and Options.compiler == 'gcc': return ' -DCMAKE_C_COMPILER=`which gcc` -DCMAKE_CXX_COMPILER=`which g++`'
if Platform == "linux" and compiler == 'clang': return ' -DCMAKE_C_COMPILER=`which clang` -DCMAKE_CXX_COMPILER=`which clang++`'
if Platform == "linux" and compiler == 'gcc': return ' -DCMAKE_C_COMPILER=`which gcc` -DCMAKE_CXX_COMPILER=`which g++`'

return ''

Expand Down Expand Up @@ -175,7 +175,7 @@ def install_llvm_tool(name, source_location, prefix_root, debug, compiler, jobs,
with open(cmake_lists, 'w') as f: f.write(tool_build_line + '\n')

config = '-DCMAKE_BUILD_TYPE={build_type}'.format(build_type='Debug' if debug else 'Release')
config += get_cmake_compiler_options()
config += get_cmake_compiler_options(compiler)

if not os.path.isdir(build_dir): os.makedirs(build_dir)
execute(
Expand Down
24 changes: 20 additions & 4 deletions documentation/config.rst
Original file line number Diff line number Diff line change
Expand Up @@ -69,6 +69,15 @@ Config file directives:
+namespace utility
* ``enum``, specify if particular enum should be bound. Purpose of this directive is to allow developer to cherry-pick
particular enum from otherwise binded/skipped namespaces and mark it for binding/skipping.

.. code-block:: bash
-enum utility::pointer::State
+enum protocols::CDR_Type
* ``class``, specify if particular class/struct should be bound. Purpose of this directive is to allow developer to cherry-pick
particular class from otherwise binded/skipped namespaces and mark it for binding/skipping.
Expand All @@ -78,6 +87,11 @@ Config file directives:
-class utility::pointer::ReferenceCount
-class std::__weak_ptr
* ``field``, specify if a particular field should be bound.

.. code-block:: bash
-field MyClass::some_field
* ``python_builtin``, specify if particular class/struct should be considered a python builtin and assume existing bindings for it already exist.
Expand Down Expand Up @@ -148,7 +162,7 @@ Config file directives:

* ``+add_on_binder``, similar to ``binder``: specify custom binding function for class/struct that will be called `after` Binder
generated code bound it. This allow developer to create extra bindings for particular type (bind special Python methods,
operators, etc.)
operators, etc.) The expected type signature of specified function should be `void f(pybind11::class_<T, std::shared_ptr<T> > &)`

.. code-block:: bash
Expand All @@ -160,7 +174,7 @@ Config file directives:
* ``+binder_for_namespace``, similar to ``binder``: specify custom binding function for namespace. Call to specified function will be generated
_instead_ of generating bindings for namaspace.
_instead_ of generating bindings for namaspace. Where expected type signature of specified function should be `void f(pybind11::module &)`

.. code-block:: bash
Expand Down Expand Up @@ -209,7 +223,7 @@ Config file directives:
+default_member_rvalue_reference_return_value_policy pybind11::return_value_policy::move
+default_call_guard pybind11::gil_scoped_release
* ``+custom_shared``: specify a custom shared pointer class that Binder should use instead of std::shared_ptr.
* ``+custom_shared``: specify a custom shared pointer class that Binder should use instead of ``std::shared_ptr``.

* ``module_local_namespace``: use to add (or remove) the extra argument module_local to the pybind11 classes and enum of a namespace. This option can be used for all the namaspaces of a given project using `+module_local_namespace @all_namespaces`.

Expand All @@ -218,10 +232,12 @@ Config file directives:
+module_local_namespace @all_namespaces
-module_local_namespace std
* ``trampoline_member_function_binder``: use to specify a custom trampoline member function defined by the user in a given header file.
* ``trampoline_member_function_binder``: use to specify a custom trampoline member function defined by the user in a given header file

.. code-block:: bash
+include_for_class aaa::A <T81.custom_trampoline_with_args.include>
+trampoline_member_function_binder aaa::A::foo myFoo
* ``+prefix_for_static_member_functions``: specify name prefix to use for static member functions, could be useful as workaround Pybind11 limitation restricting having both virtual and static member functions having the same name
44 changes: 38 additions & 6 deletions source/class.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -549,6 +549,21 @@ void ClassBinder::add_relevant_includes(IncludeSet &includes) const
includes.add_include("<sstream> // __str__");
}

string generate_opaque_declaration_if_needed(string const & qualified_name, string const & qualified_name_without_template)
{
// pybind11 container lists https://pybind11.readthedocs.io/en/stable/advanced/cast/stl.html
static vector<string> stl_containers {"std::vector", "std::deque", "std::list", "std::array", "std::valarray", "std::set", "std::unordered_set", "std::map", "std::unordered_map"};

if( begins_with(qualified_name_without_template, "std::") ) {
auto it = std::find(stl_containers.begin(), stl_containers.end(), qualified_name_without_template);
if( it != stl_containers.end() ) {
return "PYBIND11_MAKE_OPAQUE(" + qualified_name + ");\n";
}
}

return "";
}

string binding_public_data_members(CXXRecordDecl const *C)
{
string c;
Expand All @@ -560,6 +575,10 @@ string binding_public_data_members(CXXRecordDecl const *C)
for( auto s = u->shadow_begin(); s != u->shadow_end(); ++s ) {
if( UsingShadowDecl *us = dyn_cast<UsingShadowDecl>(*s) ) {
if( FieldDecl *f = dyn_cast<FieldDecl>(us->getTargetDecl()) ) {
auto config = Config::get();
if ( config.is_field_skipping_requested(f->getQualifiedNameAsString())) {
continue;
}
if( is_bindable(f) ) c += "\tcl" + bind_data_member(f, class_qualified_name(C)) + ";\n";
}
}
Expand All @@ -571,6 +590,10 @@ string binding_public_data_members(CXXRecordDecl const *C)
for( auto d = C->decls_begin(); d != C->decls_end(); ++d ) {
if( FieldDecl *f = dyn_cast<FieldDecl>(*d) ) {
//outs() << "Class: " << class_qualified_name(C); f->dump(); outs() << "\n";
auto config = Config::get();
if ( config.is_field_skipping_requested(f->getQualifiedNameAsString()) ) {
continue;
}
if( f->getAccess() == AS_public and is_bindable(f) ) c += "\tcl" + bind_data_member(f, class_qualified_name(C)) + ";\n";
}
}
Expand All @@ -581,7 +604,7 @@ string binding_public_data_members(CXXRecordDecl const *C)
inline string callback_structure_name(CXXRecordDecl const *C)
{
string ns = replace_(namespace_from_named_decl(C), "::", "_");
return "PyCallBack_" + (ns.empty() ? "" : ns + '_') + python_class_name(C);
return mangle_type_name( "PyCallBack_" + (ns.empty() ? "" : ns + '_') + python_class_name(C), false );
}


Expand Down Expand Up @@ -693,7 +716,7 @@ string bind_member_functions_for_call_back(CXXRecordDecl const *C, string const
// (*m)->dump();
// }

string return_type = standard_name(m->getReturnType().getCanonicalType().getAsString());
string return_type = standard_name(m->getReturnType());
fix_boolean_types(return_type);

// check if we need to fix return class to be 'derived-class &' or 'derived-class *'
Expand Down Expand Up @@ -1110,9 +1133,16 @@ std::string ClassBinder::bind_repr(Context &context, Config const &config)
if( config.is_function_skipping_requested(qualified_name + "::__str__") or config.is_function_skipping_requested( standard_name(C->getQualifiedNameAsString() + "::__str__" ) ) ) return c;

if( FunctionDecl const *F = context.global_insertion_operator(C) ) {
// outs() << "Found insertion operator for: " << class_qualified_name(C) << "\n";
//outs() << "Found insertion operator for: " << class_qualified_name(C) << "\n";
//outs() << "insertion operator: " << F->getNameInfo().getAsString() << " qn: " << F->getQualifiedNameAsString() << " dn:" << F->getDeclName() << "\n";

string maybe_using_decl;

c += "\n\tcl.def(\"__str__\", []({} const &o) -> std::string {{ std::ostringstream s; {}(s, o); return s.str(); }} );\n"_format(qualified_name, F->getQualifiedNameAsString());
string ns = namespace_from_named_decl(F);
if(ns.size()) maybe_using_decl = " using namespace {};"_format(ns);

//c += "\n\tcl.def(\"__str__\", []({} const &o) -> std::string {{ std::ostringstream s; {}(s, o); return s.str(); }} );\n"_format(qualified_name, F->getQualifiedNameAsString());
c += "\n\tcl.def(\"__str__\", []({} const &o) -> std::string {{ std::ostringstream s;{} s << o; return s.str(); }} );\n"_format(qualified_name, maybe_using_decl);

prefix_includes_.push_back(F);
}
Expand Down Expand Up @@ -1166,7 +1196,11 @@ void ClassBinder::bind(Context &context)
{
if( is_binded() ) return;

string const qualified_name = class_qualified_name(C);
string const qualified_name_without_template = standard_name(C->getQualifiedNameAsString());

//prefix_code_ += generate_opaque_declaration_if_needed(qualified_name, qualified_name_without_template);

std::map<string, string> const &external_binders = Config::get().binders();
if( external_binders.count(qualified_name_without_template) ) {
bind_with(external_binders.at(qualified_name_without_template), context);
Expand All @@ -1181,8 +1215,6 @@ void ClassBinder::bind(Context &context)

if( trampoline ) generate_prefix_code();

string const qualified_name{class_qualified_name(C)};

bool named_class = not C->isAnonymousStructOrUnion();

// if( named_class and (qualified_name.rfind(')') != std::string::npos) ) named_class = false; // check for anonymous structs and types in anonymous namespaces
Expand Down
4 changes: 4 additions & 0 deletions source/class.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,10 @@ std::string template_specialization(clang::CXXRecordDecl const *C);
std::string class_name(clang::CXXRecordDecl const *C);


// generate string represetiong class name that could be used in python
std::string python_class_name(clang::CXXRecordDecl const *C);


// generate qualified class name that could be used in bindings code including template specialization if any
std::string class_qualified_name(clang::CXXRecordDecl const *C);

Expand Down
51 changes: 47 additions & 4 deletions source/config.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,8 @@ void Config::read(string const &file_name)
string const _namespace_{"namespace"};
string const _function_{"function"};
string const _class_{"class"};
string const _field_{"field"};
string const _enum_{"enum"};

string const _python_builtin_{"python_builtin"};

Expand Down Expand Up @@ -89,6 +91,8 @@ void Config::read(string const &file_name)

string const _default_call_guard_{"default_call_guard"};

string const _prefix_for_static_member_functions_{"prefix_for_static_member_functions"};

std::ifstream f(file_name);

if( not f.good() ) { throw std::runtime_error("can not open file " + file_name + " for reading..."); }
Expand Down Expand Up @@ -126,6 +130,11 @@ void Config::read(string const &file_name)
if( bind ) classes_to_bind.push_back(name_without_spaces);
else classes_to_skip.push_back(name_without_spaces);
}
else if( token == _enum_ ) {

if( bind ) enums_to_bind.push_back(name_without_spaces);
else enums_to_skip.push_back(name_without_spaces);
}
else if( token == _python_builtin_ ) {

if (bind) python_builtins.insert(name_without_spaces);
Expand Down Expand Up @@ -178,28 +187,33 @@ void Config::read(string const &file_name)

if( bind ) {
auto binder_function = split_in_two(name, "Invalid line for binder specification! Must be: name_of_type + <space or tab> + name_of_binder. Got: " + line);
binders_[binder_function.first] = binder_function.second;
binders_[binder_function.first] = trim(binder_function.second);
}
}
else if( token == _add_on_binder_ ) {

if( bind ) {
auto binder_function = split_in_two(name, "Invalid line for add_on_binder specification! Must be: name_of_type + <space or tab> + name_of_binder. Got: " + line);
add_on_binders_[binder_function.first] = binder_function.second;
add_on_binders_[binder_function.first] = trim(binder_function.second);
}
}
else if( token == _binder_for_namespace_ ) {

if( bind ) {
auto binder_function = split_in_two(name, "Invalid line for binder_for_namespace specification! Must be: name_of_type + <space or tab> + name_of_binder. Got: " + line);
binder_for_namespaces_[binder_function.first] = binder_function.second;
binder_for_namespaces_[binder_function.first] = trim(binder_function.second);
}
}
else if( token == _add_on_binder_for_namespace_ ) {

if( bind ) {
auto binder_function = split_in_two(name, "Invalid line for add_on_binder_for_namespace specification! Must be: name_of_type + <space or tab> + name_of_binder. Got: " + line);
add_on_binder_for_namespaces_[binder_function.first] = binder_function.second;
add_on_binder_for_namespaces_[binder_function.first] = trim(binder_function.second);
}
} else if ( token == _field_ ) {

if (!bind) {
fields_to_skip.push_back(name_without_spaces);
}
}
else if( token == _custom_shared_ ) holder_type_ = name_without_spaces;
Expand All @@ -220,6 +234,8 @@ void Config::read(string const &file_name)
else if( token == _default_member_rvalue_reference_return_value_policy_ ) default_member_rvalue_reference_return_value_policy_ = name_without_spaces;
else if( token == _default_call_guard_ ) default_call_guard_ = name_without_spaces;

else if( token == _prefix_for_static_member_functions_ ) prefix_for_static_member_functions_ = name_without_spaces;

else if( token == _trampoline_member_function_binder_ ) {
if( bind ) {
auto member_function_name_and_function_name = split_in_two(name, "Invalid line for trampoline_member_function_binder specification! Must be: qualified_class_name::member_funtion_name + <space or tab> + name_of_function. Got: " + line);
Expand Down Expand Up @@ -363,6 +379,33 @@ bool Config::is_class_skipping_requested(string const &class__) const
}


bool Config::is_field_skipping_requested(string const &field_) const
{
return std::find(fields_to_skip.begin(), fields_to_skip.end(), field_) != fields_to_skip.end();
}


bool Config::is_enum_binding_requested(string const &enum_) const
{
auto bind = std::find(enums_to_bind.begin(), enums_to_bind.end(), enum_);

if( bind != enums_to_bind.end() ) return true;

return false;
}


bool Config::is_enum_skipping_requested(string const &enum_) const
{
auto bind = std::find(enums_to_skip.begin(), enums_to_skip.end(), enum_);

if( bind != enums_to_skip.end() ) return true;

return false;
}



string Config::is_custom_trampoline_function_requested(string const &function_) const
{
auto it = custom_trampoline_functions_.find(function_);
Expand Down
Loading

0 comments on commit 62c47f7

Please sign in to comment.