Wrapping typedef enum with Clang.jl

If I try to wrap the following header:

typedef enum _foo {
  BAR,
  BAZ
} foo;

using the following Clang.jl script:

using Clang.wrap_c

const context = wrap_c.init(
    headers = [joinpath(@__DIR__, "header.h")],
    common_file = joinpath(@__DIR__, "wrapper.jl"),
    clang_diagnostics = true,
)

run(context)

I get the following wrapper:

# begin enum _foo
const _foo = UInt32
const BAR = 0 |> UInt32
const BAZ = 1 |> UInt32
# end enum _foo

const foo = Cvoid

I was expecting that the typedef name const foo = UInt32. Functions generated as part of this wrapper which should return an integer will instead return nothing.

Do I need to configure the wrapping process differently, or is there a problem processing typedefs?

looks like a bug, it was supposed to be const foo = _foo, but I cannot reproduce it in this PR https://github.com/ihnorton/Clang.jl/pull/210 :

ctx = DefaultContext()
trans_unit = parse_header!(ctx, "typedef.h")
top = getcursor(trans_unit)
func1 = search(top, "foo")[1]
wrap!(ctx, func1)
ctx.common_buffer[:foo].items[] # -> const foo = _foo

Thank you @Gnimuc, I have opened issue #213 to track.

Are there any significant changes that I will need to make to my wrapper script (which is obviously more complicated that the MWE above) in order to use your branch?

the wrapper script should be compatible, you could refer to this script. and you may need to include CEnum.jl and ctypes.jl in your wrapper like this. I’m still adding the tests and tweaking the code, hopefully, I can finish this in a couple of hours.

@samuelpowell almost done, you could test it now. I’ll add docs in the next few days, but feel free to ping me if things go south.

The MWE reports:

ERROR: LoadError: type WrapContext has no field InternalOptions
Stacktrace:
 [1] getproperty(::Any, ::Symbol) at ./sysimg.jl:18
 [2] run(::WrapContext) at /home/spowell/.julia/dev/Clang/src/compat.jl:92
 [3] top-level scope at none:0
 [4] include at ./boot.jl:317 [inlined]
 [5] include_relative(::Module, ::String) at ./loading.jl:1044
 [6] include(::Module, ::String) at ./sysimg.jl:29
 [7] include(::String) at ./client.jl:392
 [8] top-level scope at none:0
in expression starting at /home/spowell/spinjl/headerwrap.jl:9

My larger file seems to undergo a lot of processing but then fails with the same message. Any ideas? See below for fix.

I’m guessing line 92 of compat.jl should read:

    ctx.options["is_struct_mutable"] = wc.options.ismutable

PR here

With the above change I’m getting some good looking output, will try and test it shortly.

Note that I was previously using a cursor filter which checked for an input type of Clang.cindex.FunctionDecl in order to restrict the wrapping to specific function names. This is no longer available, but after removing the filter all together the output seems sane (previously it was wrapping a load of standard C functions).

@Gnimuc there appears to be a problem with enumeriations taking negative values. For example, if you wrap the modified MWE:

typedef enum _foo {
  BAR,
  BAZ=-9
} foo;

Then after (after including CEnum and the wrapper)

julia> foo(-9)
Error showing value of type _foo:
ERROR: Invalid enum: 4294967287, name not found
Stacktrace:
 [1] error(::String) at ./error.jl:33
 [2] enum_name at /home/spowell/spinjl/CEnum.jl:44 [inlined]
 [3] show(::IOContext{REPL.Terminals.TTYTerminal}, ::_foo) at /home/spowell/spinjl/CEnum.jl:47
 [4] show(::IOContext{REPL.Terminals.TTYTerminal}, ::MIME{Symbol("text/plain")}, ::_foo) at ./sysimg.jl:194
 [5] display(::REPL.REPLDisplay, ::MIME{Symbol("text/plain")}, ::Any) at /buildworker/worker/package_linux64/build/usr/share/julia/stdlib/v1.0/REPL/src/REPL.jl:131
 [6] display(::REPL.REPLDisplay, ::Any) at /buildworker/worker/package_linux64/build/usr/share/julia/stdlib/v1.0/REPL/src/REPL.jl:135
 [7] display(::Any) at ./multimedia.jl:287
 [8] #invokelatest#1 at ./essentials.jl:697 [inlined]
 [9] invokelatest at ./essentials.jl:696 [inlined]

(truncated)

I can hack this to work by changing the equality function in CEnum.jl to cast to a signed integer, but I don’t know the code well enough to determine if that is a good idea or not!

were you looking for wc.cursor_wrapped?

you could use using Clang.LibClang; kind(cursor) == CXCursor_FunctionDecl to determine whether a cursor is a Clang.cindex.FunctionDecl. now, using Clang.jl is just like directly using libclang, so you could always refer to libclang’s documentation for help: clang: libclang: C Interface to Clang

with this commit, CEnum should support for any integer types in Base.

https://github.com/ihnorton/Clang.jl/pull/210/commits/60f2657a3b331d321549f4fc94f0b94de5e6942c

And indeed it does! Your branch now works without any problems on my wrapper (and produces sane output). Thanks so much for your help on this. I hope having a bit of testing in the wild was worthwhile!

1 Like