Skip to content

Constexpr type signatures for C++11 and semi-constexpr for MSVC #934

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 3 commits into from
Sep 16, 2017
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 2 additions & 3 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -87,9 +87,8 @@ In addition to the core functionality, pybind11 provides some extra goodies:
[reported](http://graylab.jhu.edu/RosettaCon2016/PyRosetta-4.pdf) a binary
size reduction of **5.4x** and compile time reduction by **5.8x**.

- When supported by the compiler, two new C++14 features (relaxed constexpr and
return value deduction) are used to precompute function signatures at compile
time, leading to smaller binaries.
- Function signatures are precomputed at compile time (using ``constexpr``),
leading to smaller binaries.

- With little extra effort, C++ types can be pickled and unpickled similar to
regular Python objects.
Expand Down
6 changes: 5 additions & 1 deletion docs/changelog.rst
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,11 @@ Starting with version 1.8.0, pybind11 releases use a `semantic versioning
v2.3.0 (Not yet released)
-----------------------------------------------------

* TBD
* Significantly reduced module binary size (10-20%) when compiled in C++11 mode
with GCC/Clang, or in any mode with MSVC. Function signatures are now always
precomputed at compile time (this was previously only available in C++14 mode
for non-MSVC compilers).
`#934 <https://github.com/pybind/pybind11/pull/934>`_.

v2.2.1 (September 14, 2017)
-----------------------------------------------------
Expand Down
30 changes: 0 additions & 30 deletions docs/faq.rst
Original file line number Diff line number Diff line change
Expand Up @@ -228,36 +228,6 @@ In addition to decreasing binary size, ``-fvisibility=hidden`` also avoids
potential serious issues when loading multiple modules and is required for
proper pybind operation. See the previous FAQ entry for more details.

Another aspect that can require a fair bit of code are function signature
descriptions. pybind11 automatically generates human-readable function
signatures for docstrings, e.g.:

.. code-block:: none

| __init__(...)
| __init__(*args, **kwargs)
| Overloaded function.
|
| 1. __init__(example.Example1) -> NoneType
|
| Docstring for overload #1 goes here
|
| 2. __init__(example.Example1, int) -> NoneType
|
| Docstring for overload #2 goes here
|
| 3. __init__(example.Example1, example.Example1) -> NoneType
|
| Docstring for overload #3 goes here


In C++11 mode, these are generated at run time using string concatenation,
which can amount to 10-20% of the size of the resulting binary. If you can,
enable C++14 language features (using ``-std=c++14`` for GCC/Clang), in which
case signatures are efficiently pre-generated at compile time. Unfortunately,
Visual Studio's C++14 support (``constexpr``) is not good enough as of April
2016, so it always uses the more expensive run-time approach.

Working with ancient Visual Studio 2009 builds on Windows
=========================================================

Expand Down
5 changes: 2 additions & 3 deletions docs/intro.rst
Original file line number Diff line number Diff line change
Expand Up @@ -77,9 +77,8 @@ In addition to the core functionality, pybind11 provides some extra goodies:
of `PyRosetta`_, an enormous Boost.Python binding project, reported a binary
size reduction of **5.4x** and compile time reduction by **5.8x**.

- When supported by the compiler, two new C++14 features (relaxed constexpr and
return value deduction) are used to precompute function signatures at compile
time, leading to smaller binaries.
- Function signatures are precomputed at compile time (using ``constexpr``),
leading to smaller binaries.

- With little extra effort, C++ types can be pickled and unpickled similar to
regular Python objects.
Expand Down
28 changes: 13 additions & 15 deletions include/pybind11/cast.h
Original file line number Diff line number Diff line change
Expand Up @@ -778,7 +778,7 @@ template <typename T1, typename T2> struct is_copy_constructible<std::pair<T1, T
template <typename type> class type_caster_base : public type_caster_generic {
using itype = intrinsic_t<type>;
public:
static PYBIND11_DESCR name() { return type_descr(_<type>()); }
static constexpr auto name = _<type>();

type_caster_base() : type_caster_base(typeid(type)) { }
explicit type_caster_base(const std::type_info &info) : type_caster_generic(info) { }
Expand Down Expand Up @@ -885,7 +885,7 @@ template <typename type> class type_caster<std::reference_wrapper<type>> {
"std::reference_wrapper<T> caster requires T to have a caster with an `T &` operator");
public:
bool load(handle src, bool convert) { return subcaster.load(src, convert); }
static PYBIND11_DESCR name() { return caster_t::name(); }
static constexpr auto name = caster_t::name;
static handle cast(const std::reference_wrapper<type> &src, return_value_policy policy, handle parent) {
// It is definitely wrong to take ownership of this pointer, so mask that rvp
if (policy == return_value_policy::take_ownership || policy == return_value_policy::automatic)
Expand All @@ -900,7 +900,7 @@ template <typename type> class type_caster<std::reference_wrapper<type>> {
protected: \
type value; \
public: \
static PYBIND11_DESCR name() { return type_descr(py_name); } \
static constexpr auto name = py_name; \
template <typename T_, enable_if_t<std::is_same<type, remove_cv_t<T_>>::value, int> = 0> \
static handle cast(T_ *src, return_value_policy policy, handle parent) { \
if (!src) return none().release(); \
Expand Down Expand Up @@ -1049,7 +1049,7 @@ template <> class type_caster<void> : public type_caster<void_type> {

template <typename T> using cast_op_type = void*&;
operator void *&() { return value; }
static PYBIND11_DESCR name() { return type_descr(_("capsule")); }
static constexpr auto name = _("capsule");
private:
void *value = nullptr;
};
Expand Down Expand Up @@ -1289,7 +1289,7 @@ template <typename CharT> struct type_caster<CharT, enable_if_t<is_std_char_type
return value[0];
}

static PYBIND11_DESCR name() { return type_descr(_(PYBIND11_STRING_NAME)); }
static constexpr auto name = _(PYBIND11_STRING_NAME);
template <typename _T> using cast_op_type = remove_reference_t<pybind11::detail::cast_op_type<_T>>;
};

Expand All @@ -1314,9 +1314,7 @@ template <template<typename...> class Tuple, typename... Ts> class tuple_caster
return cast_impl(std::forward<T>(src), policy, parent, indices{});
}

static PYBIND11_DESCR name() {
return type_descr(_("Tuple[") + detail::concat(make_caster<Ts>::name()...) + _("]"));
}
static constexpr auto name = _("Tuple[") + concat(make_caster<Ts>::name...) + _("]");

template <typename T> using cast_op_type = type;

Expand Down Expand Up @@ -1461,7 +1459,7 @@ struct move_only_holder_caster {
auto *ptr = holder_helper<holder_type>::get(src);
return type_caster_base<type>::cast_holder(ptr, &src);
}
static PYBIND11_DESCR name() { return type_caster_base<type>::name(); }
static constexpr auto name = type_caster_base<type>::name;
};

template <typename type, typename deleter>
Expand Down Expand Up @@ -1492,10 +1490,10 @@ template <typename base, typename holder> struct is_holder_type :
template <typename base, typename deleter> struct is_holder_type<base, std::unique_ptr<base, deleter>> :
std::true_type {};

template <typename T> struct handle_type_name { static PYBIND11_DESCR name() { return _<T>(); } };
template <> struct handle_type_name<bytes> { static PYBIND11_DESCR name() { return _(PYBIND11_BYTES_NAME); } };
template <> struct handle_type_name<args> { static PYBIND11_DESCR name() { return _("*args"); } };
template <> struct handle_type_name<kwargs> { static PYBIND11_DESCR name() { return _("**kwargs"); } };
template <typename T> struct handle_type_name { static constexpr auto name = _<T>(); };
template <> struct handle_type_name<bytes> { static constexpr auto name = _(PYBIND11_BYTES_NAME); };
template <> struct handle_type_name<args> { static constexpr auto name = _("*args"); };
template <> struct handle_type_name<kwargs> { static constexpr auto name = _("**kwargs"); };

template <typename type>
struct pyobject_caster {
Expand All @@ -1513,7 +1511,7 @@ struct pyobject_caster {
static handle cast(const handle &src, return_value_policy /* policy */, handle /* parent */) {
return src.inc_ref();
}
PYBIND11_TYPE_CASTER(type, handle_type_name<type>::name());
PYBIND11_TYPE_CASTER(type, handle_type_name<type>::name);
};

template <typename T>
Expand Down Expand Up @@ -1826,7 +1824,7 @@ class argument_loader {
static constexpr bool has_kwargs = kwargs_pos < 0;
static constexpr bool has_args = args_pos < 0;

static PYBIND11_DESCR arg_names() { return detail::concat(make_caster<Args>::name()...); }
static constexpr auto arg_names = concat(type_descr(make_caster<Args>::name)...);

bool load_args(function_call &call) {
return load_impl_sequence(call, indices{});
Expand Down
Loading