From 79370a43a595f54a07ddb0fd29d1e516c2d2c431 Mon Sep 17 00:00:00 2001 From: Wenzel Jakob Date: Sun, 17 Jan 2016 22:36:36 +0100 Subject: [PATCH] Much more efficient generation of function signatures, updated docs This modification taps into some newer C++14 features (if present) to generate function signatures considerably more efficiently at compile time rather than at run time. With this change, pybind11 binaries are now *2.1 times* smaller compared to the Boost.Python baseline in the benchmark. Compilation times get a nice improvement as well. Visual Studio 2015 unfortunately doesn't implement 'constexpr' well enough yet to support this change and uses a runtime fallback. --- CMakeLists.txt | 21 ++- README.md | 17 ++- docs/benchmark.py | 2 +- docs/benchmark.rst | 43 +++--- docs/cmake.rst | 12 +- docs/intro.rst | 17 ++- docs/pybind11_vs_boost_python1.svg | Bin 89335 -> 87708 bytes docs/pybind11_vs_boost_python2.svg | Bin 85859 -> 85853 bytes example/example11.cpp | 1 + example/example11.py | 19 ++- example/example11.ref | 26 ++-- include/pybind11/cast.h | 138 +++---------------- include/pybind11/common.h | 12 +- include/pybind11/complex.h | 2 +- include/pybind11/descr.h | 161 ++++++++++++++++++++++ include/pybind11/functional.h | 7 +- include/pybind11/pybind11.h | 207 +++++++++++++++++++---------- include/pybind11/stl.h | 24 ++-- include/pybind11/typeid.h | 10 +- 19 files changed, 451 insertions(+), 268 deletions(-) create mode 100644 include/pybind11/descr.h diff --git a/CMakeLists.txt b/CMakeLists.txt index 7be329726..29c7a9613 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -37,8 +37,16 @@ endif() find_package(PythonInterp ${PYTHONLIBS_VERSION_STRING} EXACT REQUIRED) if (CMAKE_CXX_COMPILER_ID MATCHES "Clang" OR CMAKE_CXX_COMPILER_ID MATCHES "GNU") - # Enable C++11 mode on C++ / Clang - set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++11") + CHECK_CXX_COMPILER_FLAG("-std=c++14" HAS_CPP14_FLAG) + CHECK_CXX_COMPILER_FLAG("-std=c++11" HAS_CPP11_FLAG) + + if (HAS_CPP14_FLAG) + set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++14") + elseif (HAS_CPP11_FLAG) + set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++11") + else() + message(FATAL_ERROR "Unsupported compiler -- pybind11 requires C++11 support!") + endif() # Enable link time optimization and set the default symbol # visibility to hidden (very important to obtain small binaries) @@ -74,14 +82,15 @@ include_directories(include) set(PYBIND11_HEADERS include/pybind11/cast.h include/pybind11/common.h + include/pybind11/complex.h + include/pybind11/descr.h + include/pybind11/functional.h + include/pybind11/numpy.h include/pybind11/operators.h include/pybind11/pybind11.h include/pybind11/pytypes.h - include/pybind11/typeid.h - include/pybind11/numpy.h - include/pybind11/complex.h include/pybind11/stl.h - include/pybind11/functional.h + include/pybind11/typeid.h ) # Create the binding library diff --git a/README.md b/README.md index f84154c29..96560e46e 100644 --- a/README.md +++ b/README.md @@ -23,12 +23,12 @@ become an excessively large and unnecessary dependency. Think of this library as a tiny self-contained version of Boost.Python with everything stripped away that isn't relevant for binding generation. The core -header files only require ~2K lines of code and depend on Python (2.7 or 3.x) +header files only require ~3K lines of code and depend on Python (2.7 or 3.x) and the C++ standard library. This compact implementation was possible thanks -to some of the new C++11 language features (tuples, lambda functions and -variadic templates). Since its creation, this library has grown beyond -Boost.Python in many ways, leading to dramatically simpler binding code in many -common situations. +to some of the new C++11 language features (specifically: tuples, lambda +functions and variadic templates). Since its creation, this library has grown +beyond Boost.Python in many ways, leading to dramatically simpler binding code +in many common situations. Tutorial and reference documentation is provided at [http://pybind11.readthedocs.org/en/latest](http://pybind11.readthedocs.org/en/latest). @@ -71,6 +71,13 @@ In addition to the core functionality, pybind11 provides some extra goodies: - Everything is contained in just a few header files; there is no need to link against any additional libraries. +- Binaries are generally smaller by a factor of 2 or more compared to + equivalent bindings generated by Boost.Python. + +- When supported by the compiler, two new C++14 features (relaxed constexpr, + return value deduction) such as are used to do additional work at compile + time, leading to smaller binaries. + ### License pybind11 is provided under a BSD-style license that can be found in the diff --git a/docs/benchmark.py b/docs/benchmark.py index efa82847b..6f02e92ff 100644 --- a/docs/benchmark.py +++ b/docs/benchmark.py @@ -81,7 +81,7 @@ for codegen in [generate_dummy_code_pybind11, generate_dummy_code_boost]: f.write(codegen(nclasses)) n1 = dt.datetime.now() os.system("g++ -Os -shared -rdynamic -undefined dynamic_lookup " - "-fvisibility=hidden -std=c++11 test.cpp -I include " + "-fvisibility=hidden -std=c++14 test.cpp -I include " "-I /System/Library/Frameworks/Python.framework/Headers -o test.so") n2 = dt.datetime.now() elapsed = (n2 - n1).total_seconds() diff --git a/docs/benchmark.rst b/docs/benchmark.rst index c06bfb395..c31e2902d 100644 --- a/docs/benchmark.rst +++ b/docs/benchmark.rst @@ -4,9 +4,12 @@ Benchmark The following is the result of a synthetic benchmark comparing both compilation time and module size of pybind11 against Boost.Python. -A python script (see the ``docs/benchmark.py`` file) was used to generate a -set of dummy classes whose count increases for each successive benchmark -(between 1 and 512 classes in powers of two). Each class has four methods with +Setup +----- + +A python script (see the ``docs/benchmark.py`` file) was used to generate a set +of files with dummy classes whose count increases for each successive benchmark +(between 1 and 2048 classes in powers of two). Each class has four methods with a randomly generated signature with a return value and four arguments. (There was no particular reason for this setup other than the desire to generate many unique function signatures whose count could be controlled in a simple way.) @@ -43,28 +46,38 @@ compilation was done with .. code-block:: bash - Apple LLVM version 7.0.0 (clang-700.0.72) + Apple LLVM version 7.0.2 (clang-700.1.81) and the following compilation flags .. code-block:: bash - g++ -Os -shared -rdynamic -undefined dynamic_lookup -fvisibility=hidden -std=c++11 + g++ -Os -shared -rdynamic -undefined dynamic_lookup -fvisibility=hidden -std=c++14 + +Compilation time +---------------- The following log-log plot shows how the compilation time grows for an -increasing number of class and function declarations. pybind11 includes fewer -headers, which initially leads to shorter compilation times, but the -performance is ultimately very similar (pybind11 is 1 second faster for the -largest file, which is less than 1% of the total compilation time). +increasing number of class and function declarations. pybind11 includes many +fewer headers, which initially leads to shorter compilation times, but the +performance is ultimately fairly similar (pybind11 is 19.8 seconds faster for +the largest largest file with 2048 classes and a total of 8192 methods -- a +modest **1.2x** speedup relative to Boost.Python, which required 116.35 +seconds). .. image:: pybind11_vs_boost_python1.svg -Differences between the two libraries become more pronounced when considering -the file size of the generated Python plugin. Note that the plot below does not -include the size of the Boost.Python shared library, hence Boost actually has a -slight advantage. +Module size +----------- + +Differences between the two libraries become much more pronounced when +considering the file size of the generated Python plugin: for the largest file, +the binary generated by Boost.Python required 16.8 MiB, which was **2.17 +times** / **9.1 megabytes** larger than the output generated by pybind11. For +very small inputs, Boost.Python has an edge in the plot below -- however, note +that it stores many definitions in an external library, whose size was not +included here, hence the comparison is slightly shifted in Boost.Python's +favor. .. image:: pybind11_vs_boost_python2.svg -Despite this, the libraries procuced by Boost.Python for more than a few -functions are consistently larger by a factor of 1.75. diff --git a/docs/cmake.rst b/docs/cmake.rst index 348c54a64..c44366f22 100644 --- a/docs/cmake.rst +++ b/docs/cmake.rst @@ -39,8 +39,16 @@ and that the pybind11 repository is located in a subdirectory named :file:`pybin # find_package(PythonInterp ${PYTHONLIBS_VERSION_STRING} EXACT REQUIRED) if (CMAKE_CXX_COMPILER_ID MATCHES "Clang" OR CMAKE_CXX_COMPILER_ID MATCHES "GNU") - # Enable C++11 mode on C++ / Clang - set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++11") + CHECK_CXX_COMPILER_FLAG("-std=c++14" HAS_CPP14_FLAG) + CHECK_CXX_COMPILER_FLAG("-std=c++11" HAS_CPP11_FLAG) + + if (HAS_CPP14_FLAG) + set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++14") + elseif (HAS_CPP11_FLAG) + set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++11") + else() + message(FATAL_ERROR "Unsupported compiler -- at least C++11 support is needed!") + endif() # Enable link time optimization and set the default symbol # visibility to hidden (very important to obtain small binaries) diff --git a/docs/intro.rst b/docs/intro.rst index 730d19dfa..291469b1b 100644 --- a/docs/intro.rst +++ b/docs/intro.rst @@ -18,12 +18,12 @@ become an excessively large and unnecessary dependency. Think of this library as a tiny self-contained version of Boost.Python with everything stripped away that isn't relevant for binding generation. The core -header files only require ~2K lines of code and depend on Python (2.7 or 3.x) +header files only require ~3K lines of code and depend on Python (2.7 or 3.x) and the C++ standard library. This compact implementation was possible thanks -to some of the new C++11 language features (tuples, lambda functions and -variadic templates). Since its creation, this library has grown beyond -Boost.Python in many ways, leading to dramatically simpler binding code in many -common situations. +to some of the new C++11 language features (specifically: tuples, lambda +functions and variadic templates). Since its creation, this library has grown +beyond Boost.Python in many ways, leading to dramatically simpler binding code +in many common situations. Core features ************* @@ -64,3 +64,10 @@ In addition to the core functionality, pybind11 provides some extra goodies: - Everything is contained in just a few header files; there is no need to link against any additional libraries. + +- Binaries are generally smaller by a factor of 2 or more compared to + equivalent bindings generated by Boost.Python. + +- When supported by the compiler, two new C++14 features (relaxed constexpr, + return value deduction) such as are used to do additional work at compile + time, leading to smaller binaries. diff --git a/docs/pybind11_vs_boost_python1.svg b/docs/pybind11_vs_boost_python1.svg index 8ffacf1ab670ebd22ed6ffd45e420f939c41e78a..5bf950e6fdc81676d9a9774926a623b4f6a2e2a8 100644 GIT binary patch delta 17739 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 diff --git a/docs/pybind11_vs_boost_python2.svg b/docs/pybind11_vs_boost_python2.svg index dbaf9aa08864163bfb00d13111baca0955b2c9fb..5ed6530ca112cbe643d5dd6d6fde385c4edea6b5 100644 GIT binary patch delta 6140 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 delta 6276 zcmZu$U5s8u8O~{UOMht1w%>kR=|MKMQJ5SD@x$yKMo!_5pWAZ9~Fobk@W!B$$`q7!OX$q~vB-N-%=Ky=x z&yLvs=hwz%ZR(Q0F4{@y0QdE?&%C`*b){-s_EAm3xPS4U>=AqN<=yMI{NjOyrpoWm zjhPhWs?1fVC#uz^t8!CK8Uvu5R7I&P2Ox;nfy$nFd4AfdNojIZPFhu0MUBbDq$|4` zUJcobBe+m(SHPxV+sd1&x=CBM3PuSCAMefqJ1B-ywdChGIT8_j)Pt1PV9YVHEn^@1 z-Dp}*LQ05G1XEM$GUSE{a(TrOR*B>zZ1lu*4b(w&-c-eY2uLoM0>_5uOC>6+UKwST zJ$>a7`>R)OoEAu_Z8;Ftbj)bmIHj7F*#y8!HBF+it=WwN3`SeStf;1Dk6y?^pw}$ex8K92alfU0P)x<4Y7yCnfGPkHo>Rl-7(9jeR zc8cl`SC;@cMz$>~K$*4`_6bQ?xEBl-Cjufgu?c-OzXW!h~ zvrXfC|1WI{9wR)LDly1 z#nF`4M^O}+^6ifn_}w5wNV`FXnoysSk^slX=Foq_AwMOEFqNFgUG5{~N}yHsqBxLov?vY}^Bd#UW&^P_{1CNL{iD(Oi2u6^qAjZ;MeqIOxp zA~uH-%@Bciv4s&3^y^|5qt%fQz$7O%How8U*u4PeX@phiOAyl*f0&sjyhIxeyaiim zcY)x6Pr&dbVyogtQ6dG2x=aT^i1JGa#+g$^>~@5(utn^66m|?y0ViR7B6M+9MeKN# zE{1mQ%J`T>BUQXQaSFtqxN_jO6oZr{9z#+r``#}t&G@YO%?s1{70nF2gT)NY0N(>|%HOSuBRy=5j zKHa$}V#Bw=lgL1TE=@W1Ax>*V;%&uCH~}F$x(Ra66R`m%4Sfof0hkH=QJ!f;c}6l4 zTt>=H&}Gxvirfx;dJRNuNvZf#LdsXt;W*J~Qz4+5&VMwX zV|3CdlF&%34r}8+B!HbnUMa#9jN?^=j2mtsjPk;T`s^F?D-ukUgrzNS4P2PxCSgu1 zoBe4t77+?P+~qTWY0nftk`WYYIe4z(#X_SyLYgYxDF_Hos(9AZ;)0a|4E{9wN>B<1 ze5eS3c^Y9A+7Xm;wU=;><`tx3TTtCgHSmdU2u~um%CIMqfdE~aa_mEVphhI#R{TLr z0Q-zr4~jU&=58J#1*$!K#mZ5hjh+5>{wo3$6#1osO6ej{owR^Lr{)JCw_t{JDk7kRmQ;Hs7#`e8`{M0`6ubD-D#1S6SQ;zqrV;9u}I)167 zm;JL2vX5Q=ntkho{j&{TJ~SI&Tdv}Z&;6ur2%k}V_MhM0ML!CaKE-ad;VYn%fkuSv&IztrIl>smd;(_O>4UUHiMhJhbfZ965C8U-3Gxp zxIaCYZP;% zyB7U9ly86aYnDhRvh&*5J3jiy=*kIGI1c>^Hx!327-6#a91rkJoZN9u zWpKW~^Nwr=wUA0-Fi+INQ9O{xZ9n9QVJ{Q!`+7Jj!cL|z?EepcS!EOe diff --git a/example/example11.cpp b/example/example11.cpp index 522485cb8..9599f940a 100644 --- a/example/example11.cpp +++ b/example/example11.cpp @@ -14,4 +14,5 @@ void kw_func(int x, int y) { std::cout << "kw_func(x=" << x << ", y=" << y << ") void init_ex11(py::module &m) { m.def("kw_func", &kw_func, py::arg("x"), py::arg("y")); m.def("kw_func2", &kw_func, py::arg("x") = 100, py::arg("y") = 200); + m.def("kw_func3", [](const char *) { }, py::arg("data") = std::string("Hello world!")); } diff --git a/example/example11.py b/example/example11.py index 7fc1b4816..7d0217a23 100755 --- a/example/example11.py +++ b/example/example11.py @@ -1,19 +1,19 @@ #!/usr/bin/env python from __future__ import print_function -import sys, pydoc +import sys +import pydoc + sys.path.append('.') -import example - -from example import kw_func -from example import kw_func2 +from example import kw_func, kw_func2, kw_func3 print(pydoc.render_doc(kw_func, "Help on %s")) print(pydoc.render_doc(kw_func2, "Help on %s")) +print(pydoc.render_doc(kw_func3, "Help on %s")) kw_func(5, 10) -kw_func(5, y = 10) -kw_func(y = 10, x = 5) +kw_func(5, y=10) +kw_func(y=10, x=5) kw_func2() @@ -24,3 +24,8 @@ kw_func2(y=10) kw_func2(5, 10) kw_func2(x=5, y=10) + +try: + kw_func2(x=5, y=10, z=12) +except Exception as e: + print("Caught expected exception: " + str(e)) diff --git a/example/example11.ref b/example/example11.ref index 728e804dc..b7719b132 100644 --- a/example/example11.ref +++ b/example/example11.ref @@ -1,3 +1,18 @@ +Help on built-in function kw_func + +kkww__ffuunncc(...) + Signature : (x : int, y : int) -> None + +Help on built-in function kw_func2 + +kkww__ffuunncc22(...) + Signature : (x : int = 100L, y : int = 200L) -> None + +Help on built-in function kw_func3 + +kkww__ffuunncc33(...) + Signature : (data : str = u'Hello world!') -> None + kw_func(x=5, y=10) kw_func(x=5, y=10) kw_func(x=5, y=10) @@ -7,13 +22,6 @@ kw_func(x=5, y=200) kw_func(x=100, y=10) kw_func(x=5, y=10) kw_func(x=5, y=10) -Help on built-in function kw_func - -kkww__ffuunncc(...) method of builtins.PyCapsule instance - Signature : (x : int, y : int) -> None - -Help on built-in function kw_func2 - -kkww__ffuunncc22(...) method of builtins.PyCapsule instance - Signature : (x : int = 100, y : int = 200) -> None +Caught expected exception: Incompatible function arguments. The following argument types are supported: + 1. (x : int = 100L, y : int = 200L) -> None diff --git a/include/pybind11/cast.h b/include/pybind11/cast.h index 0c7efc6d3..da0831179 100644 --- a/include/pybind11/cast.h +++ b/include/pybind11/cast.h @@ -12,8 +12,8 @@ #include "pytypes.h" #include "typeid.h" +#include "descr.h" #include -#include #include NAMESPACE_BEGIN(pybind11) @@ -25,80 +25,6 @@ NAMESPACE_BEGIN(detail) #define PYBIND11_AS_STRING PyString_AsString #endif -/** Linked list descriptor type for function signatures (produces smaller binaries - compared to a previous solution using std::string and operator +=) */ -class descr { -public: - struct entry { - const std::type_info *type = nullptr; - const char *str = nullptr; - entry *next = nullptr; - entry(const std::type_info *type) : type(type) { } - entry(const char *str) : str(str) { } - }; - - descr() { } - descr(descr &&d) : first(d.first), last(d.last) { d.first = d.last = nullptr; } - PYBIND11_NOINLINE descr(const char *str) { first = last = new entry { str }; } - PYBIND11_NOINLINE descr(const std::type_info &type) { first = last = new entry { &type }; } - - PYBIND11_NOINLINE void operator+(const char *str) { - entry *next = new entry { str }; - last->next = next; - last = next; - } - - PYBIND11_NOINLINE void operator+(const std::type_info *type) { - entry *next = new entry { type }; - last->next = next; - last = next; - } - - PYBIND11_NOINLINE void operator+=(descr &&other) { - last->next = other.first; - while (last->next) - last = last->next; - other.first = other.last = nullptr; - } - - PYBIND11_NOINLINE friend descr operator+(descr &&l, descr &&r) { - descr result(std::move(l)); - result += std::move(r); - return result; - } - - PYBIND11_NOINLINE std::string str() const { - std::string result; - auto const& registered_types = get_internals().registered_types; - for (entry *it = first; it != nullptr; it = it->next) { - if (it->type) { - auto it2 = registered_types.find(it->type); - if (it2 != registered_types.end()) { - result += it2->second.type->tp_name; - } else { - std::string tname(it->type->name()); - detail::clean_type_id(tname); - result += tname; - } - } else { - result += it->str; - } - } - return result; - } - - PYBIND11_NOINLINE ~descr() { - while (first) { - entry *tmp = first->next; - delete first; - first = tmp; - } - } - - entry *first = nullptr; - entry *last = nullptr; -}; - class type_caster_custom { public: PYBIND11_NOINLINE type_caster_custom(const std::type_info *type_info) { @@ -195,7 +121,7 @@ protected: /// Generic type caster for objects stored on the heap template class type_caster : public type_caster_custom { public: - static descr name() { return typeid(type); } + static PYBIND11_DESCR name() { return type_descr(_()); } type_caster() : type_caster_custom(&typeid(type)) { } @@ -224,7 +150,7 @@ protected: protected: \ type value; \ public: \ - static descr name() { return py_name; } \ + static PYBIND11_DESCR name() { return type_descr(py_name); } \ static PyObject *cast(const type *src, return_value_policy policy, PyObject *parent) { \ return cast(*src, policy, parent); \ } \ @@ -296,9 +222,10 @@ public: return cast(*src, policy, parent); } - static descr name() { - return std::is_floating_point::value ? "float" : "int"; - } + template ::value, int>::type = 0> + static PYBIND11_DESCR name() { return type_descr(_("int")); } + template ::value, int>::type = 0> + static PYBIND11_DESCR name() { return type_descr(_("float")); } operator T*() { return &value; } operator T&() { return value; } @@ -314,7 +241,7 @@ public: Py_INCREF(Py_None); return Py_None; } - PYBIND11_TYPE_CASTER(void_type, "None"); + PYBIND11_TYPE_CASTER(void_type, _("None")); }; template <> class type_caster : public type_caster { }; @@ -332,7 +259,7 @@ public: Py_INCREF(result); return result; } - PYBIND11_TYPE_CASTER(bool, "bool"); + PYBIND11_TYPE_CASTER(bool, _("bool")); }; template <> class type_caster { @@ -352,7 +279,7 @@ public: static PyObject *cast(const std::string &src, return_value_policy /* policy */, PyObject * /* parent */) { return PyUnicode_FromString(src.c_str()); } - PYBIND11_TYPE_CASTER(std::string, "str"); + PYBIND11_TYPE_CASTER(std::string, _("str")); }; template <> class type_caster { @@ -379,7 +306,7 @@ public: return PyUnicode_DecodeLatin1(str, 1, nullptr); } - static descr name() { return "str"; } + static PYBIND11_DESCR name() { return type_descr(_("str")); } operator char*() { return (char *) value.c_str(); } operator char() { if (value.length() > 0) return value[0]; else return '\0'; } @@ -411,13 +338,10 @@ public: return tuple; } - static descr name() { - class descr result("("); - result += std::move(type_caster::type>::name()); - result += ", "; - result += std::move(type_caster::type>::name()); - result += ")"; - return result; + static PYBIND11_DESCR name() { + return type_descr( + _("(") + type_caster::type>::name() + + _(", ") + type_caster::type>::name() + _(")")); } operator type() { @@ -441,31 +365,11 @@ public: return cast(src, policy, parent, typename make_index_sequence::type()); } - static descr name(const std::list &args = std::list()) { - std::array type_names {{ - type_caster::type>::name()... - }}; - auto it = args.begin(); - class descr result("("); - for (int i=0; iname; - result += " : "; - } - result += std::move(type_names[i]); - if (it != args.end()) { - if (it->descr) { - result += " = "; - result += it->descr; - } - ++it; - } - if (i+1 < size) - result += ", "; - ++it; - } - result += ")"; - return result; + static PYBIND11_DESCR name() { + return type_descr( + _("(") + + detail::concat(type_caster::type>::name()...) + + _(")")); } template typename std::enable_if::value, ReturnValue>::type call(Func &&f) { @@ -576,7 +480,7 @@ public: src.inc_ref(); return (PyObject *) src.ptr(); } - PYBIND11_TYPE_CASTER(type, typeid(type)); + PYBIND11_TYPE_CASTER(type, _()); }; NAMESPACE_END(detail) diff --git a/include/pybind11/common.h b/include/pybind11/common.h index 3ed473cdc..d5d10e822 100644 --- a/include/pybind11/common.h +++ b/include/pybind11/common.h @@ -167,16 +167,12 @@ struct overload_hash { /// Stores information about a keyword argument struct argument_entry { - char *name; ///< Argument name - char *descr; ///< Human-readable version of the argument value - PyObject *value; ///< Associated Python object + const char *name; ///< Argument name + const char *descr; ///< Human-readable version of the argument value + PyObject *value; ///< Associated Python object - argument_entry(char *name, char *descr, PyObject *value) + argument_entry(const char *name, const char *descr, PyObject *value) : name(name), descr(descr), value(value) { } - - ~argument_entry() { - free(name); free(descr); Py_XDECREF(value); - } }; /// Internal data struture used to track registered instances and types diff --git a/include/pybind11/complex.h b/include/pybind11/complex.h index ce3276c05..be0614c59 100644 --- a/include/pybind11/complex.h +++ b/include/pybind11/complex.h @@ -34,7 +34,7 @@ public: return PyComplex_FromDoubles((double) src.real(), (double) src.imag()); } - PYBIND11_TYPE_CASTER(std::complex, "complex"); + PYBIND11_TYPE_CASTER(std::complex, _("complex")); }; NAMESPACE_END(detail) NAMESPACE_END(pybind11) diff --git a/include/pybind11/descr.h b/include/pybind11/descr.h new file mode 100644 index 000000000..ed44b2626 --- /dev/null +++ b/include/pybind11/descr.h @@ -0,0 +1,161 @@ +/* + pybind11/descr.h: Helper type for concatenating type signatures + either at runtime (C++11) or compile time (C++14) + + Copyright (c) 2015 Wenzel Jakob + + All rights reserved. Use of this source code is governed by a + BSD-style license that can be found in the LICENSE file. +*/ + +#pragma once + +#include "common.h" + +NAMESPACE_BEGIN(pybind11) +NAMESPACE_BEGIN(detail) + + +#if defined(__clang__) +# if __has_feature(cxx_return_type_deduction) && __has_feature(cxx_relaxed_constexpr) +# define PYBIND11_CPP14 +# endif +#elif defined(__GNUG__) +# if __cpp_constexpr >= 201304 && __cpp_decltype_auto >= 201304 +# define PYBIND11_CPP14 +# endif +#endif + +#if defined(PYBIND11_CPP14) /* Concatenate type signatures at compile time using C++14 */ + +template class descr { + template friend class descr; +public: + constexpr descr(char const (&text) [Size1+1], const std::type_info * const (&types)[Size2+1]) + : descr(text, types, + typename make_index_sequence::type(), + typename make_index_sequence::type()) { } + + constexpr const char *text() const { return m_text; } + constexpr const std::type_info * const * types() const { return m_types; } + + template + constexpr descr operator+(const descr &other) const { + return concat(other, + typename make_index_sequence::type(), + typename make_index_sequence::type(), + typename make_index_sequence::type(), + typename make_index_sequence::type()); + } + +protected: + template + constexpr descr( + char const (&text) [Size1+1], + const std::type_info * const (&types) [Size2+1], + index_sequence, index_sequence) + : m_text{text[Indices1]..., '\0'}, + m_types{types[Indices2]..., nullptr } {} + + template + constexpr descr + concat(const descr &other, + index_sequence, index_sequence, + index_sequence, index_sequence) const { + return descr( + { m_text[Indices1]..., other.m_text[OtherIndices1]..., '\0' }, + { m_types[Indices2]..., other.m_types[OtherIndices2]..., nullptr } + ); + } + +protected: + char m_text[Size1 + 1]; + const std::type_info * m_types[Size2 + 1]; +}; + +template constexpr descr _(char const(&text)[Size]) { + return descr(text, { nullptr }); +} + +template constexpr descr<1, 1> _() { + return descr<1, 1>({ '%', '\0' }, { &typeid(Type), nullptr }); +} + +inline constexpr descr<0, 0> concat() { return _(""); } +template auto constexpr concat(descr descr) { return descr; } +template auto constexpr concat(descr descr, Args&&... args) { return descr + _(", ") + concat(args...); } +template auto constexpr type_descr(descr descr) { return _("{") + descr + _("}"); } + +#define PYBIND11_DESCR constexpr auto + +#else /* Simpler C++11 implementation based on run-time memory allocation and copying */ + +class descr { +public: + PYBIND11_NOINLINE descr(const char *text, const std::type_info * const * types) { + size_t nChars = len(text), nTypes = len(types); + m_text = new char[nChars]; + m_types = new const std::type_info *[nTypes]; + memcpy(m_text, text, nChars * sizeof(char)); + memcpy(m_types, types, nTypes * sizeof(const std::type_info *)); + } + + PYBIND11_NOINLINE descr friend operator+(descr &&d1, descr &&d2) { + descr r; + + size_t nChars1 = len(d1.m_text), nTypes1 = len(d1.m_types); + size_t nChars2 = len(d2.m_text), nTypes2 = len(d2.m_types); + + r.m_text = new char[nChars1 + nChars2 - 1]; + r.m_types = new const std::type_info *[nTypes1 + nTypes2 - 1]; + memcpy(r.m_text, d1.m_text, (nChars1-1) * sizeof(char)); + memcpy(r.m_text + nChars1 - 1, d2.m_text, nChars2 * sizeof(char)); + memcpy(r.m_types, d1.m_types, (nTypes1-1) * sizeof(std::type_info *)); + memcpy(r.m_types + nTypes1 - 1, d2.m_types, nTypes2 * sizeof(std::type_info *)); + + delete[] d1.m_text; delete[] d1.m_types; + delete[] d2.m_text; delete[] d2.m_types; + + return r; + } + + char *text() { return m_text; } + const std::type_info * * types() { return m_types; } + +protected: + PYBIND11_NOINLINE descr() { } + + template static size_t len(const T *ptr) { // return length including null termination + const T *it = ptr; + while (*it++ != (T) 0) + ; + return it - ptr; + } + + const std::type_info **m_types = nullptr; + char *m_text = nullptr; +}; + +/* The 'PYBIND11_NOINLINE inline' combinations below are intentional to get the desired linkage while producing as little object code as possible */ + +PYBIND11_NOINLINE inline descr _(const char *text) { + const std::type_info *types[1] = { nullptr }; + return descr(text, types); +} + +template PYBIND11_NOINLINE descr _() { + const std::type_info *types[2] = { &typeid(Type), nullptr }; + return descr("%", types); +} + +PYBIND11_NOINLINE inline descr concat() { return _(""); } +PYBIND11_NOINLINE inline descr concat(descr &&d) { return d; } +template PYBIND11_NOINLINE descr concat(descr &&d, Args&&... args) { return std::move(d) + _(", ") + concat(std::forward(args)...); } +PYBIND11_NOINLINE inline descr type_descr(descr&& d) { return _("{") + std::move(d) + _("}"); } + +#define PYBIND11_DESCR descr +#endif + +NAMESPACE_END(detail) +NAMESPACE_END(pybind11) diff --git a/include/pybind11/functional.h b/include/pybind11/functional.h index 34d1c6bc9..18056ce4a 100644 --- a/include/pybind11/functional.h +++ b/include/pybind11/functional.h @@ -39,11 +39,10 @@ public: return f.ptr(); } - - PYBIND11_TYPE_CASTER(type, detail::descr("function<") + - type_caster>::name() + detail::descr(" -> ") + + PYBIND11_TYPE_CASTER(type, _("function<") + + type_caster>::name() + _(" -> ") + type_caster::type>::name() + - detail::descr(">")); + _(">")); }; NAMESPACE_END(detail) diff --git a/include/pybind11/pybind11.h b/include/pybind11/pybind11.h index 34383bcab..0c3d75614 100644 --- a/include/pybind11/pybind11.h +++ b/include/pybind11/pybind11.h @@ -24,7 +24,6 @@ #endif #include "cast.h" -#include NAMESPACE_BEGIN(pybind11) @@ -39,10 +38,10 @@ struct arg { /// Annotation for keyword arguments with default values template struct arg_t : public arg { - arg_t(const char *name, const T &value, const char *value_str = nullptr) - : arg(name), value(value), value_str(value_str) {} + arg_t(const char *name, const T &value, const char *descr = nullptr) + : arg(name), value(value), descr(descr) { } T value; - const char *value_str; + const char *descr; }; template arg_t arg::operator=(const T &value) { return arg_t(name, value); } @@ -65,15 +64,17 @@ private: /// Linked list of function overloads struct function_entry { /// Function name and user-specified documentation string - const char *name = nullptr, *doc = nullptr; + char *name = nullptr, *doc = nullptr; /* why no C++ strings? They generate heavier code.. */ + /// Human-readable version of the function signature + char *signature = nullptr; /// List of registered keyword arguments - std::list args; - /// Pointer to lambda function which converts arguments and performs the call + std::vector args; + /// Pointer to lambda function which converts arguments and performs the actual call PyObject * (*impl) (function_entry *, PyObject *, PyObject *) = nullptr; /// Storage for the wrapped function pointer and captured data, if any void *data = nullptr; /// Pointer to custom destructor for 'data' (if needed) - void (*free) (void *ptr) = nullptr; + void (*free_data) (void *ptr) = nullptr; /// Return value policy associated with this function return_value_policy policy = return_value_policy::automatic; /// True if name == '__init__' @@ -86,12 +87,6 @@ private: PyObject *sibling = nullptr; /// Pointer to next overload function_entry *next = nullptr; - - ~function_entry() { - delete def; - if (free) - free(data); - } }; function_entry *m_entry; @@ -115,59 +110,56 @@ private: (void) unused; } - static void process_extra(const char *doc, function_entry *entry) { entry->doc = doc; } - static void process_extra(const pybind11::doc &d, function_entry *entry) { entry->doc = d.value; } - static void process_extra(const pybind11::name &n, function_entry *entry) { entry->name = n.value; } + static void process_extra(const char *doc, function_entry *entry) { entry->doc = (char *) doc; } + static void process_extra(const pybind11::doc &d, function_entry *entry) { entry->doc = (char *) d.value; } + static void process_extra(const pybind11::name &n, function_entry *entry) { entry->name = (char *) n.value; } static void process_extra(const pybind11::return_value_policy p, function_entry *entry) { entry->policy = p; } static void process_extra(const pybind11::sibling s, function_entry *entry) { entry->sibling = s.value; } static void process_extra(const pybind11::is_method &m, function_entry *entry) { entry->class_ = m.class_; } static void process_extra(const pybind11::arg &a, function_entry *entry) { if (entry->class_ && entry->args.empty()) - entry->args.emplace_back(strdup("self"), nullptr, nullptr); - entry->args.emplace_back(strdup(a.name), nullptr, nullptr); + entry->args.emplace_back("self", nullptr, nullptr); + entry->args.emplace_back(a.name, nullptr, nullptr); } template static void process_extra(const pybind11::arg_t &a, function_entry *entry) { if (entry->class_ && entry->args.empty()) - entry->args.emplace_back(strdup("self"), nullptr, nullptr); + entry->args.emplace_back("self", nullptr, nullptr); PyObject *obj = detail::type_caster::type>::cast( a.value, return_value_policy::automatic, nullptr); - entry->args.emplace_back( - strdup(a.name), - strdup(a.value_str != nullptr ? a.value_str : - (const char *) ((object) handle(obj).attr("__repr__")).call().str()), - obj - ); + if (obj == nullptr) + throw std::runtime_error("arg(): could not convert default keyword " + "argument into a Python object (type not " + "registered yet?)"); + + entry->args.emplace_back(a.name, a.descr, obj); } public: cpp_function() { } /// Vanilla function pointers - template - cpp_function(Return (*f)(Arg...), Extra&&... extra) { + template + cpp_function(Return (*f)(Args...), Extra&&... extra) { + using detail::descr; m_entry = new function_entry(); m_entry->data = (void *) f; - typedef arg_value_caster cast_in; + typedef arg_value_caster cast_in; typedef return_value_caster cast_out; m_entry->impl = [](function_entry *entry, PyObject *pyArgs, PyObject *parent) -> PyObject * { cast_in args; if (!args.load(pyArgs, true)) return (PyObject *) 1; /* Special return code: try next overload */ - return cast_out::cast(args.template call((Return (*)(Arg...)) entry->data), entry->policy, parent); + return cast_out::cast(args.template call((Return (*)(Args...)) entry->data), entry->policy, parent); }; process_extras(std::make_tuple(std::forward(extra)...), m_entry); - - detail::descr d = cast_in::name(m_entry->args); - d += " -> "; - d += std::move(cast_out::name()); - - initialize(d, sizeof...(Arg)); + PYBIND11_DESCR signature = cast_in::name() + detail::_(" -> ") + cast_out::name(); + initialize(signature.text(), signature.types(), sizeof...(Args)); } /// Delegating helper constructor to deal with lambda functions @@ -197,21 +189,21 @@ public: private: /// Functors, lambda functions, etc. - template - void initialize(Func &&f, Return (*)(Arg...), Extra&&... extra) { - struct capture { - typename std::remove_reference::type f; - }; + template + void initialize(Func &&f, Return (*)(Args...), Extra&&... extra) { + using detail::descr; + + struct capture { typename std::remove_reference::type f; }; m_entry = new function_entry(); m_entry->data = new capture { std::forward(f) }; if (!std::is_trivially_destructible::value) - m_entry->free = [](void *ptr) { delete (capture *) ptr; }; + m_entry->free_data = [](void *ptr) { delete (capture *) ptr; }; else - m_entry->free = operator delete; + m_entry->free_data = operator delete; - typedef arg_value_caster cast_in; + typedef arg_value_caster cast_in; typedef return_value_caster cast_out; m_entry->impl = [](function_entry *entry, PyObject *pyArgs, PyObject *parent) -> PyObject *{ @@ -222,12 +214,8 @@ private: }; process_extras(std::make_tuple(std::forward(extra)...), m_entry); - - detail::descr d = cast_in::name(m_entry->args); - d += " -> "; - d += std::move(cast_out::name()); - - initialize(d, sizeof...(Arg)); + PYBIND11_DESCR signature = cast_in::name() + detail::_(" -> ") + cast_out::name(); + initialize(signature.text(), signature.types(), sizeof...(Args)); } static PyObject *dispatcher(PyObject *self, PyObject *args, PyObject *kwargs) { @@ -250,17 +238,17 @@ private: PyTuple_SET_ITEM(args_, i, item); } int arg_ctr = 0; - for (auto const &it : it->args) { + for (auto const &it2 : it->args) { int index = arg_ctr++; if (PyTuple_GET_ITEM(args_, index)) continue; PyObject *value = nullptr; if (kwargs) - value = PyDict_GetItemString(kwargs, it.name); + value = PyDict_GetItemString(kwargs, it2.name); if (value) kwargs_consumed++; - else if (it.value) - value = it.value; + else if (it2.value) + value = it2.value; if (value) { Py_INCREF(value); PyTuple_SET_ITEM(args_, index, value); @@ -301,7 +289,7 @@ private: int ctr = 0; for (function_entry *it2 = overloads; it2 != nullptr; it2 = it2->next) { msg += " "+ std::to_string(++ctr) + ". "; - //msg += it2->signature; XXX + msg += it2->signature; msg += "\n"; } PyErr_SetString(PyExc_TypeError, msg.c_str()); @@ -309,7 +297,7 @@ private: } else if (result == nullptr) { std::string msg = "Unable to convert function return value to a " "Python type! The signature was\n\t"; - //msg += it->signature; + msg += it->signature; PyErr_SetString(PyExc_TypeError, msg.c_str()); return nullptr; } else { @@ -327,18 +315,88 @@ private: static void destruct(function_entry *entry) { while (entry) { function_entry *next = entry->next; + delete entry->def; + if (entry->free_data) + entry->free_data(entry->data); + std::free((char *) entry->name); + std::free((char *) entry->doc); + std::free((char *) entry->signature); + for (auto &arg: entry->args) { + std::free((char *) arg.name); + std::free((char *) arg.descr); + Py_XDECREF(arg.value); + } delete entry; entry = next; } } - void initialize(const detail::descr &, int args) { - if (m_entry->name == nullptr) - m_entry->name = ""; + void initialize(const char *text, const std::type_info * const * types, int args) { + /* Create copies of all referenced C-style strings */ + m_entry->name = strdup(m_entry->name ? m_entry->name : ""); + if (m_entry->doc) m_entry->doc = strdup(m_entry->doc); + for (auto &a: m_entry->args) { + if (a.name) + a.name = strdup(a.name); + if (a.descr) + a.descr = strdup(a.descr); + else if (a.value) + a.descr = strdup(((object) handle(a.value).attr("__repr__")).call().str()); + } + auto const ®istered_types = detail::get_internals().registered_types; + + /* Generate a proper function signature */ + std::string signature; + size_t type_depth = 0, char_index = 0, type_index = 0, arg_index = 0; + while (true) { + char c = text[char_index++]; + if (c == '\0') + break; + + if (c == '{') { + if (type_depth == 1 && arg_index < m_entry->args.size()) { + signature += m_entry->args[arg_index].name; + signature += " : "; + } + ++type_depth; + } else if (c == '}') { + --type_depth; + if (type_depth == 1 && arg_index < m_entry->args.size()) { + if (m_entry->args[arg_index].descr) { + signature += " = "; + signature += m_entry->args[arg_index].descr; + } + arg_index++; + } + } else if (c == '%') { + const std::type_info *t = types[type_index++]; + if (!t) + throw std::runtime_error("Internal error while generating type signature (1)"); + auto it = registered_types.find(t); + if (it != registered_types.end()) { + signature += it->second.type->tp_name; + } else { + std::string tname(t->name()); + detail::clean_type_id(tname); + signature += tname; + } + } else { + signature += c; + } + } + if (type_depth != 0 && types[type_index ] != nullptr) + throw std::runtime_error("Internal error while generating type signature (2)"); + + #if !defined(PYBIND11_CPP14) + delete[] types; + delete[] text; + #endif #if PY_MAJOR_VERSION < 3 - if (strcmp(m_entry->name, "__next__") == 0) - m_entry->name = "next"; + if (strcmp(m_entry->name, "__next__") == 0) { + free(m_entry->name); + m_entry->name = strdup("next"); + } #endif if (!m_entry->args.empty() && (int) m_entry->args.size() != args) @@ -348,7 +406,8 @@ private: " pybind11::arg entries were specified!"); m_entry->is_constructor = !strcmp(m_entry->name, "__init__"); - //m_entry->signature = descr.str(); // XXX + m_entry->signature = strdup(signature.c_str()); + m_entry->args.shrink_to_fit(); #if PY_MAJOR_VERSION < 3 if (m_entry->sibling && PyMethod_Check(m_entry->sibling)) @@ -360,10 +419,10 @@ private: capsule entry_capsule(PyCFunction_GetSelf(m_entry->sibling), true); s_entry = (function_entry *) entry_capsule; if (s_entry->class_ != m_entry->class_) - s_entry = nullptr; /* Method override */ + s_entry = nullptr; /* Overridden method, don't append to parent class overloads */ } - if (!s_entry) { + if (!s_entry) { /* No existing overload was found, create a function object */ m_entry->def = new PyMethodDef(); memset(m_entry->def, 0, sizeof(PyMethodDef)); m_entry->def->ml_name = m_entry->name; @@ -385,19 +444,24 @@ private: std::string signatures; int index = 0; function_entry *it = entry; - while (it) { /* Create pydoc entry */ + while (it) { /* Create pydoc entry including all function signatures and docstrings of the overload chain */ if (s_entry) signatures += std::to_string(++index) + ". "; - //signatures += "Signature : " + std::string(it->signature) + "\n"; XXX - if (it->doc && strlen(it->doc) > 0) - signatures += "\n" + std::string(it->doc) + "\n"; + signatures += "Signature : "; + signatures += it->signature; + signatures += "\n"; + if (it->doc && strlen(it->doc) > 0) { + signatures += "\n"; + signatures += it->doc; + signatures += "\n"; + } if (it->next) signatures += "\n"; it = it->next; } PyCFunctionObject *func = (PyCFunctionObject *) m_ptr; if (func->m_ml->ml_doc) - std::free((char *) func->m_ml->ml_doc); + free((char *) func->m_ml->ml_doc); func->m_ml->ml_doc = strdup(signatures.c_str()); if (entry->class_) { #if PY_MAJOR_VERSION >= 3 @@ -510,7 +574,7 @@ public: type->ht_type.tp_base = (PyTypeObject *) parent; if (doc) { size_t size = strlen(doc)+1; - type->ht_type.tp_doc = (char *)PyObject_MALLOC(size); + type->ht_type.tp_doc = (char *) PyObject_MALLOC(size); memcpy((void *) type->ht_type.tp_doc, doc, size); } Py_XINCREF(parent); @@ -522,7 +586,8 @@ public: /* Needed by pydoc */ attr("__module__") = scope_name; - auto &type_info = detail::get_internals().registered_types[tinfo]; + auto ®istered_types = get_internals().registered_types; + auto &type_info = registered_types[tinfo]; type_info.type = (PyTypeObject *) m_ptr; type_info.type_size = type_size; type_info.init_holder = init_holder; diff --git a/include/pybind11/stl.h b/include/pybind11/stl.h index c4f4b196c..3bff58216 100644 --- a/include/pybind11/stl.h +++ b/include/pybind11/stl.h @@ -45,7 +45,7 @@ public: static PyObject *cast(const type &src, return_value_policy policy, PyObject *parent) { object list(PyList_New(src.size()), false); if (!list) - return nullptr; + return nullptr; size_t index = 0; for (auto const &value: src) { object value_ (value_conv::cast(value, policy, parent), false); @@ -55,23 +55,23 @@ public: } return list.release(); } - PYBIND11_TYPE_CASTER(type, detail::descr("list<") + value_conv::name() + detail::descr(">")); + PYBIND11_TYPE_CASTER(type, _("list<") + value_conv::name() + _(">")); }; -template struct type_caster> { - typedef std::set type; - typedef type_caster value_conv; +template struct type_caster> { + typedef std::set type; + typedef type_caster key_conv; public: bool load(PyObject *src, bool convert) { pybind11::set s(src, true); if (!s.check()) return false; value.clear(); - value_conv conv; + key_conv conv; for (const object &o: s) { if (!conv.load((PyObject *) o.ptr(), convert)) return false; - value.insert((Value) conv); + value.insert((Key) conv); } return true; } @@ -79,15 +79,15 @@ public: static PyObject *cast(const type &src, return_value_policy policy, PyObject *parent) { object set(PySet_New(nullptr), false); if (!set) - return nullptr; + return nullptr; for (auto const &value: src) { - object value_(value_conv::cast(value, policy, parent), false); + object value_(key_conv::cast(value, policy, parent), false); if (!value_ || PySet_Add(set.ptr(), value_.ptr()) != 0) return nullptr; } return set.release(); } - PYBIND11_TYPE_CASTER(type, detail::descr("set<") + value_conv::name() + detail::descr(">")); + PYBIND11_TYPE_CASTER(type, _("set<") + key_conv::name() + _(">")); }; template struct type_caster> { @@ -116,7 +116,7 @@ public: static PyObject *cast(const type &src, return_value_policy policy, PyObject *parent) { object dict(PyDict_New(), false); if (!dict) - return nullptr; + return nullptr; for (auto const &kv: src) { object key(key_conv::cast(kv.first, policy, parent), false); object value(value_conv::cast(kv.second, policy, parent), false); @@ -126,7 +126,7 @@ public: return dict.release(); } - PYBIND11_TYPE_CASTER(type, detail::descr("dict<") + key_conv::name() + detail::descr(", ") + value_conv::name() + detail::descr(">")); + PYBIND11_TYPE_CASTER(type, _("dict<") + key_conv::name() + _(", ") + value_conv::name() + _(">")); }; NAMESPACE_END(detail) diff --git a/include/pybind11/typeid.h b/include/pybind11/typeid.h index 551895fcc..2d7c39bbd 100644 --- a/include/pybind11/typeid.h +++ b/include/pybind11/typeid.h @@ -33,11 +33,11 @@ inline void clean_type_id(std::string &name) { abi::__cxa_demangle(name.c_str(), nullptr, nullptr, &status), std::free }; if (status == 0) name = res.get(); - #else - detail::erase_all(name, "class "); - detail::erase_all(name, "struct "); - detail::erase_all(name, "enum "); - #endif +#else + detail::erase_all(name, "class "); + detail::erase_all(name, "struct "); + detail::erase_all(name, "enum "); +#endif detail::erase_all(name, "pybind11::"); } NAMESPACE_END(detail)