Skip to content

Commit 5349f76

Browse files
Add documentation and changelog for namespaced enum support
- Update CHANGELOG.md with new features for v0.24.0: - Support for enums with same name in different namespaces - Support for arbitrary key types in operator[] - Add comprehensive documentation to enums.pxd explaining the pattern for wrapping namespaced enums with wrap-as annotation - Add documentation to enums.hpp explaining the C++ structure - Add detailed docstring to test_enums() explaining: - How scoped enums are mapped to Python Enum classes - The pattern for handling namespace conflicts - Type-safe enum validation behavior 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com>
1 parent f89d4ca commit 5349f76

4 files changed

Lines changed: 123 additions & 6 deletions

File tree

CHANGELOG.md

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,10 @@
1+
autowrap 0.24.0 (unreleased)
2+
3+
- Support for enums with the same name in different C++ namespaces using
4+
scoped enum declarations with `wrap-as` annotation for renaming
5+
- Support for arbitrary key types in `operator[]` (getitem/setitem), not
6+
just integer types like `size_t`
7+
18
autowrap 0.23.0
29

310
Support for Cython 3.1! This means the removal of some py2 compatibility code, no more python distinction between long and int, some fixes to multiline comment processing.

tests/test_code_generator.py

Lines changed: 33 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -50,6 +50,29 @@
5050

5151

5252
def test_enums():
53+
"""
54+
Test wrapping of C++ enums, including enums with the same name in different namespaces.
55+
56+
This test demonstrates how autowrap handles:
57+
1. Scoped enums (enum class) from C++ mapped to Python Enum classes
58+
2. Enums with the same name in different namespaces (Foo::MyEnum vs Foo2::MyEnum)
59+
3. Type-safe enum validation (passing wrong enum type raises AssertionError)
60+
4. Enum documentation via wrap-doc annotation
61+
62+
Pattern for wrapping namespaced enums in .pxd files:
63+
cpdef enum class Foo_MyEnum "Foo::MyEnum":
64+
# wrap-attach:
65+
# Foo
66+
# wrap-as:
67+
# MyEnum
68+
A
69+
B
70+
C
71+
72+
This creates Foo.MyEnum in Python that maps to Foo::MyEnum in C++.
73+
74+
See tests/test_files/enums.pxd for the full example.
75+
"""
5376
if int(cython_version[0]) < 3:
5477
return
5578
target = os.path.join(test_files, "enums.pyx")
@@ -66,17 +89,26 @@ def test_enums():
6689
include_dirs,
6790
)
6891

92+
# Test 1: Enums with same name in different namespaces are both accessible
93+
# Foo.MyEnum and Foo2.MyEnum are separate enum types
6994
assert mod.Foo.MyEnum
7095
assert mod.Foo.MyEnum.B
7196
assert mod.Foo2.MyEnum
7297
assert mod.Foo2.MyEnum.D
7398

99+
# Test 2: Enum documentation is preserved via wrap-doc
74100
foo = mod.Foo()
75101
my_enum = mod.Foo.MyEnum
76102
assert "Testing Enum documentation." in my_enum.__doc__
103+
104+
# Test 3: Correct enum type is accepted
77105
myenum_a = mod.Foo.MyEnum.A
78-
myenum2_a = mod.Foo.MyEnum2.A
79106
assert foo.enumToInt(myenum_a) == 1
107+
108+
# Test 4: Wrong enum type raises AssertionError (type-safe validation)
109+
# Even though MyEnum2.A has the same numeric value as MyEnum.A,
110+
# it's a different enum type and should be rejected
111+
myenum2_a = mod.Foo.MyEnum2.A
80112
with pytest.raises(AssertionError):
81113
foo.enumToInt(myenum2_a)
82114

tests/test_files/enums.hpp

Lines changed: 30 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,16 +1,38 @@
1+
/**
2+
* =============================================================================
3+
* Example: C++ Enums in Different Namespaces
4+
* =============================================================================
5+
*
6+
* This file demonstrates C++ code with enums that have the same name but exist
7+
* in different scopes (class Foo and namespace Foo2). See enums.pxd for how to
8+
* wrap these with autowrap to avoid naming conflicts in Python.
9+
*
10+
* C++ Structure:
11+
* - Foo::MyEnum (scoped enum inside class Foo)
12+
* - Foo::MyEnum2 (second enum inside class Foo)
13+
* - Foo2::MyEnum (scoped enum inside namespace Foo2 - same name as Foo::MyEnum)
14+
*
15+
* =============================================================================
16+
*/
17+
18+
// Class with nested scoped enums
119
class Foo
220
{
321
public:
22+
// First enum - will be accessible as Foo.MyEnum in Python
423
enum class MyEnum
524
{
6-
A,B,C
25+
A, B, C
726
};
827

28+
// Second enum in same class - will be accessible as Foo.MyEnum2 in Python
929
enum class MyEnum2
1030
{
11-
A,B,C
31+
A, B, C
1232
};
1333

34+
// Method that accepts MyEnum - demonstrates type-safe enum usage
35+
// In Python, passing Foo2.MyEnum.A will raise AssertionError (wrong type)
1436
int enumToInt(MyEnum e)
1537
{
1638
switch(e)
@@ -19,14 +41,18 @@ class Foo
1941
case MyEnum::B : return 2;
2042
case MyEnum::C : return 3;
2143
}
44+
return 0; // unreachable, but silences compiler warning
2245
};
2346
};
2447

48+
// Separate namespace with an enum of the same name as Foo::MyEnum
49+
// This demonstrates the namespace collision problem that autowrap solves
2550
namespace Foo2
2651
{
27-
52+
// This enum has the same name "MyEnum" as Foo::MyEnum
53+
// In Python, it will be accessible as Foo2.MyEnum (no conflict)
2854
enum class MyEnum
2955
{
30-
A,C,D
56+
A, C, D
3157
};
3258
};

tests/test_files/enums.pxd

Lines changed: 53 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,17 +1,62 @@
11
# cython: language_level=2
2+
#
3+
# =============================================================================
4+
# Example: Wrapping C++ Enums in Different Namespaces
5+
# =============================================================================
6+
#
7+
# This file demonstrates how to wrap C++ enums that have the same name but
8+
# exist in different namespaces (e.g., Foo::MyEnum and Foo2::MyEnum).
9+
#
10+
# THE PROBLEM:
11+
# When two C++ classes/namespaces define enums with the same name, Cython
12+
# cannot distinguish them at the module level, causing naming conflicts.
13+
#
14+
# THE SOLUTION:
15+
# Use a unique Cython identifier with a C++ name string to map to the
16+
# actual C++ enum, then use wrap-as to expose it with the desired Python name.
17+
#
18+
# PATTERN:
19+
# cpdef enum class <UniqueID> "<C++::FullName>":
20+
# # wrap-attach:
21+
# # <ClassName>
22+
# # wrap-as:
23+
# # <PythonName>
24+
#
25+
# EXAMPLE:
26+
# For Foo::MyEnum and Foo2::MyEnum, declare them as:
27+
# - Foo_MyEnum "Foo::MyEnum" -> exposed as Foo.MyEnum in Python
28+
# - Foo2_MyEnum "Foo2::MyEnum" -> exposed as Foo2.MyEnum in Python
29+
#
30+
# IMPORTANT:
31+
# - Use `cpdef enum class` (not `cpdef enum`) for scoped enums
32+
# - Scoped enums generate Python Enum classes with proper type checking
33+
# - The wrap-attach directive attaches the enum to the specified class
34+
# - The wrap-as directive controls the Python-visible name
35+
#
36+
# RESULT IN PYTHON:
37+
# foo = Foo()
38+
# foo.enumToInt(Foo.MyEnum.A) # Works - correct enum type
39+
# foo.enumToInt(Foo2.MyEnum.A) # Raises AssertionError - wrong enum type
40+
#
41+
# =============================================================================
242

343
cdef extern from "enums.hpp":
444
cdef cppclass Foo:
5-
45+
# Method accepts Foo_MyEnum (which maps to Foo::MyEnum in C++)
646
int enumToInt(Foo_MyEnum e)
747

848
cdef extern from "enums.hpp":
949
cdef cppclass Foo2:
50+
# Foo2 has no methods, but we still wrap it to attach its enum
1051
pass
1152

1253

54+
# -----------------------------------------------------------------------------
55+
# Enums in the "Foo" namespace
56+
# -----------------------------------------------------------------------------
1357
cdef extern from "enums.hpp" namespace "Foo":
1458

59+
# Foo::MyEnum - attached to class Foo, exposed as Foo.MyEnum
1560
cpdef enum class Foo_MyEnum "Foo::MyEnum":
1661
# wrap-attach:
1762
# Foo
@@ -25,6 +70,7 @@ cdef extern from "enums.hpp" namespace "Foo":
2570
B
2671
C
2772

73+
# Foo::MyEnum2 - a second enum in the same class
2874
cpdef enum class Foo_MyEnum2 "Foo::MyEnum2":
2975
# wrap-attach:
3076
# Foo
@@ -35,8 +81,14 @@ cdef extern from "enums.hpp" namespace "Foo":
3581
B
3682
C
3783

84+
85+
# -----------------------------------------------------------------------------
86+
# Enums in the "Foo2" namespace
87+
# -----------------------------------------------------------------------------
3888
cdef extern from "enums.hpp" namespace "Foo2":
3989

90+
# Foo2::MyEnum - same name as Foo::MyEnum but in different namespace
91+
# Attached to Foo2 class, exposed as Foo2.MyEnum (no conflict with Foo.MyEnum)
4092
cpdef enum class Foo2_MyEnum "Foo2::MyEnum":
4193
# wrap-attach:
4294
# Foo2

0 commit comments

Comments
 (0)