diff --git a/py/py_byml.cpp b/py/py_byml.cpp index 74bd22d..f84a54b 100644 --- a/py/py_byml.cpp +++ b/py/py_byml.cpp @@ -17,6 +17,7 @@ * along with syaz0. If not, see . */ +#include #include #include #include @@ -26,6 +27,7 @@ #include #include +#include #include "main.h" OEAD_MAKE_OPAQUE("Array", oead::Byml::Array); @@ -55,6 +57,31 @@ struct type_caster { } // namespace pybind11::detail namespace oead::bind { +/// Wraps a callable to avoid expensive conversions to Byml; instead, we detect +/// whether the first parameter is a Byml::Array or a Byml::Hash and if so we +/// borrow the value and move-construct a temporary Byml in order to avoid copies. +template +static auto BorrowByml(Callable&& callable) { + return [&, callable](py::handle handle, Ts&&... args) { + Byml obj; + const auto invoke = [&, callable](auto&& value) { + obj = std::move(value); + return std::invoke(callable, obj, std::forward(args)...); + }; + if (py::isinstance(handle)) { + auto& ref = handle.cast(); + util::ScopeGuard guard{[&] { ref = std::move(obj.Get()); }}; + return invoke(std::move(ref)); + } + if (py::isinstance(handle)) { + auto& ref = handle.cast(); + util::ScopeGuard guard{[&] { ref = std::move(obj.Get()); }}; + return invoke(std::move(ref)); + } + return invoke(handle.cast()); + }; +} + void BindByml(py::module& parent) { auto m = parent.def_submodule("byml"); m.def( @@ -62,17 +89,18 @@ void BindByml(py::module& parent) { py::return_value_policy::move, ":return: An Array or a Hash."); m.def("from_text", &Byml::FromText, "yml_text"_a, py::return_value_policy::move, ":return: An Array or a Hash."); - m.def("to_binary", &Byml::ToBinary, "data"_a, "big_endian"_a, "version"_a = 2); - m.def("to_text", &Byml::ToText, "data"_a); + m.def("to_binary", BorrowByml(&Byml::ToBinary), "data"_a, "big_endian"_a, + "version"_a = 2); + m.def("to_text", BorrowByml(&Byml::ToText), "data"_a); - m.def("get_bool", &Byml::GetBool, "data"_a); - m.def("get_double", &Byml::GetDouble, "data"_a); - m.def("get_float", &Byml::GetFloat, "data"_a); - m.def("get_int", &Byml::GetInt, "data"_a); - m.def("get_int64", &Byml::GetInt64, "data"_a); - m.def("get_string", &Byml::GetString, "data"_a); - m.def("get_uint", &Byml::GetUInt, "data"_a); - m.def("get_uint64", &Byml::GetUInt64, "data"_a); + m.def("get_bool", BorrowByml(&Byml::GetBool), "data"_a); + m.def("get_double", BorrowByml(&Byml::GetDouble), "data"_a); + m.def("get_float", BorrowByml(&Byml::GetFloat), "data"_a); + m.def("get_int", BorrowByml(&Byml::GetInt), "data"_a); + m.def("get_int64", BorrowByml(&Byml::GetInt64), "data"_a); + m.def("get_string", BorrowByml(&Byml::GetString), "data"_a); + m.def("get_uint", BorrowByml(&Byml::GetUInt), "data"_a); + m.def("get_uint64", BorrowByml(&Byml::GetUInt64), "data"_a); BindVector(m, "Array"); BindMap(m, "Hash");