Skip to content
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

support None for const char* arg, with minimal overhead #683

Open
wants to merge 1 commit into
base: master
Choose a base branch
from
Open
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
8 changes: 5 additions & 3 deletions docs/api_core.rst
Original file line number Diff line number Diff line change
Expand Up @@ -1622,9 +1622,11 @@ parameter of :cpp:func:`module_::def`, :cpp:func:`class_::def`,

Set a flag noting that the function argument accepts ``None``. Can only
be used for python wrapper types (e.g. :cpp:class:`handle`,
:cpp:class:`int_`) and types that have been bound using
:cpp:class:`class_`. You cannot use this to implement functions that
accept null pointers to builtin C++ types like ``int *i = nullptr``.
:cpp:class:`int_`), for types that have been bound using
:cpp:class:`class_`, for :cpp:class:`ndarray`, ``std::optional``,
``std::variant``, and ``const char*``.
You cannot use this to implement functions that
accept null pointers to other builtin C++ types like ``int *i = nullptr``.

.. cpp:function:: arg &noconvert(bool value = true)

Expand Down
6 changes: 0 additions & 6 deletions docs/porting.rst
Original file line number Diff line number Diff line change
Expand Up @@ -86,12 +86,6 @@ It is also possible to set a ``None`` default value, in which case the

m.def("func", &func, "arg"_a = nb::none());

``None``-valued arguments are only supported by two of the three parameter
passing styles described in the section on :ref:`information exchange
<exchange>`. In particular, they are supported by :ref:`bindings <bindings>`
and :ref:`wrappers <wrappers>`, *but not* by :ref:`type casters
<type_casters>`.

Shared pointers and holders
---------------------------

Expand Down
3 changes: 2 additions & 1 deletion include/nanobind/nb_cast.h
Original file line number Diff line number Diff line change
Expand Up @@ -286,7 +286,8 @@ template <> struct type_caster<char> {
value = PyUnicode_AsUTF8AndSize(src.ptr(), &size);
if (!value) {
PyErr_Clear();
return false;
// optimize for src being a string, check for None afterwards
return src.is_none();
}
return true;
}
Expand Down
1 change: 1 addition & 0 deletions tests/test_functions.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -161,6 +161,7 @@ NB_MODULE(test_functions_ext, m) {

// Test string caster
m.def("test_12", [](const char *c) { return nb::str(c); });
m.def("test_12_n", [](const char *c) { return nb::str(c ? c : "n/a"); }, nb::arg().none());
m.def("test_13", []() -> const char * { return "test"; });
m.def("test_14", [](nb::object o) -> const char * { return nb::cast<const char *>(o); });

Expand Down
2 changes: 2 additions & 0 deletions tests/test_functions.py
Original file line number Diff line number Diff line change
Expand Up @@ -236,6 +236,8 @@ def test21_numpy_overloads():

def test22_string_return():
assert t.test_12("hello") == "hello"
assert t.test_12_n("hello") == "hello"
assert t.test_12_n(None) == "n/a"
assert t.test_13() == "test"
assert t.test_14("abc") == "abc"

Expand Down
2 changes: 2 additions & 0 deletions tests/test_functions_ext.pyi.ref
Original file line number Diff line number Diff line change
Expand Up @@ -84,6 +84,8 @@ def test_11_ull(arg: int, /) -> int: ...

def test_12(arg: str, /) -> str: ...

def test_12_n(arg: str | None) -> str: ...

def test_13() -> str: ...

def test_14(arg: object, /) -> str: ...
Expand Down