diff --git a/clif/backend/matcher.cc b/clif/backend/matcher.cc index a7f4091d..b2404ac6 100644 --- a/clif/backend/matcher.cc +++ b/clif/backend/matcher.cc @@ -2963,6 +2963,10 @@ const clang::FunctionDecl* ClifMatcher::SpecializeFunctionTemplate( false, // No partial overloading #if PYCLIF_LLVM_VERSION_MAJOR > 16 /*AggregateDeductionCandidate=*/false, +#endif +#if PYCLIF_LLVM_VERSION_MAJOR > 18 + /*ObjectType=*/QualType(), + /*ObjectClassification=*/clang::Expr::Classification(), #endif [&](clang::ArrayRef param_types) { // For template parameters that aren't instantiation diff --git a/clif/pybind11/classes.py b/clif/pybind11/classes.py index a01d0ea8..e7e92dd8 100644 --- a/clif/pybind11/classes.py +++ b/clif/pybind11/classes.py @@ -196,7 +196,7 @@ def _generate_iterator( if function_lib.has_bytes_return(func_decl): template_param = '' yield ( - f'{class_name}.def("__iter__", [](const {class_decl.name.cpp_name} &s)' + f'{class_name}.def("__iter__", []({class_decl.name.cpp_name} &s)' f'{{ return py::make_iterator{template_param}(s.begin(), s.end()); }}, ' 'py::keep_alive<0, 1>());') diff --git a/clif/pybind11/clif_type_casters.h b/clif/pybind11/clif_type_casters.h index 3d659f0d..54f79905 100644 --- a/clif/pybind11/clif_type_casters.h +++ b/clif/pybind11/clif_type_casters.h @@ -234,11 +234,10 @@ struct clif_type_caster< } template - using cast_op_type = movable_cast_op_type; + using cast_op_type = ::pybind11::detail::cast_op_type; operator Type*() { return value; } operator Type&() { return *value; } - operator Type&&() && { return std::move(*value); } private: Type* value; diff --git a/clif/pybind11/function_lib.py b/clif/pybind11/function_lib.py index 971ee772..6c91ce64 100644 --- a/clif/pybind11/function_lib.py +++ b/clif/pybind11/function_lib.py @@ -63,8 +63,13 @@ def __init__(self, param: ast_pb2.ParamDecl, param_name: str, self.function_argument = f'std::move({self.gen_name})' elif ptype.cpp_raw_pointer: if (not ptype.cpp_toptr_conversion and ctype.endswith('*') - and ptype.cpp_has_public_dtor and not ptype.cpp_abstract - and ptype.cpp_has_def_ctor): + and ptype.cpp_abstract and ptype.cpp_touniqptr_conversion): + t = ctype[:-1] + self.cpp_type = f'::std::unique_ptr<{t}>' + self.function_argument = f'{param_name}.get()' + elif (not ptype.cpp_toptr_conversion and ctype.endswith('*') + and ptype.cpp_has_public_dtor and not ptype.cpp_abstract + and ptype.cpp_has_def_ctor): # Create a copy on stack and pass its address. # For compatibility with the original C API code generator. self.cpp_type = ctype[:-1] diff --git a/clif/pybind11/generator.py b/clif/pybind11/generator.py index ae6a067c..c47c78a0 100644 --- a/clif/pybind11/generator.py +++ b/clif/pybind11/generator.py @@ -99,6 +99,10 @@ def generate_header(self, includes = set() for decl in ast.decls: includes.add(decl.cpp_file) + for include in self._ast.usertype_includes: + includes.add(include) + for include in self._pybind11_only_includes: + includes.add(include) yield '#include "third_party/pybind11/include/pybind11/smart_holder.h"' yield '#include "clif/python/postconv.h"' for include in includes: @@ -269,11 +273,6 @@ def _generate_headlines(self): yield '#include "third_party/pybind11/include/pybind11/stl.h"' yield '#include "third_party/pybind11/include/pybind11/type_caster_pyobject_ptr.h"' # pylint: disable=long-line yield '' - yield '// See pybind11_protobuf/proto_caster_impl.h' - yield '#if !defined(PYBIND11_PROTOBUF_UNSAFE)' - yield I + '#define PYBIND11_PROTOBUF_UNSAFE 1' - yield '#endif' - yield '' for include in includes: yield f'#include "{include}"' yield f'#include "{self._header_path}"' diff --git a/clif/pybind11/variables.py b/clif/pybind11/variables.py index dc6ee8bb..85988ef1 100644 --- a/clif/pybind11/variables.py +++ b/clif/pybind11/variables.py @@ -62,6 +62,7 @@ def _generate_cpp_get( var_decl: ast_pb2.VarDecl, class_decl: ast_pb2.ClassDecl, generate_comma: bool = False, + is_unproperty: bool = False, ) -> Generator[str, None, None]: """Generate lambda expressions for getters.""" reference_internal = True @@ -72,7 +73,7 @@ def _generate_cpp_get( ret = f'self.{function_name}()' else: ret = f'self.{var_decl.name.cpp_name}' - if _convert_ptr_to_ref(var_decl): + if not is_unproperty and _convert_ptr_to_ref(var_decl): ret = f'*{ret}' reference_internal = False return_value_policy = function_lib.generate_return_value_policy_for_type( @@ -228,7 +229,7 @@ def _generate_unproperty( ) -> Generator[str, None, None]: """Generates functions to expose attributes instead of exposing directly.""" yield f'{class_name}.def("{var_decl.cpp_get.name.native}",' - yield from _generate_cpp_get(var_decl, class_decl) + yield from _generate_cpp_get(var_decl, class_decl, is_unproperty=True) if var_decl.HasField('cpp_set'): yield f'{class_name}.def("{var_decl.cpp_set.name.native}",' yield from _generate_cpp_set_without_setter(var_decl, class_decl) diff --git a/clif/testing/classes.h b/clif/testing/classes.h index a35cba67..aa176816 100644 --- a/clif/testing/classes.h +++ b/clif/testing/classes.h @@ -165,6 +165,12 @@ struct NestedAttributes { } }; +struct SomePointee {}; + +struct SomePointerOwner { + const SomePointee* some_pointer = nullptr; +}; + // Use the duplicated namespace `clif_testing` for testing purposes. namespace clif_testing { diff --git a/clif/testing/copy_move_types_custom_from_as.h b/clif/testing/copy_move_types_custom_from_as.h new file mode 100644 index 00000000..ee398afb --- /dev/null +++ b/clif/testing/copy_move_types_custom_from_as.h @@ -0,0 +1,132 @@ +/* + * Copyright 2023 Google LLC + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef CLIF_TESTING_COPY_MOVE_TYPES_CUSTOM_FROM_AS_H_ +#define CLIF_TESTING_COPY_MOVE_TYPES_CUSTOM_FROM_AS_H_ + +#include +#include +#include + +#include "clif/testing/copy_move_types_library.h" + +namespace clif_testing::copy_move_types_custom_from_as { + +using copy_move_types_library::CopyMoveType; + +// Need to hide inheritance to get the correct `Clif_PyObjAs()`. +// For clif_type_casters that use `Type` to hold the value: +// http://google3/clif/pybind11/clif_type_casters.h;l=177;rcl=570854707 +class FromCRAsPCopyMoveType : private CopyMoveType { + public: + FromCRAsPCopyMoveType() = default; + explicit FromCRAsPCopyMoveType(const CopyMoveType& obj) + : CopyMoveType{obj} {} + using CopyMoveType::GetTrace; +}; + +inline FromCRAsPCopyMoveType MakeFromCRAsPCopyMoveType( + const CopyMoveType& class_obj) { + return FromCRAsPCopyMoveType{class_obj}; +} + +inline std::string GetTraceFromCRAsPCopyMoveType( + const FromCRAsPCopyMoveType& cfa_obj) { + return cfa_obj.GetTrace(); +} + +// For clif_type_casters that use `std::optional` to hold the value: +// http://google3/clif/pybind11/clif_type_casters.h;l=193;rcl=570854707 +class FromCRAsOpCopyMoveType : private CopyMoveType { + public: + FromCRAsOpCopyMoveType() = default; + explicit FromCRAsOpCopyMoveType(const CopyMoveType& obj) + : CopyMoveType{obj} {} + using CopyMoveType::GetTrace; +}; + +inline FromCRAsOpCopyMoveType MakeFromCRAsOpCopyMoveType( + const CopyMoveType& class_obj) { + return FromCRAsOpCopyMoveType{class_obj}; +} + +inline std::string GetTraceFromCRAsOpCopyMoveType( + std::optional cfa_obj) { + return cfa_obj.value().GetTrace(); +} + +// For clif_type_casters that use `Type*` to hold the value: +// http://google3/clif/pybind11/clif_type_casters.h;l=219;rcl=570854707 +class FromCRAsPPCopyMoveType : private CopyMoveType { + public: + explicit FromCRAsPPCopyMoveType(const CopyMoveType& obj) + : CopyMoveType{obj} {} + using CopyMoveType::GetTrace; +}; + +inline FromCRAsPPCopyMoveType MakeFromCRAsPPCopyMoveType( + const CopyMoveType& class_obj) { + return FromCRAsPPCopyMoveType{class_obj}; +} + +inline std::string GetTraceFromCRAsPPCopyMoveType( + const FromCRAsPPCopyMoveType& cfa_obj) { + return cfa_obj.GetTrace(); +} + +// For clif_type_casters that use `std::unique_ptr` to hold the value: +// http://google3/clif/pybind11/clif_type_casters.h;l=247;rcl=570854707 +class FromCRAsUpCopyMoveType : private CopyMoveType { + public: + FromCRAsUpCopyMoveType() = default; + explicit FromCRAsUpCopyMoveType(const CopyMoveType& obj) + : CopyMoveType{obj} {} + using CopyMoveType::GetTrace; +}; + +inline FromCRAsUpCopyMoveType MakeFromCRAsUpCopyMoveType( + const CopyMoveType& class_obj) { + return FromCRAsUpCopyMoveType{class_obj}; +} + +inline std::string GetTraceFromCRAsUpCopyMoveType( + std::unique_ptr cfa_obj) { + return cfa_obj->GetTrace(); +} + +// For clif_type_casters that use `std::shared_ptr` to hold the value: +// http://google3/clif/pybind11/clif_type_casters.h;l=285;rcl=570854707 +class FromCRAsSpCopyMoveType : private CopyMoveType { + public: + FromCRAsSpCopyMoveType() = default; + explicit FromCRAsSpCopyMoveType(const CopyMoveType& obj) + : CopyMoveType{obj} {} + using CopyMoveType::GetTrace; +}; + +inline FromCRAsSpCopyMoveType MakeFromCRAsSpCopyMoveType( + const CopyMoveType& class_obj) { + return FromCRAsSpCopyMoveType{class_obj}; +} + +inline std::string GetTraceFromCRAsSpCopyMoveType( + FromCRAsSpCopyMoveType* cfa_obj) { + return cfa_obj->GetTrace(); +} + +} // namespace clif_testing::copy_move_types_custom_from_as + +#endif // CLIF_TESTING_COPY_MOVE_TYPES_CUSTOM_FROM_AS_H_ diff --git a/clif/testing/copy_move_types_library.h b/clif/testing/copy_move_types_library.h new file mode 100644 index 00000000..988e69d6 --- /dev/null +++ b/clif/testing/copy_move_types_library.h @@ -0,0 +1,126 @@ +/* + * Copyright 2023 Google LLC + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef CLIF_TESTING_COPY_MOVE_TYPES_LIBRARY_H_ +#define CLIF_TESTING_COPY_MOVE_TYPES_LIBRARY_H_ + +#include +#include + +namespace clif_testing::copy_move_types_library { + +// Intentionally not using inheritance, to ensure each type is completely +// self-contained and does not trigger any inheritance-related functionality. + +class CopyMoveType { + private: + std::string trace_; + + public: + const std::string &GetTrace() const { return trace_; } + + explicit CopyMoveType(std::string_view trace = "DefaultCtor") + : trace_(trace) {} + + CopyMoveType(const CopyMoveType &other) { trace_ = other.trace_ + "_CpCtor"; } + + CopyMoveType &operator=(const CopyMoveType &rhs) { + trace_ = rhs.trace_ + "_CpLhs"; + return *this; + } + + CopyMoveType(CopyMoveType &&other) { + trace_ = other.trace_ + "_MvCtorTo"; + other.trace_ += "_MvCtorFrom"; + } + + CopyMoveType &operator=(CopyMoveType &&rhs) { + trace_ = rhs.trace_ + "_MvLhs"; + rhs.trace_ += "_MvRhs"; + return *this; + } +}; + +class CopyOnlyType { + private: + std::string trace_; + + public: + const std::string &GetTrace() const { return trace_; } + + explicit CopyOnlyType(std::string_view trace = "DefaultCtor") + : trace_(trace) {} + + CopyOnlyType(const CopyOnlyType &other) { trace_ = other.trace_ + "_CpCtor"; } + + CopyOnlyType &operator=(const CopyOnlyType &rhs) { + trace_ = rhs.trace_ + "_CpLhs"; + return *this; + } + + CopyOnlyType(CopyOnlyType &&) = delete; + + CopyOnlyType &operator=(CopyOnlyType &&) = delete; +}; + +class MoveOnlyType { + private: + std::string trace_; + + public: + const std::string &GetTrace() const { return trace_; } + + explicit MoveOnlyType(std::string_view trace = "DefaultCtor") + : trace_(trace) {} + + MoveOnlyType(const MoveOnlyType &) = delete; + + MoveOnlyType &operator=(const MoveOnlyType &) = delete; + + MoveOnlyType(MoveOnlyType &&other) { + trace_ = other.trace_ + "_MvCtorTo"; + other.trace_ += "_MvCtorFrom"; + } + + MoveOnlyType &operator=(MoveOnlyType &&rhs) { + trace_ = rhs.trace_ + "_MvLhs"; + rhs.trace_ += "_MvRhs"; + return *this; + } +}; + +class StayPutType { + private: + std::string trace_; + + public: + const std::string &GetTrace() const { return trace_; } + + explicit StayPutType(std::string_view trace = "DefaultCtor") + : trace_(trace) {} + + StayPutType(const StayPutType &) = delete; + + StayPutType &operator=(const StayPutType &) = delete; + + StayPutType(StayPutType &&) = delete; + + StayPutType &operator=(StayPutType &&) = delete; +}; + +} // namespace clif_testing::copy_move_types_library + +#endif // CLIF_TESTING_COPY_MOVE_TYPES_LIBRARY_H_ diff --git a/clif/testing/py_capsule_helpers.h b/clif/testing/py_capsule_helpers.h new file mode 100644 index 00000000..f13a4594 --- /dev/null +++ b/clif/testing/py_capsule_helpers.h @@ -0,0 +1,67 @@ +/* + * Copyright 2023 Google LLC + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef CLIF_TESTING_PY_CAPSULE_HELPERS_H_ +#define CLIF_TESTING_PY_CAPSULE_HELPERS_H_ + +#include + +#include +#include + +#include "absl/log/log.h" + +namespace clif_testing::py_capsule_helpers { + +template +PyObject* MakePyCapsuleWithPayloadPointer( + std::unique_ptr&& payload) { + return PyCapsule_New( + payload.release(), typeid(PayloadType*).name(), [](PyObject* self) { + void* void_ptr = + PyCapsule_GetPointer(self, typeid(PayloadType*).name()); + if (PyErr_Occurred()) { + PyErr_Print(); + LOG(FATAL) << "UNEXPECTED Python exception (sent to stderr)."; + } + assert(void_ptr != nullptr); + delete static_cast(void_ptr); + }); +} + +template +bool GetPayloadPointerFromPyCapsule(PyObject* py_obj, PayloadType** c_obj) { + void* void_ptr = PyCapsule_GetPointer(py_obj, typeid(PayloadType*).name()); + if (void_ptr == nullptr) { + return false; + } + *c_obj = static_cast(void_ptr); + return true; +} + +template +bool GetPayloadFromPyCapsule(PyObject* py_obj, PayloadType* c_obj) { + void* void_ptr = PyCapsule_GetPointer(py_obj, typeid(PayloadType*).name()); + if (void_ptr == nullptr) { + return false; + } + *c_obj = *(static_cast(void_ptr)); + return true; +} + +} // namespace clif_testing::py_capsule_helpers + +#endif // CLIF_TESTING_PY_CAPSULE_HELPERS_H_ diff --git a/clif/testing/python/classes.clif b/clif/testing/python/classes.clif index 7ec7ee61..b8fa3eb3 100644 --- a/clif/testing/python/classes.clif +++ b/clif/testing/python/classes.clif @@ -99,6 +99,16 @@ from "clif/testing/classes.h": @setter def `pair_unproperty_bytes_bytes` as set_pair_unproperty_bytes_bytes(self, p: tuple) + class SomePointee: + pass + + class SomePointerOwner: + def __init__(self) + @getter + def `some_pointer` as GetSomePointee(self) -> SomePointee + @setter + def `some_pointer` as SetSomePointee(self, pointee: SomePointee) + namespace `::clif_testing::classes::clif_testing`: class WithAmbiguousNamespace: def get_value(self) -> int diff --git a/clif/testing/python/classes_test.py b/clif/testing/python/classes_test.py index 0030aab4..0a598dd2 100644 --- a/clif/testing/python/classes_test.py +++ b/clif/testing/python/classes_test.py @@ -112,6 +112,13 @@ def testGenerateDefaultCtor(self): obj = classes.AddInitNoParams() self.assertEqual(obj.get_value(), 10) + def testUnpropertyForPointers(self): + obj = classes.SomePointerOwner() + self.assertIsNone(obj.GetSomePointee()) + pointee = classes.SomePointee() + obj.SetSomePointee(pointee) + self.assertIsNotNone(obj.GetSomePointee()) + def testHandleAmbiguousNamespace(self): obj = classes.WithAmbiguousNamespace() self.assertEqual(obj.get_value(), 10) diff --git a/clif/testing/python/copy_move_types_custom_from_as.clif b/clif/testing/python/copy_move_types_custom_from_as.clif new file mode 100644 index 00000000..69bdaf56 --- /dev/null +++ b/clif/testing/python/copy_move_types_custom_from_as.clif @@ -0,0 +1,39 @@ +# Copyright 2023 Google LLC +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +from "clif/testing/python/copy_move_types_custom_from_as_clif_use.h" import * +from "clif/testing/python/copy_move_types_library_clif.h" import * + +from "clif/testing/copy_move_types_custom_from_as.h": + namespace `clif_testing::copy_move_types_custom_from_as`: + + # Default clif type caster + def MakeFromCRAsPCopyMoveType(class_obj: CopyMoveType) -> FromCRAsPCopyMoveType + def GetTraceFromCRAsPCopyMoveType(cfa_obj: FromCRAsPCopyMoveType) -> str + + # std::optional + def MakeFromCRAsOpCopyMoveType(class_obj: CopyMoveType) -> FromCRAsOpCopyMoveType + def GetTraceFromCRAsOpCopyMoveType(cfa_obj: NoneOr) -> str + + # Pointer to pointer + def MakeFromCRAsPPCopyMoveType(class_obj: CopyMoveType) -> FromCRAsPPCopyMoveType + def GetTraceFromCRAsPPCopyMoveType(cfa_obj: FromCRAsPPCopyMoveType) -> str + + # std::unique_ptr + def MakeFromCRAsUpCopyMoveType(class_obj: CopyMoveType) -> FromCRAsUpCopyMoveType + def GetTraceFromCRAsUpCopyMoveType(cfa_obj: FromCRAsUpCopyMoveType) -> str + + # std::shared_ptr + def MakeFromCRAsSpCopyMoveType(class_obj: CopyMoveType) -> FromCRAsSpCopyMoveType + def GetTraceFromCRAsSpCopyMoveType(cfa_obj: FromCRAsSpCopyMoveType) -> str diff --git a/clif/testing/python/copy_move_types_custom_from_as_clif_use.h b/clif/testing/python/copy_move_types_custom_from_as_clif_use.h new file mode 100644 index 00000000..7474fc2f --- /dev/null +++ b/clif/testing/python/copy_move_types_custom_from_as_clif_use.h @@ -0,0 +1,137 @@ +/* + * Copyright 2023 Google LLC + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef CLIF_TESTING_PYTHON_COPY_MOVE_TYPES_CUSTOM_FROM_AS_CLIF_USE_H_ +#define CLIF_TESTING_PYTHON_COPY_MOVE_TYPES_CUSTOM_FROM_AS_CLIF_USE_H_ + +#include + +#include +#include +#include + +#include "clif/python/postconv.h" +#include "clif/testing/copy_move_types_custom_from_as.h" +#include "clif/testing/py_capsule_helpers.h" + +namespace clif_testing::copy_move_types_custom_from_as { + +// This does not exercise all aspects of the type_caster move semantics, because +// `GetPayloadFromPyCapsule()` makes copies. + +// clang-format off +// CLIF use `::clif_testing::copy_move_types_custom_from_as::FromCRAsPPCopyMoveType` as FromCRAsPPCopyMoveType NOLINT +// clang-format on + +inline PyObject* Clif_PyObjFrom(const FromCRAsPPCopyMoveType& c_obj, + const ::clif::py::PostConv&) { + return py_capsule_helpers::MakePyCapsuleWithPayloadPointer( + std::make_unique(c_obj)); +} + +inline bool Clif_PyObjAs(PyObject* py_obj, FromCRAsPPCopyMoveType** c_obj) { + return py_capsule_helpers::GetPayloadPointerFromPyCapsule(py_obj, c_obj); +} + +// clang-format off +// CLIF use `::clif_testing::copy_move_types_custom_from_as::FromCRAsPCopyMoveType` as FromCRAsPCopyMoveType NOLINT +// clang-format on +inline PyObject* Clif_PyObjFrom(const FromCRAsPCopyMoveType& c_obj, + const ::clif::py::PostConv& pc) { + return py_capsule_helpers::MakePyCapsuleWithPayloadPointer( + std::make_unique(c_obj)); +} + +// See move semantics comment above. +inline bool Clif_PyObjAs(PyObject* py_obj, FromCRAsPCopyMoveType* c_obj) { + return py_capsule_helpers::GetPayloadFromPyCapsule(py_obj, c_obj); +} + +// clang-format off +// CLIF use `::clif_testing::copy_move_types_custom_from_as::FromCRAsOpCopyMoveType` as FromCRAsOpCopyMoveType NOLINT +// clang-format on +inline PyObject* Clif_PyObjFrom(const FromCRAsOpCopyMoveType& c_obj, + const ::clif::py::PostConv& pc) { + return py_capsule_helpers::MakePyCapsuleWithPayloadPointer( + std::make_unique(c_obj)); +} + +inline bool Clif_PyObjAs( + PyObject* py_obj, std::optional* c_obj) { + FromCRAsOpCopyMoveType value; + bool result = py_capsule_helpers::GetPayloadFromPyCapsule(py_obj, &value); + if (!result) { + return false; + } + *c_obj = value; + return true; +} + +// clang-format off +// CLIF use `::clif_testing::copy_move_types_custom_from_as::FromCRAsUpCopyMoveType` as FromCRAsUpCopyMoveType NOLINT +// clang-format on +inline PyObject* Clif_PyObjFrom(const FromCRAsUpCopyMoveType& c_obj, + const ::clif::py::PostConv& pc) { + return py_capsule_helpers::MakePyCapsuleWithPayloadPointer( + std::make_unique(c_obj)); +} + +// See move semantics comment above. +inline bool Clif_PyObjAs( + PyObject* py_obj, std::unique_ptr* c_obj) { + FromCRAsUpCopyMoveType value; + bool result = py_capsule_helpers::GetPayloadFromPyCapsule(py_obj, &value); + if (!result) { + return false; + } + std::unique_ptr pt = std::make_unique< + FromCRAsUpCopyMoveType>(value); + *c_obj = std::move(pt); + return true; +} + +// clang-format off +// CLIF use `::clif_testing::copy_move_types_custom_from_as::FromCRAsSpCopyMoveType` as FromCRAsSpCopyMoveType NOLINT +// clang-format on +inline PyObject* Clif_PyObjFrom(const FromCRAsSpCopyMoveType& c_obj, + const ::clif::py::PostConv& pc) { + return py_capsule_helpers::MakePyCapsuleWithPayloadPointer( + std::make_unique(c_obj)); +} + +// See move semantics comment above. +inline bool Clif_PyObjAs( + PyObject* py_obj, std::shared_ptr* c_obj) { + FromCRAsSpCopyMoveType value; + bool result = py_capsule_helpers::GetPayloadFromPyCapsule(py_obj, &value); + if (!result) { + return false; + } + std::shared_ptr pt = std::make_shared< + FromCRAsSpCopyMoveType>(value); + *c_obj = std::move(pt); + return true; +} + +// Needed by legacy CLIF. +inline bool Clif_PyObjAs( + PyObject* py_obj, FromCRAsSpCopyMoveType* c_obj) { + return py_capsule_helpers::GetPayloadFromPyCapsule(py_obj, c_obj); +} + +} // namespace clif_testing::copy_move_types_custom_from_as + +#endif // CLIF_TESTING_PYTHON_COPY_MOVE_TYPES_CUSTOM_FROM_AS_CLIF_USE_H_ diff --git a/clif/testing/python/copy_move_types_custom_from_as_test.py b/clif/testing/python/copy_move_types_custom_from_as_test.py new file mode 100644 index 00000000..7b6670b4 --- /dev/null +++ b/clif/testing/python/copy_move_types_custom_from_as_test.py @@ -0,0 +1,55 @@ +# Copyright 2023 Google LLC +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +from absl.testing import absltest + +from clif.testing.python import copy_move_types_custom_from_as as tm +from clif.testing.python import copy_move_types_library + + +class CopyMoveTypesCustomFromAsTestCase(absltest.TestCase): + + def testFromCRAsPPCopyMoveType(self): + class_obj = copy_move_types_library.CopyMoveType() + cfa_obj = tm.MakeFromCRAsPPCopyMoveType(class_obj) + trace = tm.GetTraceFromCRAsPPCopyMoveType(cfa_obj) + self.assertEqual(trace, "DefaultCtor_CpCtor_CpCtor") + + def testFromCRAsPCopyMoveType(self): + class_obj = copy_move_types_library.CopyMoveType() + cfa_obj = tm.MakeFromCRAsPCopyMoveType(class_obj) + trace = tm.GetTraceFromCRAsPCopyMoveType(cfa_obj) + self.assertRegex(trace, r"DefaultCtor_CpCtor_CpCtor_CpLhs.*") + + def testFromCRAsSOCopyMoveType(self): + class_obj = copy_move_types_library.CopyMoveType() + cfa_obj = tm.MakeFromCRAsOpCopyMoveType(class_obj) + trace = tm.GetTraceFromCRAsOpCopyMoveType(cfa_obj) + self.assertRegex(trace, r"DefaultCtor_CpCtor_CpCtor_CpLhs_CpCtor.*") + + def testFromCRAsUPCopyMoveType(self): + class_obj = copy_move_types_library.CopyMoveType() + cfa_obj = tm.MakeFromCRAsUpCopyMoveType(class_obj) + trace = tm.GetTraceFromCRAsUpCopyMoveType(cfa_obj) + self.assertRegex(trace, r"DefaultCtor_CpCtor_CpCtor_CpLhs_CpCtor.*") + + def testFromCRAsSPCopyMoveType(self): + class_obj = copy_move_types_library.CopyMoveType() + cfa_obj = tm.MakeFromCRAsSpCopyMoveType(class_obj) + trace = tm.GetTraceFromCRAsSpCopyMoveType(cfa_obj) + self.assertRegex(trace, r"DefaultCtor_CpCtor_CpCtor_CpLhs.*") + + +if __name__ == "__main__": + absltest.main() diff --git a/clif/testing/python/copy_move_types_library.clif b/clif/testing/python/copy_move_types_library.clif new file mode 100644 index 00000000..4023dc5b --- /dev/null +++ b/clif/testing/python/copy_move_types_library.clif @@ -0,0 +1,32 @@ +# Copyright 2023 Google LLC +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +from "clif/testing/copy_move_types_library.h": + namespace `clif_testing::copy_move_types_library`: + + class CopyMoveType: + def __init__(self, trace: str = default) + trace: str = property(`GetTrace`) + + class CopyOnlyType: + def __init__(self, trace: str = default) + trace: str = property(`GetTrace`) + + class MoveOnlyType: + def __init__(self, trace: str = default) + trace: str = property(`GetTrace`) + + class StayPutType: + def __init__(self, trace: str = default) + trace: str = property(`GetTrace`) diff --git a/clif/testing/python/copy_move_types_library_test.py b/clif/testing/python/copy_move_types_library_test.py new file mode 100644 index 00000000..d2f70ebf --- /dev/null +++ b/clif/testing/python/copy_move_types_library_test.py @@ -0,0 +1,42 @@ +# Copyright 2023 Google LLC +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +from absl.testing import absltest +from absl.testing import parameterized + +from clif.testing.python import copy_move_types_library as tm + +LIBRARY_TYPE_NAMES = ( + "CopyMoveType", + "CopyOnlyType", + "MoveOnlyType", + "StayPutType", +) + + +class CopyMoveTypesLibraryTestCase(parameterized.TestCase): + + @parameterized.parameters(*LIBRARY_TYPE_NAMES) + def testDefaultCtor(self, type_name): + obj = getattr(tm, type_name)() + self.assertEqual(obj.trace, "DefaultCtor") + + @parameterized.parameters(*LIBRARY_TYPE_NAMES) + def testCtorArg(self, type_name): + obj = getattr(tm, type_name)("CtorArg") + self.assertEqual(obj.trace, "CtorArg") + + +if __name__ == "__main__": + absltest.main() diff --git a/clif/testing/python/std_containers_copy_move.clif b/clif/testing/python/std_containers_copy_move.clif new file mode 100644 index 00000000..e996e624 --- /dev/null +++ b/clif/testing/python/std_containers_copy_move.clif @@ -0,0 +1,34 @@ +# Copyright 2023 Google LLC +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +from "clif/testing/python/copy_move_types_custom_from_as_clif_use.h" import * +from "clif/testing/python/copy_move_types_library_clif.h" import * + +from "clif/testing/std_containers_copy_move.h": + namespace `clif_testing::std_containers_copy_move`: + + # The bindings commented out below do not compile (with PyCLIF-C-API). + # Leaving them here for easy testing, and to highlight the known behavior. + + def PassStdVectorCopyMoveType(seq: list) -> str + # def PassStdVectorCopyOnlyType(seq: list) -> str + # def PassStdVectorMoveOnlyType(seq: list) -> str + def PassStdVectorStayPutTypePtr(seq: list) -> str + + def PassStdVectorFromCRAsPPCopyMoveType(seq: list) -> str + + def PassStdArray1CopyMoveType(seq: `StdArray1` as list) -> str + # def PassStdArray1CopyOnlyType(seq: `StdArray1` as list) -> str + # def PassStdArray1MoveOnlyType(seq: `StdArray1` as list) -> str + def PassStdArray1StayPutTypePtr(seq: `StdArray1` as list) -> str diff --git a/clif/testing/python/std_containers_copy_move_test.py b/clif/testing/python/std_containers_copy_move_test.py new file mode 100644 index 00000000..b4464ef6 --- /dev/null +++ b/clif/testing/python/std_containers_copy_move_test.py @@ -0,0 +1,74 @@ +# Copyright 2023 Google LLC +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +from absl.testing import absltest +from absl.testing import parameterized + +from clif.testing.python import copy_move_types_custom_from_as +from clif.testing.python import copy_move_types_library +from clif.testing.python import std_containers_copy_move as tm + +GEN_IX = 1 if "pybind11" in tm.__doc__ else 0 + + +class StdContainersCopyMoveTestCase(parameterized.TestCase): + + @parameterized.parameters([ + ( + "StdVector", + ["DefaultCtor_CpLhs_MvCtorTo@", "DefaultCtor_CpCtor@"][GEN_IX], + ), + ( + "StdArray1", + ["DefaultCtor_CpLhs_MvLhs@", "DefaultCtor_CpLhs_MvCtorTo@"][GEN_IX], + ), + ]) + def testPassSeqCopyMoveType(self, std_type_name, expected_traces): + obj = copy_move_types_library.CopyMoveType() + traces = getattr(tm, f"Pass{std_type_name}CopyMoveType")([obj]) + self.assertEqual(obj.trace, "DefaultCtor") + self.assertEqual(traces, expected_traces) + + @parameterized.parameters(["StdVector", "StdArray1"]) + def testPassSeqStayPutTypePtr(self, std_type_name): + obj = copy_move_types_library.StayPutType() + traces = getattr(tm, f"Pass{std_type_name}StayPutTypePtr")([obj]) + self.assertEqual(obj.trace, "DefaultCtor") + self.assertEqual(traces, "DefaultCtor@") + + def testPassStdVectorFromCRAsPPCopyMoveType(self): + class_obj = copy_move_types_library.CopyMoveType() + cfa_obj = copy_move_types_custom_from_as.MakeFromCRAsPPCopyMoveType( + class_obj + ) + self.assertEqual( + copy_move_types_custom_from_as.GetTraceFromCRAsPPCopyMoveType(cfa_obj), + "DefaultCtor_CpCtor_CpCtor", + ) + traces = tm.PassStdVectorFromCRAsPPCopyMoveType([cfa_obj]) + self.assertEqual( + copy_move_types_custom_from_as.GetTraceFromCRAsPPCopyMoveType(cfa_obj), + "DefaultCtor_CpCtor_CpCtor", + ) + self.assertEqual( + traces, + [ + "DefaultCtor_CpCtor_CpCtor_CpCtor_MvCtorTo@", + "DefaultCtor_CpCtor_CpCtor_CpCtor@", + ][GEN_IX], + ) + + +if __name__ == "__main__": + absltest.main() diff --git a/clif/testing/std_containers_copy_move.h b/clif/testing/std_containers_copy_move.h new file mode 100644 index 00000000..8f7afba3 --- /dev/null +++ b/clif/testing/std_containers_copy_move.h @@ -0,0 +1,105 @@ +/* + * Copyright 2023 Google LLC + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +#ifndef CLIF_TESTING_STD_CONTAINERS_COPY_MOVE_H_ +#define CLIF_TESTING_STD_CONTAINERS_COPY_MOVE_H_ + +#include +#include +#include + +#include "clif/testing/copy_move_types_custom_from_as.h" +#include "clif/testing/copy_move_types_library.h" + +namespace clif_testing::std_containers_copy_move { + +using copy_move_types_library::CopyMoveType; +using copy_move_types_library::CopyOnlyType; +using copy_move_types_library::MoveOnlyType; +using copy_move_types_library::StayPutType; + +using copy_move_types_custom_from_as::FromCRAsPPCopyMoveType; + +template +std::string PassSeqType(SeqType& vec) { + std::string traces; + for (const auto& it : vec) { + traces += it.GetTrace() + "@"; + } + return traces; +} + +template +std::string PassSeqTypeItemPtr(SeqType& vec) { + std::string traces; + for (const auto& it : vec) { + traces += it->GetTrace() + "@"; + } + return traces; +} + +// +// std::vector +// + +inline std::string PassStdVectorCopyMoveType( + const std::vector& seq) { + return PassSeqType(seq); +} +inline std::string PassStdVectorCopyOnlyType( + const std::vector& seq) { + return PassSeqType(seq); +} +inline std::string PassStdVectorMoveOnlyType( + const std::vector& seq) { + return PassSeqType(seq); +} +inline std::string PassStdVectorStayPutTypePtr( + const std::vector& seq) { + return PassSeqTypeItemPtr(seq); +} + +inline std::string PassStdVectorFromCRAsPPCopyMoveType( + const std::vector& seq) { + return PassSeqType(seq); +} + +// +// std::array +// + +template +using StdArray1 = std::array; + +inline std::string PassStdArray1CopyMoveType( + const StdArray1& seq) { + return PassSeqType(seq); +} +inline std::string PassStdArray1CopyOnlyType( + const StdArray1& seq) { + return PassSeqType(seq); +} +inline std::string PassStdArray1MoveOnlyType( + const StdArray1& seq) { + return PassSeqType(seq); +} +inline std::string PassStdArray1StayPutTypePtr( + const StdArray1& seq) { + return PassSeqTypeItemPtr(seq); +} + +} // namespace clif_testing::std_containers_copy_move + +#endif // CLIF_TESTING_STD_CONTAINERS_COPY_MOVE_H_