JuliaInterop / Cxx.jl

The Julia C++ Interface
Other
757 stars 108 forks source link

BoundsError using icxx with string interpolation #449

Closed GlenHenshaw closed 4 years ago

GlenHenshaw commented 4 years ago

I'm using Cxx to call a C++ library (qpOASES, as it happens). This code has worked for a couple of years but recently broke.

I have the following code:

solver = @cxxnew SQProblem(7, 1)
myOptions = @cxxnew Options()
icxx"$myOptions->printLevel = PL_NONE;"
icxx"$solver->setOptions(*$myOptions);"

What this is supposed to do is create an SQProblem object, create an Options object, set some public member variables in Options, and then set a pointer inside SQProblem to the Options instance. However, as of right now, it gives me the following error instead:

Stacktrace:
 [1] getindex(::Array{Tuple{AbstractString,Symbol,Int64,Int64,Bool},1}, ::Int64) at ./array.jl:728
 [2] #s37#70 at /Users/glenhenshaw/.julia/packages/Cxx/UZsgx/src/cxxstr.jl:705 [inlined]
 [3] #s37#70(::Any, ::Any, ::Any, ::Any) at ./none:0
 [4] (::Core.GeneratedFunctionStub)(::Any, ::Vararg{Any,N} where N) at ./boot.jl:524
 [5] RMRCsetup(::Manipulator, ::Function, ::Array{Float64,1}, ::Float64, ::Float64, ::Float64, ::Float64, ::Array{Constraint,1}, ::Float64, ::Float64) at /Users/glenhenshaw/Code/RSGS/PositionControl/RMRC.jl:79
 [6] RMRCsetup at /Users/glenhenshaw/Code/RSGS/PositionControl/RMRC.jl:56 [inlined]
 [7] sam_trajectory() at /Users/glenhenshaw/Code/RSGS/PositionControl/SamTrajectory.jl:117
 [8] top-level scope at REPL[4]:1

which corresponds to execution of line 3 in the provided code snippet. This looks to me like is might be a Cxx bug (see level 2 of the stack trace).

Gnimuc commented 4 years ago

This code has worked for a couple of years but recently broke.

Could you share some versioninfo(both Julia and Cxx)? It's hard to debug such an error without further information.

From the limited info shown in the stacktrace, it looks like something goes wrong when accessing sourcebuffers, but according to https://github.com/JuliaInterop/Cxx.jl/blob/master/src/cxxstr.jl#L696 and https://github.com/JuliaInterop/Cxx.jl/blob/master/src/cxxstr.jl#L704-L705, the id is equal to the length of the sourcebuffers, which means there shouldn't be any bounds error as long as the sourcebuffers is not empty.

GlenHenshaw commented 4 years ago

Julia 1.2.0 and Cxx 0.3.3. If it matters, running on macOS 10.14.6

On Nov 26, 2019, at 8:42 PM, Gnimuc notifications@github.com wrote:

 This code has worked for a couple of years but recently broke.

Could you share some versioninfo(both Julia and Cxx)? It's hard to debug such an error without further information.

From the limited info shown in the stacktrace, it looks like something goes wrong when accessing sourcebuffers, but according to https://github.com/JuliaInterop/Cxx.jl/blob/master/src/cxxstr.jl#L696 and https://github.com/JuliaInterop/Cxx.jl/blob/master/src/cxxstr.jl#L704-L705, the id is equal to the length of the sourcebuffers, which means there shouldn't be a bounds error as long as the sourcebuffers is not empty.

— You are receiving this because you authored the thread. Reply to this email directly, view it on GitHub, or unsubscribe.

stevengj commented 4 years ago

I'm seeing a similar error with a different library (trying to call liboctave) that appears sporadically:

ERROR: BoundsError: attempt to access 36-element Array{Tuple{AbstractString,Symbol,Int64,Int64,Bool},1} at index [37]
Stacktrace:
 [1] getindex(::Array{Tuple{AbstractString,Symbol,Int64,Int64,Bool},1}, ::Int64) at ./array.jl:744
 [2] #s37#70 at /Users/stevenj/.julia/packages/Cxx/1RaOv/src/cxxstr.jl:705 [inlined]
 [3] #s37#70(::Any, ::Any, ::Any, ::Any) at ./none:0
 [4] (::Core.GeneratedFunctionStub)(::Any, ::Vararg{Any,N} where N) at ./boot.jl:524
 [5] jl2oct at /Users/stevenj/.julia/dev/OctCall/src/conversions.jl:16 [inlined]
stevengj commented 4 years ago

I found a self-contained example that exhibits the problem using only standard libraries. It only occurs in a precompiled module — if I paste the same code into the REPL in global scope it works fine. Create a module Foo with the following body:

module Foo

export foo

using Cxx

cxx"""
#include <complex>
"""

foo(x::Complex{Float64}) = icxx"std::complex<double>($(real(x)),$(imag(x)));"

end # module

then I get (on MacOS 10.15.4 with Julia 1.3):

julia> using Foo; Foo(3.0+4.0im)
[ Info: Precompiling Foo [6291f09a-a54c-4f4a-82d6-3769fa4467bb]
ERROR: BoundsError: attempt to access 36-element Array{Tuple{AbstractString,Symbol,Int64,Int64,Bool},1} at index [37]
Stacktrace:
 [1] getindex(::Array{Tuple{AbstractString,Symbol,Int64,Int64,Bool},1}, ::Int64) at ./array.jl:744
 [2] #s37#70 at /Users/stevenj/.julia/packages/Cxx/1RaOv/src/cxxstr.jl:705 [inlined]
 [3] #s37#70(::Any, ::Any, ::Any, ::Any) at ./none:0
 [4] (::Core.GeneratedFunctionStub)(::Any, ::Vararg{Any,N} where N) at ./boot.jl:524
 [5] foo(::Complex{Float64}) at /Users/stevenj/.julia/dev/Foo/src/Foo.jl:11
 [6] top-level scope at REPL[1]:1
stevengj commented 4 years ago

The problem seems to be the sourcebuffers array (https://github.com/JuliaInterop/Cxx.jl/blob/aad6940e9f31a3801bc87a8bfa6d35defbb492c3/src/cxxstr.jl#L230).

The cxx string macro appends to this global array, and expects that the appended value is present when the code is called.

In a precompiled module Foo, the sourcebuffers array is appended to during precompilation of Foo. When the Cxx module is subsequently loaded by using Foo, however, it reconstitutes the original sourcebuffers array (from when Cxx was precompiled), meaning that the entries appended by Foo are lost.

Solution: the sourcebuffers array must be local to the calling module, not to the Cxx module.

stevengj commented 4 years ago

Posted a fix.