Skip to content

Commit ece5014

Browse files
committed
Use proper Julia enums for enum wrapping
1 parent 6c707b4 commit ece5014

File tree

4 files changed

+57
-7
lines changed

4 files changed

+57
-7
lines changed

Project.toml

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
name = "CxxWrap"
22
uuid = "1f15a43c-97ca-5a2a-ae31-89f07a497df4"
33
authors = ["Bart Janssens <[email protected]>"]
4-
version = "0.17.2"
4+
version = "0.17.3"
55

66
[deps]
77
Libdl = "8f399da3-3557-5675-b5ff-fb832c97cbdb"
@@ -11,7 +11,7 @@ libcxxwrap_julia_jll = "3eaa8342-bff7-56a5-9981-c04077f7cee7"
1111
[compat]
1212
MacroTools = "0.5.9"
1313
julia = "1.6"
14-
libcxxwrap_julia_jll = "0.14.0"
14+
libcxxwrap_julia_jll = "0.14.4"
1515

1616
[extras]
1717
BenchmarkTools = "6e4b80f9-dd63-53aa-95a3-0cdb28fa8baf"

README.md

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -321,7 +321,7 @@ See the test at [`examples/inheritance.cpp`](https://github.com/JuliaInterop/lib
321321

322322
## Enum types
323323

324-
Enum types are converted to strongly-typed bits types on the Julia side.
324+
Enum types are converted to standard Julia enums by calling the `@enum` macro in the backend.
325325
Consider the C++ enum:
326326

327327
```c++
@@ -337,15 +337,17 @@ This is registered as follows:
337337
```c++
338338
JLCXX_MODULE define_types_module(jlcxx::Module& types)
339339
{
340-
types.add_bits<MyEnum>("MyEnum", jlcxx::julia_type("CppEnum"));
341-
types.set_const("EnumValA", EnumValA);
342-
types.set_const("EnumValB", EnumValB);
340+
types.add_enum<MyEnum>("MyEnum",
341+
std::vector<const char*>({"EnumValA", "EnumValB"}),
342+
std::vector<int>({EnumValA, EnumValB}));
343343
}
344344
```
345345
346346
The enum constants will be available on the Julia side as `CppTypes.EnumValA` and `CppTypes.EnumValB`, both of type `CppTypes.MyEnum`.
347347
Wrapped C++ functions taking a `MyEnum` will only accept a value of type `CppTypes.MyEnum` in Julia.
348348
349+
Note that the old method (CxxWrap 0.17.2 and earlier) of adding enums using `add_bits` and `set_const` should be considered as deprecated.
350+
349351
## Template (parametric) types
350352
351353
The natural Julia equivalent of a C++ template class is the parametric type.

src/CxxWrap.jl

Lines changed: 36 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -143,7 +143,7 @@ end
143143

144144
@inline cpp_trait_type(::Type) = IsNormalType
145145

146-
# Enum type interface
146+
# Legacy enum type interface
147147
abstract type CppEnum <: Integer end
148148
(::Type{T})(x::CppEnum) where {T <: Integer} = T(reinterpret(Int32, x))::T
149149
(::Type{T})(x::Integer) where {T <: CppEnum} = reinterpret(T, Int32(x))
@@ -153,6 +153,41 @@ import Base: +, |
153153
|(a::T, b::T) where {T <: CppEnum} = reinterpret(T, convert(Int32,a) | convert(Int32,b))
154154
Base.promote_rule(::Type{E}, ::Type{T}) where {E <: CppEnum, T <: Integer} = Int32
155155

156+
Base.unsafe_string(s::Ptr{CxxChar}) = unsafe_string(reinterpret(Ptr{Cchar}, s))
157+
158+
function has_enum(wrapped_module, typesym, labels, values::Vector{BaseT}) where BaseT
159+
if !Base.invokelatest(isdefined, wrapped_module, typesym)
160+
return false
161+
end
162+
existing_type = Base.invokelatest(getproperty, wrapped_module, typesym)
163+
if (existing_type isa DataType) && (supertype(existing_type) <: Enum{BaseT})
164+
istces = instances(existing_type)
165+
return all(BaseT.(istces) .== values) && all(Symbol.(istces) .== labels)
166+
end
167+
return false
168+
end
169+
170+
# New enum wrapping
171+
function add_enum(wrapped_module, type_name, labels, values::Vector{BaseT}) where BaseT
172+
typesym = Symbol(type_name)
173+
labelsyms = Symbol.(unsafe_string.(labels))
174+
if has_enum(wrapped_module, typesym, labelsyms, values)
175+
return Base.invokelatest(getproperty, wrapped_module, typesym)
176+
end
177+
enum_block = Expr(:block)
178+
try
179+
for (label, value) in zip(labelsyms, values)
180+
push!(enum_block.args, :($label = $value))
181+
end
182+
@eval wrapped_module @enum $(typesym)::$BaseT $enum_block
183+
return Base.invokelatest(getproperty, wrapped_module, typesym)
184+
catch e
185+
@error "Error adding enum $type_name: " e
186+
display(stacktrace())
187+
return nothing
188+
end
189+
end
190+
156191
"""
157192
Base class for smart pointers
158193
"""

test/types.jl

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -197,6 +197,19 @@ end
197197
@show CppTypes.EnumClassBlue
198198
@test CppTypes.check_red(CppTypes.EnumClassRed)
199199

200+
@test CppTypes.newenum_to_int(CppTypes.EnumVal1) == 0
201+
@test CppTypes.newenum_to_int(CppTypes.EnumVal2) == 1
202+
@test CppTypes.MyEnumNew(1) == CppTypes.EnumVal2
203+
@test CppTypes.newenum_to_int(CppTypes.MyEnumNew(1)) == 1
204+
@test CppTypes.newenum_from_int(0) == CppTypes.EnumVal1
205+
@test CppTypes.newenum_from_int(1) == CppTypes.EnumVal2
206+
@test CppTypes.newenum_byref(CppTypes.EnumVal1) == 0
207+
@test CppTypes.newenum_byref(CppTypes.EnumVal2) == 1
208+
209+
@test typeof(Integer(CppTypes.zero)) == UInt64
210+
@test UInt64(CppTypes.zero) == 0
211+
@test UInt64(CppTypes.verybig) == typemax(UInt64)
212+
200213
foovec = Any[CppTypes.Foo(StdWString("a"), [1.0, 2.0, 3.0]), CppTypes.Foo(StdWString("b"), [11.0, 12.0, 13.0])] # Must be Any because of the boxing
201214
@test CppTypes.name(foovec[1]) == "a"
202215
@test CppTypes.data(foovec[1]) == [1.0, 2.0, 3.0]

0 commit comments

Comments
 (0)