-
Notifications
You must be signed in to change notification settings - Fork 2.1k
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Apply smart_holder-branch-based PR #5280 on top of master.
- Loading branch information
Showing
15 changed files
with
784 additions
and
14 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,123 @@ | ||
// Copyright (c) 2022-2025 The pybind Community. | ||
// 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 | ||
|
||
#define PYBIND11_HAS_NATIVE_ENUM | ||
|
||
#include "../pytypes.h" | ||
#include "common.h" | ||
#include "internals.h" | ||
|
||
#include <string> | ||
#include <typeindex> | ||
|
||
PYBIND11_NAMESPACE_BEGIN(PYBIND11_NAMESPACE) | ||
PYBIND11_NAMESPACE_BEGIN(detail) | ||
|
||
class native_enum_data { | ||
public: | ||
native_enum_data(const char *enum_name, | ||
const std::type_index &enum_type_index, | ||
bool use_int_enum) | ||
: enum_name_encoded{enum_name}, enum_type_index{enum_type_index}, | ||
use_int_enum{use_int_enum}, enum_name{enum_name} {} | ||
|
||
native_enum_data(const native_enum_data &) = delete; | ||
native_enum_data &operator=(const native_enum_data &) = delete; | ||
|
||
void disarm_correct_use_check() const { correct_use_check = false; } | ||
void arm_correct_use_check() const { correct_use_check = true; } | ||
|
||
// This is a separate public function only to enable easy unit testing. | ||
std::string was_not_added_error_message() const { | ||
return "`native_enum` was not added to any module." | ||
" Use e.g. `m += native_enum<...>(\"" | ||
+ enum_name_encoded + "\", ...)` to fix."; | ||
} | ||
|
||
#if !defined(NDEBUG) | ||
// This dtor cannot easily be unit tested because it terminates the process. | ||
~native_enum_data() { | ||
if (correct_use_check) { | ||
pybind11_fail(was_not_added_error_message()); | ||
} | ||
} | ||
#endif | ||
|
||
private: | ||
mutable bool correct_use_check{false}; | ||
|
||
public: | ||
std::string enum_name_encoded; | ||
std::type_index enum_type_index; | ||
bool use_int_enum; | ||
bool export_values_flag{false}; | ||
str enum_name; | ||
list members; | ||
list docs; | ||
}; | ||
|
||
inline void global_internals_native_enum_type_map_set_item(const std::type_index &enum_type_index, | ||
PyObject *py_enum) { | ||
with_internals( | ||
[&](internals &internals) { internals.native_enum_type_map[enum_type_index] = py_enum; }); | ||
} | ||
|
||
inline handle | ||
global_internals_native_enum_type_map_get_item(const std::type_index &enum_type_index) { | ||
return with_internals([&](internals &internals) { | ||
auto found = internals.native_enum_type_map.find(enum_type_index); | ||
if (found != internals.native_enum_type_map.end()) { | ||
return handle(found->second); | ||
} | ||
return handle(); | ||
}); | ||
} | ||
|
||
inline bool | ||
global_internals_native_enum_type_map_contains(const std::type_index &enum_type_index) { | ||
return with_internals([&](internals &internals) { | ||
return internals.native_enum_type_map.count(enum_type_index) != 0; | ||
}); | ||
} | ||
|
||
inline void native_enum_add_to_parent(const object &parent, const detail::native_enum_data &data) { | ||
data.disarm_correct_use_check(); | ||
if (hasattr(parent, data.enum_name)) { | ||
pybind11_fail("pybind11::native_enum<...>(\"" + data.enum_name_encoded | ||
+ "\"): an object with that name is already defined"); | ||
} | ||
auto enum_module = reinterpret_steal<object>(PyImport_ImportModule("enum")); | ||
if (!enum_module) { | ||
raise_from(PyExc_SystemError, | ||
"`import enum` FAILED at " __FILE__ ":" PYBIND11_TOSTRING(__LINE__)); | ||
throw error_already_set(); | ||
} | ||
auto py_enum_type = enum_module.attr(data.use_int_enum ? "IntEnum" : "Enum"); | ||
auto py_enum = py_enum_type(data.enum_name, data.members); | ||
object module_name = get_module_name_if_available(parent); | ||
if (module_name) { | ||
py_enum.attr("__module__") = module_name; | ||
} | ||
parent.attr(data.enum_name) = py_enum; | ||
if (data.export_values_flag) { | ||
for (auto member : data.members) { | ||
auto member_name = member[int_(0)]; | ||
if (hasattr(parent, member_name)) { | ||
pybind11_fail("pybind11::native_enum<...>(\"" + data.enum_name_encoded | ||
+ "\").value(\"" + member_name.cast<std::string>() | ||
+ "\"): an object with that name is already defined"); | ||
} | ||
parent.attr(member_name) = py_enum[member_name]; | ||
} | ||
} | ||
for (auto doc : data.docs) { | ||
py_enum[doc[int_(0)]].attr("__doc__") = doc[int_(1)]; | ||
} | ||
global_internals_native_enum_type_map_set_item(data.enum_type_index, py_enum.release().ptr()); | ||
} | ||
|
||
PYBIND11_NAMESPACE_END(detail) | ||
PYBIND11_NAMESPACE_END(PYBIND11_NAMESPACE) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,63 @@ | ||
// Copyright (c) 2022-2025 The pybind Community. | ||
// 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 "detail/common.h" | ||
#include "detail/native_enum_data.h" | ||
#include "detail/type_caster_base.h" | ||
#include "cast.h" | ||
|
||
#include <limits> | ||
#include <type_traits> | ||
#include <typeindex> | ||
|
||
PYBIND11_NAMESPACE_BEGIN(PYBIND11_NAMESPACE) | ||
|
||
enum class native_enum_kind { Enum, IntEnum }; | ||
|
||
/// Conversions between Python's native (stdlib) enum types and C++ enums. | ||
template <typename Type> | ||
class native_enum : public detail::native_enum_data { | ||
public: | ||
using Underlying = typename std::underlying_type<Type>::type; | ||
|
||
explicit native_enum(const char *name, native_enum_kind kind) | ||
: detail::native_enum_data( | ||
name, std::type_index(typeid(Type)), kind == native_enum_kind::IntEnum) { | ||
if (detail::get_local_type_info(typeid(Type)) != nullptr | ||
|| detail::get_global_type_info(typeid(Type)) != nullptr) { | ||
pybind11_fail( | ||
"pybind11::native_enum<...>(\"" + enum_name_encoded | ||
+ "\") is already registered as a `pybind11::enum_` or `pybind11::class_`!"); | ||
} | ||
if (detail::global_internals_native_enum_type_map_contains(enum_type_index)) { | ||
pybind11_fail("pybind11::native_enum<...>(\"" + enum_name_encoded | ||
+ "\") is already registered!"); | ||
} | ||
arm_correct_use_check(); | ||
} | ||
|
||
/// Export enumeration entries into the parent scope | ||
native_enum &export_values() { | ||
export_values_flag = true; | ||
return *this; | ||
} | ||
|
||
/// Add an enumeration entry | ||
native_enum &value(char const *name, Type value, const char *doc = nullptr) { | ||
disarm_correct_use_check(); | ||
members.append(make_tuple(name, static_cast<Underlying>(value))); | ||
if (doc) { | ||
docs.append(make_tuple(name, doc)); | ||
} | ||
arm_correct_use_check(); | ||
return *this; | ||
} | ||
|
||
native_enum(const native_enum &) = delete; | ||
native_enum &operator=(const native_enum &) = delete; | ||
}; | ||
|
||
PYBIND11_NAMESPACE_END(PYBIND11_NAMESPACE) |
Oops, something went wrong.