From f06efcdba39aff2a1744ede72f6dace84aed9523 Mon Sep 17 00:00:00 2001 From: Dave Kleinschmidt Date: Mon, 2 Oct 2023 11:46:21 -0400 Subject: [PATCH 01/44] use `cppsize` when constructing `String` from `StdString` --- src/StdLib.jl | 2 +- test/stdlib.jl | 8 ++++++++ 2 files changed, 9 insertions(+), 1 deletion(-) diff --git a/src/StdLib.jl b/src/StdLib.jl index 0618de7..320f2e4 100644 --- a/src/StdLib.jl +++ b/src/StdLib.jl @@ -101,7 +101,7 @@ end return v end -@cxxdereference Base.String(s::StdString) = unsafe_string(reinterpret(Ptr{Cchar},c_str(s).cpp_object)) +@cxxdereference Base.String(s::StdString) = unsafe_string(reinterpret(Ptr{Cchar},c_str(s).cpp_object), cppsize(s)) @cxxdereference function Base.String(s::StdWString) chars = unsafe_wrap(Vector{Cwchar_t}, reinterpret(Ptr{Cwchar_t},c_str(s).cpp_object), (cppsize(s),)) return transcode(String, chars) diff --git a/test/stdlib.jl b/test/stdlib.jl index f632c38..a351c32 100644 --- a/test/stdlib.jl +++ b/test/stdlib.jl @@ -38,6 +38,14 @@ let s = StdString("foo") @test unsafe_string(CxxWrap.StdLib.c_str(s),2) == "fo" end +let s = "\x01\x00\x02" + @test length(StdString) == 1 + @test length(StdString(s, length(s))) == 3 + + @test String(StdString(s)) != s + @test String(StdString(s, length(s))) == s +end + stvec = StdVector(Int32[1,2,3]) @test all(stvec .== [1,2,3]) push!(stvec,1) From e84508c56d0d41e4915d9a1f9b29f92dde00398b Mon Sep 17 00:00:00 2001 From: Dave Kleinschmidt Date: Mon, 2 Oct 2023 17:07:02 -0400 Subject: [PATCH 02/44] whoops --- test/stdlib.jl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/stdlib.jl b/test/stdlib.jl index a351c32..90876aa 100644 --- a/test/stdlib.jl +++ b/test/stdlib.jl @@ -39,7 +39,7 @@ let s = StdString("foo") end let s = "\x01\x00\x02" - @test length(StdString) == 1 + @test length(StdString(s)) == 1 @test length(StdString(s, length(s))) == 3 @test String(StdString(s)) != s From 326d9f8fe68ef95863a60ef66e8e6df5777eac2d Mon Sep 17 00:00:00 2001 From: Bart Janssens Date: Mon, 2 Oct 2023 23:59:18 +0200 Subject: [PATCH 03/44] Use length argument in conversion to StdString --- src/StdLib.jl | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/StdLib.jl b/src/StdLib.jl index 320f2e4..5eb4199 100644 --- a/src/StdLib.jl +++ b/src/StdLib.jl @@ -112,9 +112,9 @@ Base.cmp(a::String, b::CppBasicString) = cmp(a,String(b)) # Make sure functions taking a C++ string as argument can also take a Julia string CxxWrapCore.map_julia_arg_type(x::Type{<:StdString}) = AbstractString -StdLib.StdStringAllocated(x::String) = StdString(x) -Base.cconvert(::Type{CxxWrapCore.ConstCxxRef{StdString}}, x::String) = StdString(x) -Base.cconvert(::Type{StdLib.StdStringDereferenced}, x::String) = StdString(x) +StdLib.StdStringAllocated(x::String) = StdString(x,length(x)) +Base.cconvert(::Type{CxxWrapCore.ConstCxxRef{StdString}}, x::String) = StdString(x,length(x)) +Base.cconvert(::Type{StdLib.StdStringDereferenced}, x::String) = StdString(x,length(x)) Base.unsafe_convert(::Type{CxxWrapCore.ConstCxxRef{StdString}}, x::StdString) = ConstCxxRef(x) function StdValArray(v::Vector{T}) where {T} From ecd527979b23730a6b30459adeed11afe9e24758 Mon Sep 17 00:00:00 2001 From: Bart Janssens Date: Wed, 4 Oct 2023 08:08:50 +0200 Subject: [PATCH 04/44] Always use ncodeunits as size argument when converting to StdString --- src/StdLib.jl | 7 ++++--- test/stdlib.jl | 4 ++-- 2 files changed, 6 insertions(+), 5 deletions(-) diff --git a/src/StdLib.jl b/src/StdLib.jl index 5eb4199..451b3f5 100644 --- a/src/StdLib.jl +++ b/src/StdLib.jl @@ -112,9 +112,10 @@ Base.cmp(a::String, b::CppBasicString) = cmp(a,String(b)) # Make sure functions taking a C++ string as argument can also take a Julia string CxxWrapCore.map_julia_arg_type(x::Type{<:StdString}) = AbstractString -StdLib.StdStringAllocated(x::String) = StdString(x,length(x)) -Base.cconvert(::Type{CxxWrapCore.ConstCxxRef{StdString}}, x::String) = StdString(x,length(x)) -Base.cconvert(::Type{StdLib.StdStringDereferenced}, x::String) = StdString(x,length(x)) +StdString(x::String) = StdString(x,ncodeunits(x)) +StdLib.StdStringAllocated(x::String) = StdString(x,ncodeunits(x)) +Base.cconvert(::Type{CxxWrapCore.ConstCxxRef{StdString}}, x::String) = StdString(x,ncodeunits(x)) +Base.cconvert(::Type{StdLib.StdStringDereferenced}, x::String) = StdString(x,ncodeunits(x)) Base.unsafe_convert(::Type{CxxWrapCore.ConstCxxRef{StdString}}, x::StdString) = ConstCxxRef(x) function StdValArray(v::Vector{T}) where {T} diff --git a/test/stdlib.jl b/test/stdlib.jl index 90876aa..c1a465b 100644 --- a/test/stdlib.jl +++ b/test/stdlib.jl @@ -39,10 +39,10 @@ let s = StdString("foo") end let s = "\x01\x00\x02" - @test length(StdString(s)) == 1 + @test length(StdString(s)) == 3 @test length(StdString(s, length(s))) == 3 - @test String(StdString(s)) != s + @test String(StdString(s)) == s @test String(StdString(s, length(s))) == s end From a1e26aaaa1b218cfa60341cbed3b1eab1c03a564 Mon Sep 17 00:00:00 2001 From: Curtis Vogt Date: Wed, 18 Oct 2023 15:09:49 -0500 Subject: [PATCH 05/44] Stop `@cxxdereference` from messing up linenum information --- src/CxxWrap.jl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/CxxWrap.jl b/src/CxxWrap.jl index b8e10ea..91a056a 100644 --- a/src/CxxWrap.jl +++ b/src/CxxWrap.jl @@ -843,7 +843,7 @@ macro cxxdereference(f) fdict[:kwargs] .= maparg.(fdict[:kwargs]) # Dereference the arguments - deref_expr = quote end + deref_expr = Expr(:block) for arg in vcat(fdict[:args], fdict[:kwargs]) (argname, _, slurp, _) = MacroTools.splitarg(arg) if argname === nothing From 81864a2bc5637808ff86a5bc2365a0a5e6766e71 Mon Sep 17 00:00:00 2001 From: Curtis Vogt Date: Sat, 21 Oct 2023 07:11:32 -0500 Subject: [PATCH 06/44] Handle UTF-8 code points in `StdString` (#381) * Improve UTF-8 support for StdString * Common support for StdWString/StdString * Fix invalid empty character literal on Julia 1.6 * Debug Windows StdWString issue * Fix StdWString tests on Windows * Support constructing of malformed chars for tests * Update iterating into invalid index * Support null-terminated constructor via Cstring * Comment on custom iterate method * Add docstring for StdString(::Any, ::Integer) * Add tests for StdString(::String, ::Integer) * Add README entry * fixup! Add README entry * fixup! Add tests for StdString(::String, ::Integer) --- README.md | 4 ++ src/StdLib.jl | 88 +++++++++++++++++++++++++++++++++++----- test/stdlib.jl | 107 ++++++++++++++++++++++++++++++++++++++++++++++--- 3 files changed, 182 insertions(+), 17 deletions(-) diff --git a/README.md b/README.md index e4f6028..b7feacf 100644 --- a/README.md +++ b/README.md @@ -902,6 +902,10 @@ mod.method("getSecondaryWorldVector", [](const World* p)->const std::vector ncodeunits(s) && return nothing + return convert(Char, codeunit(s, i)), nextind(s, i) +end + +# Since the Julia base string iteration is `String` specific we need to implement our own. +# This implementation is based around a functioning `nextind` which allows us to convert the +# UTF-8 codeunits into their big-endian encoding. +function Base.iterate(s::StdString, i::Integer=firstindex(s)) + i > ncodeunits(s) && return nothing + j = isvalid(s, i) ? nextind(s, i) : i + 1 + u = UInt32(codeunit(s, i)) << 24 + (i += 1) < j || @goto ret + u |= UInt32(codeunit(s, i)) << 16 + (i += 1) < j || @goto ret + u |= UInt32(codeunit(s, i)) << 8 + (i += 1) < j || @goto ret + u |= UInt32(codeunit(s, i)) + @label ret + return reinterpret(Char, u), j +end + +function Base.getindex(s::CppBasicString, i::Int) + checkbounds(s, i) + isvalid(s, i) || Base.string_index_err(s, i) + c, i = iterate(s, i) + return c end -Base.getindex(s::CppBasicString, i::Int) = Char(cxxgetindex(s,i)) function StdWString(s::String) char_arr = transcode(Cwchar_t, s) @@ -112,10 +136,52 @@ Base.cmp(a::String, b::CppBasicString) = cmp(a,String(b)) # Make sure functions taking a C++ string as argument can also take a Julia string CxxWrapCore.map_julia_arg_type(x::Type{<:StdString}) = AbstractString -StdString(x::String) = StdString(x,ncodeunits(x)) -StdLib.StdStringAllocated(x::String) = StdString(x,ncodeunits(x)) -Base.cconvert(::Type{CxxWrapCore.ConstCxxRef{StdString}}, x::String) = StdString(x,ncodeunits(x)) -Base.cconvert(::Type{StdLib.StdStringDereferenced}, x::String) = StdString(x,ncodeunits(x)) + +""" + StdString(str::String) + +Create a `StdString` from the contents of the string. Any null-characters ('\\0') will be +included in the string such that `ncodeunits(str) == ncodeunits(StdString(str))`. +""" +StdString(x::String) = StdString(x, ncodeunits(x)) + +""" + StdString(str::Union{Cstring, Base.CodeUnits, Vector{UInt8}, Ref{Int8}, Array{Int8}}) + +Create a `StdString` from the null-terminated character sequence. + +If you want to construct a `StdString` that includes the null-character ('\\0') either use +[`StdString(::String)`](@ref) or [`StdString(::Any, ::Int)`](@ref). + +## Examples + +```julia +julia> StdString(b"visible\\0hidden") +"visible" +``` +""" +StdString(::Union{Cstring, Base.CodeUnits, Vector{UInt8}, Ref{Int8}, Array{Int8}}) + +StdString(x::Cstring) = StdString(convert(Ptr{Int8}, x)) +StdString(x::Base.CodeUnits) = StdString(collect(x)) +StdString(x::Vector{UInt8}) = StdString(collect(reinterpret(Int8, x))) + +""" + StdString(str, n::Integer) + +Create a `StdString` from the first `n` code units of `str` (including null-characters). + +## Examples + +```julia +julia> StdString("visible\\0hidden", 10) +"visible\\0hi" +``` +""" +StdString(::Any, ::Integer) + +Base.cconvert(::Type{CxxWrapCore.ConstCxxRef{StdString}}, x::String) = StdString(x, ncodeunits(x)) +Base.cconvert(::Type{StdLib.StdStringDereferenced}, x::String) = StdString(x, ncodeunits(x)) Base.unsafe_convert(::Type{CxxWrapCore.ConstCxxRef{StdString}}, x::StdString) = ConstCxxRef(x) function StdValArray(v::Vector{T}) where {T} diff --git a/test/stdlib.jl b/test/stdlib.jl index c1a465b..01869e9 100644 --- a/test/stdlib.jl +++ b/test/stdlib.jl @@ -1,6 +1,10 @@ using CxxWrap using Test +# Can use invalid character literals (e.g. '\xa8') as of Julia 1.9: +# https://github.com/JuliaLang/julia/pull/44989 +malformed_char(x) = reinterpret(Char, UInt32(x) << 24) + @testset "$(basename(@__FILE__)[1:end-3])" begin let s = StdString("test") @@ -38,12 +42,103 @@ let s = StdString("foo") @test unsafe_string(CxxWrap.StdLib.c_str(s),2) == "fo" end -let s = "\x01\x00\x02" - @test length(StdString(s)) == 3 - @test length(StdString(s, length(s))) == 3 +let str = "\x01\x00\x02" + std_str = StdString(codeunits(str)) + @test length(std_str) == 1 + @test collect(std_str) == ['\x01'] + @test ncodeunits(std_str) == 1 + @test codeunits(std_str) == b"\x01" + + std_str = StdString(str) + @test length(std_str) == 3 + @test collect(std_str) == ['\x01', '\x00', '\x02'] + @test ncodeunits(std_str) == 3 + @test codeunits(std_str) == b"\x01\x00\x02" + + std_str = StdString(str, 2) + @test length(std_str) == 2 + @test collect(std_str) == ['\x01', '\x00'] + @test ncodeunits(std_str) == 2 + @test codeunits(std_str) == b"\x01\x00" + + std_str = convert(StdString, str) + @test length(std_str) == 3 + @test collect(std_str) == ['\x01', '\x00', '\x02'] + @test ncodeunits(std_str) == 3 + @test codeunits(std_str) == b"\x01\x00\x02" + @test convert(String, std_str) == str +end - @test String(StdString(s)) == s - @test String(StdString(s, length(s))) == s +let str = "α\0β" + std_str = StdString(codeunits(str)) + @test length(std_str) == 1 + @test collect(std_str) == ['α'] + @test ncodeunits(std_str) == 2 + @test codeunits(std_str) == b"α" + + std_str = StdString(str) + @test length(std_str) == 3 + @test collect(std_str) == ['α', '\0', 'β'] + @test ncodeunits(std_str) == 5 + @test codeunits(std_str) == b"α\0β" + + std_str = StdString(str, 4) + @test length(std_str) == 3 + @test collect(std_str) == ['α', '\0', malformed_char(0xce)] + @test ncodeunits(std_str) == 4 + @test codeunits(std_str) == b"α\0\xce" + + std_str = convert(StdString, str) + @test length(std_str) == 3 + @test collect(std_str) == ['α', '\0', 'β'] + @test ncodeunits(std_str) == 5 + @test codeunits(std_str) == b"α\0β" + @test convert(String, std_str) == str +end + +@testset "StdString" begin + @testset "null-terminated constructors" begin + c_str = Cstring(Base.unsafe_convert(Ptr{Cchar}, "visible\0hidden")) + @test StdString(c_str) == "visible" + @test StdString(b"visible\0hidden") == "visible" + @test StdString(UInt8[0xff, 0x00, 0xff]) == "\xff" + end + + @testset "iterate" begin + s = StdString("𨉟") + @test iterate(s) == ('𨉟', 5) + @test iterate(s, firstindex(s)) == ('𨉟', 5) + @test iterate(s, 2) == (malformed_char(0xa8), 3) + @test iterate(s, 3) == (malformed_char(0x89), 4) + @test iterate(s, 4) == (malformed_char(0x9f), 5) + @test iterate(s, 5) === nothing + @test iterate(s, typemax(Int)) === nothing + end + + @testset "getindex" begin + s = StdString("α") + @test getindex(s, firstindex(s)) == 'α' + @test_throws StringIndexError getindex(s, 2) + @test_throws BoundsError getindex(s, 3) + end +end + +@testset "StdWString" begin + @testset "iterate" begin + char = codeunit(StdWString()) == UInt32 ? '😄' : 'α' + s = StdWString(string(char)) + @test iterate(s) == (char, 2) + @test iterate(s, firstindex(s)) == (char, 2) + @test iterate(s, 2) === nothing + @test iterate(s, typemax(Int)) === nothing + end + + @testset "getindex" begin + char = codeunit(StdWString()) == UInt32 ? '😄' : 'α' + s = StdWString(string(char)) + @test getindex(s, firstindex(s)) == char + @test_throws BoundsError getindex(s, 2) + end end stvec = StdVector(Int32[1,2,3]) @@ -112,4 +207,4 @@ let @test length(deque2) == 1 end -end \ No newline at end of file +end From 9c0055cff6f7b929d0d41e3b4ed2c853831a3b44 Mon Sep 17 00:00:00 2001 From: Bart Janssens Date: Sat, 21 Oct 2023 14:44:58 +0200 Subject: [PATCH 07/44] Bump version to v0.14.1 (#383) --- Project.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Project.toml b/Project.toml index 3a11d30..c9fe2ac 100644 --- a/Project.toml +++ b/Project.toml @@ -1,7 +1,7 @@ name = "CxxWrap" uuid = "1f15a43c-97ca-5a2a-ae31-89f07a497df4" authors = ["Bart Janssens "] -version = "0.14.0" +version = "0.14.1" [deps] Libdl = "8f399da3-3557-5675-b5ff-fb832c97cbdb" From 05548c7d7e6b8cbea9bb1da353c94c41f2c34569 Mon Sep 17 00:00:00 2001 From: Curtis Vogt Date: Tue, 24 Oct 2023 16:06:49 -0500 Subject: [PATCH 08/44] Parameterized constructors for `StdVector` (#385) * Refactor StdVector tests * Support parameterized constructors * Support StdVector{Any} * Refactor tests * Refactor logic in StdVector(::Vector) * Empty array constructor tests * Use eltype --- src/StdLib.jl | 30 ++++----- test/stdlib.jl | 163 ++++++++++++++++++++++++++++++++++++++++--------- 2 files changed, 150 insertions(+), 43 deletions(-) diff --git a/src/StdLib.jl b/src/StdLib.jl index 541db18..5f56e37 100644 --- a/src/StdLib.jl +++ b/src/StdLib.jl @@ -74,30 +74,30 @@ function StdWString(s::String) StdWString(char_arr, length(char_arr)) end -function StdVector(v::Vector{T}) where {T} - if isempty(v) - return StdVector{T}() - end - if (CxxWrapCore.cpp_trait_type(T) == CxxWrapCore.IsCxxType) - return StdVector(CxxRef.(v)) - end +function StdVector{T}(v::Union{Vector{T},Vector{CxxRef{T}}}) where {T} result = StdVector{T}() - append(result, v) + isempty(v) || append(result, v) return result end +StdVector{T}(v::Vector) where {T} = StdVector{T}(convert(Vector{T}, v)) + function StdVector(v::Vector{CxxRef{T}}) where {T} - result = isconcretetype(T) ? StdVector{supertype(T)}() : StdVector{T}() - append(result, v) - return result + S = isconcretetype(T) ? supertype(T) : T + return StdVector{S}(v) end -function StdVector(v::Vector{Bool}) - result = StdVector{CxxBool}() - append(result, convert(Vector{CxxBool}, v)) - return result +function StdVector(v::Vector{T}) where {T} + S = if isconcretetype(T) && CxxWrapCore.cpp_trait_type(T) == CxxWrapCore.IsCxxType + supertype(T) + else + T + end + return StdVector{S}(v) end +StdVector(v::Vector{Bool}) = StdVector{CxxBool}(v) + Base.IndexStyle(::Type{<:StdVector}) = IndexLinear() Base.size(v::StdVector) = (Int(cppsize(v)),) Base.getindex(v::StdVector, i::Int) = cxxgetindex(v,i)[] diff --git a/test/stdlib.jl b/test/stdlib.jl index 01869e9..ac0fd90 100644 --- a/test/stdlib.jl +++ b/test/stdlib.jl @@ -141,34 +141,141 @@ end end end -stvec = StdVector(Int32[1,2,3]) -@test all(stvec .== [1,2,3]) -push!(stvec,1) -@test all(stvec .== [1,2,3,1]) -resize!(stvec,2) -@test all(stvec .== [1,2]) -append!(stvec,Int32[2,1]) -@test all(stvec .== [1,2,2,1]) -empty!(stvec) -@test isempty(stvec) - -@test all(StdVector([1,2,3]) .== [1,2,3]) -@test all(StdVector([1.0,2.0,3.0]) .== [1,2,3]) -@test all(StdVector([true, false, true]) .== [true, false, true]) -bvec = StdVector([true, false, true]) -append!(bvec, [true]) -@test all(bvec .== [true, false, true, true]) - -cxxstrings = StdString["one", "two", "three"] -svec = StdVector(CxxRef.(cxxstrings)) -@test all(svec .== ["one", "two", "three"]) -push!(svec, StdString("four")) -@test all(svec .== ["one", "two", "three", "four"]) -cxxappstrings = StdString["five", "six"] -append!(svec, CxxRef.(cxxappstrings)) -@test all(svec .== ["one", "two", "three", "four", "five", "six"]) -empty!(svec) -@test isempty(svec) +@testset "StdVector" begin + @testset "parameterized constructors" begin + vec = StdVector{Int}() + @test vec isa StdVector{Int} + @test isempty(vec) + + vec = StdVector{Int}([]) + @test vec isa StdVector{Int} + @test isempty(vec) + + vec = StdVector{Any}([]) + @test vec isa StdVector{Any} + @test isempty(vec) + + vec = StdVector{Int}([1,2,3]) + @test vec isa StdVector{Int} + @test vec == [1,2,3] + + vec = StdVector{Any}([1,2,3]) + @test vec isa StdVector{Any} + @test vec == [1,2,3] + + vec = StdVector{Float64}([1,2,3]) + @test vec isa StdVector{Float64} + @test vec == [1.0,2.0,3.0] + + vec = StdVector{CxxBool}([true, false, true]) + @test vec isa StdVector{CxxBool} + @test vec == [true, false, true] + + vec = StdVector{StdString}(["a", "b", "c"]) + @test vec isa StdVector{StdString} + @test vec == ["a", "b", "c"] + + svec_alloc = StdString.(["a", "b", "c"])::Vector{CxxWrap.StdLib.StdStringAllocated} + vec = StdVector{StdString}(svec_alloc) + @test vec isa StdVector{StdString} + @test vec == ["a", "b", "c"] + + svec_ref = CxxRef.(StdString["a", "b", "c"]) + vec = StdVector{StdString}(svec_ref) + @test vec isa StdVector{StdString} + @test vec == ["a", "b", "c"] + + svec_deref = getindex.(svec_ref)::Vector{CxxWrap.StdLib.StdStringDereferenced} + vec = StdVector{StdString}(svec_deref) + @test vec isa StdVector{StdString} + @test vec == ["a", "b", "c"] + + @test_throws MethodError StdVector{Bool}([true]) + @test_throws MethodError StdVector{eltype(svec_alloc)}(svec_alloc) + @test_throws MethodError StdVector{eltype(svec_deref)}(svec_deref) + end + + @testset "constructors" begin + @test_throws MethodError StdVector() + + vec = StdVector(Int[]) + @test vec isa StdVector{Int} + @test isempty(vec) + + vec = StdVector(Any[]) + @test vec isa StdVector{Any} + @test isempty(vec) + + vec = StdVector([1,2,3]) + @test vec isa StdVector{Int} + @test vec == [1,2,3] + + vec = StdVector(Any[1,2,3]) + @test vec isa StdVector{Any} + @test vec == [1,2,3] + + vec = StdVector([1.0, 2.0, 3.0]) + @test vec isa StdVector{Float64} + @test vec == [1,2,3] + + vec = StdVector([true, false, true]) + @test vec isa StdVector{CxxBool} + @test vec == [true, false, true] + + vec = StdVector(StdString["a", "b", "c"]) + @test vec isa StdVector{StdString} + @test vec == ["a", "b", "c"] + + svec_alloc = StdString.(["a", "b", "c"])::Vector{CxxWrap.StdLib.StdStringAllocated} + vec = StdVector(svec_alloc) + @test vec isa StdVector{StdString} + @test vec == ["a", "b", "c"] + + svec_ref = CxxRef.(StdString["a", "b", "c"]) + vec = StdVector(svec_ref) + @test vec isa StdVector{StdString} + @test vec == ["a", "b", "c"] + + svec_deref = getindex.(svec_ref)::Vector{CxxWrap.StdLib.StdStringDereferenced} + vec = StdVector(svec_deref) + @test vec isa StdVector{StdString} + @test vec == ["a", "b", "c"] + + @test_throws MethodError StdVector(["a", "b", "c"]) + end + + @testset "mutating with integer" begin + stvec = StdVector(Int32[1,2,3]) + @test stvec == [1,2,3] + push!(stvec,1) + @test stvec == [1,2,3,1] + resize!(stvec, 2) + @test stvec == [1,2] + append!(stvec, Int32[2,1]) + @test stvec == [1,2,2,1] + empty!(stvec) + @test isempty(stvec) + end + + @testset "mutating with bool" begin + bvec = StdVector([true, false, true]) + append!(bvec, [true]) + @test bvec == [true, false, true, true] + end + + @testset "mutating with StdString" begin + cxxstrings = StdString["one", "two", "three"] + svec = StdVector(CxxRef.(cxxstrings)) + @test svec == ["one", "two", "three"] + push!(svec, StdString("four")) + @test svec == ["one", "two", "three", "four"] + cxxappstrings = StdString["five", "six"] + append!(svec, CxxRef.(cxxappstrings)) + @test svec == ["one", "two", "three", "four", "five", "six"] + empty!(svec) + @test isempty(svec) + end +end stvec = StdVector(Int32[1,2,3]) for vref in (CxxRef(stvec), CxxPtr(stvec)) From 2e5c2ebc4d0196ac26555b996d327043da094fd3 Mon Sep 17 00:00:00 2001 From: Bart Janssens Date: Wed, 25 Oct 2023 07:39:53 +0200 Subject: [PATCH 09/44] Bump version to v0.14.2 (#387) --- Project.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Project.toml b/Project.toml index c9fe2ac..c5039e0 100644 --- a/Project.toml +++ b/Project.toml @@ -1,7 +1,7 @@ name = "CxxWrap" uuid = "1f15a43c-97ca-5a2a-ae31-89f07a497df4" authors = ["Bart Janssens "] -version = "0.14.1" +version = "0.14.2" [deps] Libdl = "8f399da3-3557-5675-b5ff-fb832c97cbdb" From 952a584838b3117261e7c5019282963b6f3a7e98 Mon Sep 17 00:00:00 2001 From: Curtis Vogt Date: Wed, 25 Oct 2023 09:21:44 -0500 Subject: [PATCH 10/44] Fix README test badge (#389) --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index b7feacf..cf4e215 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,6 @@ # CxxWrap -![test](https://github.com/JuliaInterop/CxxWrap.jl/workflows/test/badge.svg) +[![test](https://github.com/JuliaInterop/CxxWrap.jl/actions/workflows/test.yml/badge.svg?branch=main)](https://github.com/JuliaInterop/CxxWrap.jl/actions/workflows/test.yml?query=branch%3Amain) This package aims to provide a Boost.Python-like wrapping for C++ types and functions to Julia. The idea is to write the code for the Julia wrapper in C++, and then use a one-liner on the Julia side to make the wrapped C++ library available there. From a80510ad01fc5094e0e8efc0722c588904b8acb4 Mon Sep 17 00:00:00 2001 From: Benjamin Lorenz Date: Wed, 1 Nov 2023 21:08:39 +0100 Subject: [PATCH 11/44] convert RefArray: add new RefArray type signature (#390) seems to be needed for julia nightly --- src/CxxWrap.jl | 1 + 1 file changed, 1 insertion(+) diff --git a/src/CxxWrap.jl b/src/CxxWrap.jl index 91a056a..de2d5a1 100644 --- a/src/CxxWrap.jl +++ b/src/CxxWrap.jl @@ -568,6 +568,7 @@ Base.unsafe_convert(to_type::Type{<:CxxBaseRef}, v::Base.RefValue) = to_type(poi Base.cconvert(::Type{CxxPtr{CxxPtr{CxxChar}}}, v::Vector{String}) = Base.cconvert(Ptr{Ptr{Cchar}}, v) Base.unsafe_convert(to_type::Type{CxxPtr{CxxPtr{CxxChar}}}, a::Base.RefArray{Ptr{Int8}, Vector{Ptr{Int8}}, Any}) = to_type(Base.unsafe_convert(Ptr{Ptr{Cchar}}, a)) +Base.unsafe_convert(to_type::Type{CxxPtr{CxxPtr{CxxChar}}}, a::Base.RefArray{Ptr{Int8}, Vector{Ptr{Int8}}, Vector{Any}}) = to_type(Base.unsafe_convert(Ptr{Ptr{Cchar}}, a)) cxxconvert(to_type::Type{<:CxxBaseRef{T}}, x, ::Type{IsNormalType}) where {T} = Ref{T}(convert(T,x)) cxxconvert(to_type::Type{<:CxxBaseRef{T}}, x::Base.RefValue, ::Type{IsNormalType}) where {T} = x From bbddc3f4d21cb69286bbba54372bb9f7ad2a3c68 Mon Sep 17 00:00:00 2001 From: Bart Janssens Date: Thu, 16 Nov 2023 23:26:34 +0100 Subject: [PATCH 12/44] Add method to Base.between to fix string indexing (#392) --- src/StdLib.jl | 1 + 1 file changed, 1 insertion(+) diff --git a/src/StdLib.jl b/src/StdLib.jl index 5f56e37..82b849c 100644 --- a/src/StdLib.jl +++ b/src/StdLib.jl @@ -37,6 +37,7 @@ Base.codeunit(s::StdString) = UInt8 Base.codeunit(s::StdWString) = Cwchar_t == Int32 ? UInt32 : UInt16 Base.codeunit(s::CppBasicString, i::Integer) = reinterpret(codeunit(s), cxxgetindex(s,i)) Base.isvalid(s::CppBasicString, i::Int) = checkbounds(Bool, s, i) && thisind(s, i) == i +@inline Base.between(b::T1, lo::T2, hi::T2) where {T1<:Integer,T2<:Integer} = (lo ≤ b) & (b ≤ hi) Base.thisind(s::CppBasicString, i::Int) = Base._thisind_str(s, i) Base.nextind(s::CppBasicString, i::Int) = Base._nextind_str(s, i) From 3e02e6938052e11bc1f5ed60a4a8a8d8421455e1 Mon Sep 17 00:00:00 2001 From: Melven Roehrig-Zoellner Date: Wed, 20 Dec 2023 19:10:45 +0100 Subject: [PATCH 13/44] docstring support for functions --- src/CxxWrap.jl | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/src/CxxWrap.jl b/src/CxxWrap.jl index de2d5a1..59a752a 100644 --- a/src/CxxWrap.jl +++ b/src/CxxWrap.jl @@ -304,6 +304,7 @@ mutable struct CppFunctionInfo function_pointer::Ptr{Cvoid} thunk_pointer::Ptr{Cvoid} override_module::Module + doc::Any end # Interpreted as a constructor for Julia > 0.5 @@ -646,6 +647,12 @@ function build_function_expression(func::CppFunctionInfo, funcidx, julia_mod) end function_expression = :($(make_func_declaration((func.name,func.override_module), argmap(argtypes), julia_mod))::$(map_julia_return_type(func.julia_return_type)) = $call_exp) + + if func.doc != "" + function_expression = :(Core.@doc $(func.doc) $function_expression) + println(function_expression) + end + return function_expression end From 4d0c193766f67854d0521fc7a188f9c831018454 Mon Sep 17 00:00:00 2001 From: Bart Janssens Date: Thu, 11 Jan 2024 23:06:16 +0100 Subject: [PATCH 14/44] Remove debug println --- src/CxxWrap.jl | 1 - 1 file changed, 1 deletion(-) diff --git a/src/CxxWrap.jl b/src/CxxWrap.jl index 59a752a..0316770 100644 --- a/src/CxxWrap.jl +++ b/src/CxxWrap.jl @@ -650,7 +650,6 @@ function build_function_expression(func::CppFunctionInfo, funcidx, julia_mod) if func.doc != "" function_expression = :(Core.@doc $(func.doc) $function_expression) - println(function_expression) end return function_expression From e2227db6e1be8307827b5efb8f1a4e594d33445b Mon Sep 17 00:00:00 2001 From: Melven Roehrig-Zoellner Date: Mon, 8 Jan 2024 17:35:39 +0100 Subject: [PATCH 15/44] extra_arg: working arg-names and kw-args --- src/CxxWrap.jl | 19 ++++++++++++++----- test/functions.jl | 2 ++ 2 files changed, 16 insertions(+), 5 deletions(-) diff --git a/src/CxxWrap.jl b/src/CxxWrap.jl index 0316770..208d51b 100644 --- a/src/CxxWrap.jl +++ b/src/CxxWrap.jl @@ -305,6 +305,9 @@ mutable struct CppFunctionInfo thunk_pointer::Ptr{Cvoid} override_module::Module doc::Any + argument_names::Array{Any,1} + argument_default_values::Array{Any,1} + n_keyword_arguments::Any end # Interpreted as a constructor for Julia > 0.5 @@ -470,9 +473,9 @@ function process_fname(fn::CallOpOverload) return :(arg1::$(fn._type)) end -make_func_declaration(fn, argmap, julia_mod) = :($(process_fname(fn, julia_mod))($(argmap...))) -function make_func_declaration(fn::Tuple{CallOpOverload,Module}, argmap, julia_mod) - return :($(process_fname(fn, julia_mod))($((argmap[2:end])...))) +make_func_declaration(fn, argmap, kw_argmap, julia_mod) = :($(process_fname(fn, julia_mod))($(argmap...);$(kw_argmap...))) +function make_func_declaration(fn::Tuple{CallOpOverload,Module}, argmap, kw_argmap, julia_mod) + return :($(process_fname(fn, julia_mod))($((argmap[2:end])...);$(kw_argmap...))) end # By default, no argument overloading happens @@ -588,7 +591,9 @@ end function build_function_expression(func::CppFunctionInfo, funcidx, julia_mod) # Arguments and types argtypes = func.argument_types - argsymbols = map((i) -> Symbol(:arg,i[1]), enumerate(argtypes)) + argnames = func.argument_names + argsymbols = map((i) -> i[1] <= length(argnames) ? Symbol(argnames[i[1]]) : Symbol(:arg,i[1]), enumerate(argtypes)) + n_kw_args = func.n_keyword_arguments map_c_arg_type(t::Type) = map_c_arg_type(Base.invokelatest(cpp_trait_type, t), t) map_c_arg_type(::Type{IsNormalType}, t::Type) = t @@ -646,7 +651,11 @@ function build_function_expression(func::CppFunctionInfo, funcidx, julia_mod) return result end - function_expression = :($(make_func_declaration((func.name,func.override_module), argmap(argtypes), julia_mod))::$(map_julia_return_type(func.julia_return_type)) = $call_exp) + complete_argmap = argmap(argtypes) + pos_argmap = complete_argmap[1:end-n_kw_args] + kw_argmap = complete_argmap[end-n_kw_args+1:end] + + function_expression = :($(make_func_declaration((func.name,func.override_module), pos_argmap, kw_argmap, julia_mod))::$(map_julia_return_type(func.julia_return_type)) = $call_exp) if func.doc != "" function_expression = :(Core.@doc $(func.doc) $function_expression) diff --git a/test/functions.jl b/test/functions.jl index 5c2b98c..a5db102 100644 --- a/test/functions.jl +++ b/test/functions.jl @@ -73,6 +73,8 @@ end # Test functions from the CppTestFunctions module @test CppTestFunctions.concatenate_numbers(4, 2.) == "42" +@test methods(CppTestFunctions.concatenate_numbers_with_named_args)[1].slot_syms == "#self#\0i\0d\0val\0" +@test CppTestFunctions.concatenate_numbers_with_kwargs(d=2., i=4) == "42" @test hasmethod(CppTestFunctions.concatenate_numbers, (Union{Cint,CxxWrap.CxxWrapCore.argument_overloads(Cint)...},Union{Cdouble,CxxWrap.CxxWrapCore.argument_overloads(Cdouble)...})) @test CppTestFunctions.concatenate_strings(2, "ho", "la") == "holahola" @test CppTestFunctions.test_int32_array(Int32[1,2]) From 328ad85f913b9f603c4bc7abd0eab4f0382162a6 Mon Sep 17 00:00:00 2001 From: Melven Roehrig-Zoellner Date: Mon, 8 Jan 2024 22:05:16 +0100 Subject: [PATCH 16/44] extra_arg: implement default args --- src/CxxWrap.jl | 14 ++++++++++++-- test/functions.jl | 1 + 2 files changed, 13 insertions(+), 2 deletions(-) diff --git a/src/CxxWrap.jl b/src/CxxWrap.jl index 208d51b..b85e252 100644 --- a/src/CxxWrap.jl +++ b/src/CxxWrap.jl @@ -594,6 +594,7 @@ function build_function_expression(func::CppFunctionInfo, funcidx, julia_mod) argnames = func.argument_names argsymbols = map((i) -> i[1] <= length(argnames) ? Symbol(argnames[i[1]]) : Symbol(:arg,i[1]), enumerate(argtypes)) n_kw_args = func.n_keyword_arguments + arg_default_values = func.argument_default_values map_c_arg_type(t::Type) = map_c_arg_type(Base.invokelatest(cpp_trait_type, t), t) map_c_arg_type(::Type{IsNormalType}, t::Type) = t @@ -645,8 +646,17 @@ function build_function_expression(func::CppFunctionInfo, funcidx, julia_mod) # Build an array of arg1::Type1... expressions function argmap(signature) result = Expr[] - for (t, s) in zip(signature, argsymbols) - push!(result, :($s::$(map_julia_arg_type_named(func.name, t)))) + for (t, s, i) in zip(signature, argsymbols, range(1,length(signature))) + argt = map_julia_arg_type_named(func.name, t) + if isassigned(arg_default_values, i) + # somewhat strange syntax to define default argument argument... + kw = Expr(:kw) + push!(kw.args, :($s::$argt)) + push!(kw.args, arg_default_values[i]) + push!(result, kw) + else + push!(result, :($s::$argt)) + end end return result end diff --git a/test/functions.jl b/test/functions.jl index a5db102..8bf9422 100644 --- a/test/functions.jl +++ b/test/functions.jl @@ -75,6 +75,7 @@ end @test CppTestFunctions.concatenate_numbers(4, 2.) == "42" @test methods(CppTestFunctions.concatenate_numbers_with_named_args)[1].slot_syms == "#self#\0i\0d\0val\0" @test CppTestFunctions.concatenate_numbers_with_kwargs(d=2., i=4) == "42" +@test CppTestFunctions.concatenate_numbers_with_default_values(3) == "35.2" @test hasmethod(CppTestFunctions.concatenate_numbers, (Union{Cint,CxxWrap.CxxWrapCore.argument_overloads(Cint)...},Union{Cdouble,CxxWrap.CxxWrapCore.argument_overloads(Cdouble)...})) @test CppTestFunctions.concatenate_strings(2, "ho", "la") == "holahola" @test CppTestFunctions.test_int32_array(Int32[1,2]) From e245afb9ad5f9c5737662a617bc382896d427131 Mon Sep 17 00:00:00 2001 From: Melven Roehrig-Zoellner Date: Mon, 8 Jan 2024 22:31:01 +0100 Subject: [PATCH 17/44] extra_arg: improved default arguments --- src/CxxWrap.jl | 4 +++- test/functions.jl | 3 +++ 2 files changed, 6 insertions(+), 1 deletion(-) diff --git a/src/CxxWrap.jl b/src/CxxWrap.jl index b85e252..11c2544 100644 --- a/src/CxxWrap.jl +++ b/src/CxxWrap.jl @@ -652,7 +652,9 @@ function build_function_expression(func::CppFunctionInfo, funcidx, julia_mod) # somewhat strange syntax to define default argument argument... kw = Expr(:kw) push!(kw.args, :($s::$argt)) - push!(kw.args, arg_default_values[i]) + # convert to t to avoid problems on the calling site with mismatchin data types + # (e.g. f(x::Int64 = Int32(1)) = ... is not callable without argument because f(Int32) does not exist + push!(kw.args, t(arg_default_values[i])) push!(result, kw) else push!(result, :($s::$argt)) diff --git a/test/functions.jl b/test/functions.jl index 8bf9422..e83f7f5 100644 --- a/test/functions.jl +++ b/test/functions.jl @@ -76,6 +76,9 @@ end @test methods(CppTestFunctions.concatenate_numbers_with_named_args)[1].slot_syms == "#self#\0i\0d\0val\0" @test CppTestFunctions.concatenate_numbers_with_kwargs(d=2., i=4) == "42" @test CppTestFunctions.concatenate_numbers_with_default_values(3) == "35.2" +@test CppTestFunctions.concatenate_numbers_with_default_values_of_different_type(3) == "35" +@test CppTestFunctions.concatenate_numbers_with_default_kwarg(3) == "35.2" +@test CppTestFunctions.concatenate_numbers_with_default_kwarg(3, d=7) == "37" @test hasmethod(CppTestFunctions.concatenate_numbers, (Union{Cint,CxxWrap.CxxWrapCore.argument_overloads(Cint)...},Union{Cdouble,CxxWrap.CxxWrapCore.argument_overloads(Cdouble)...})) @test CppTestFunctions.concatenate_strings(2, "ho", "la") == "holahola" @test CppTestFunctions.test_int32_array(Int32[1,2]) From 2086708cbf892449d8af1eb20704c82a3a520976 Mon Sep 17 00:00:00 2001 From: Melven Roehrig-Zoellner Date: Sat, 13 Jan 2024 18:53:34 +0100 Subject: [PATCH 18/44] extra_arg: update README --- README.md | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/README.md b/README.md index cf4e215..4114f30 100644 --- a/README.md +++ b/README.md @@ -179,11 +179,11 @@ CppTypes.set(w, "hello") ``` The manually added constructor using the `constructor` function also creates a finalizer. -This can be disabled by adding the argument `false`: +This can be disabled by adding the argument `jlcxx::finalize_policy::no`: ```c++ types.add_type("World") - .constructor(false); + .constructor(jlcxx::finalize_policy::no); ``` The `add_type` function actually builds two Julia types related to `World`. @@ -648,10 +648,10 @@ The behavior of the macro can be customized by adding methods to `CxxWrap.refere ## Exceptions When directly adding a regular free C++ function as a method, it will be called directly using `ccall` and any exception will abort the Julia program. -To avoid this, you can force wrapping it in an `std::function` to intercept the exception automatically by setting the `force_convert` argument to `method` to true: +To avoid this, you can force wrapping it in an `std::function` to intercept the exception automatically by setting the `jlcxx::calling_policy` argument to `std_function`: ```c++ -mod.method("test_exception", test_exception, true); +mod.method("test_exception", test_exception, jlcxx::calling_policy::std_function); ``` Member functions and lambdas are automatically wrapped in an `std::function` and so any exceptions thrown there are always intercepted and converted to a Julia exception. From b98380bc9154a47b6c3c8d804a93d513141a745a Mon Sep 17 00:00:00 2001 From: Melven Roehrig-Zoellner Date: Tue, 16 Jan 2024 10:50:48 +0100 Subject: [PATCH 19/44] extra_arg: fix compatibility with different julia versions --- src/CxxWrap.jl | 2 +- test/functions.jl | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/CxxWrap.jl b/src/CxxWrap.jl index 11c2544..4cfea73 100644 --- a/src/CxxWrap.jl +++ b/src/CxxWrap.jl @@ -646,7 +646,7 @@ function build_function_expression(func::CppFunctionInfo, funcidx, julia_mod) # Build an array of arg1::Type1... expressions function argmap(signature) result = Expr[] - for (t, s, i) in zip(signature, argsymbols, range(1,length(signature))) + for (t, s, i) in zip(signature, argsymbols, 1:length(signature)) argt = map_julia_arg_type_named(func.name, t) if isassigned(arg_default_values, i) # somewhat strange syntax to define default argument argument... diff --git a/test/functions.jl b/test/functions.jl index e83f7f5..0dc6f5e 100644 --- a/test/functions.jl +++ b/test/functions.jl @@ -73,7 +73,7 @@ end # Test functions from the CppTestFunctions module @test CppTestFunctions.concatenate_numbers(4, 2.) == "42" -@test methods(CppTestFunctions.concatenate_numbers_with_named_args)[1].slot_syms == "#self#\0i\0d\0val\0" +@test startswith(methods(CppTestFunctions.concatenate_numbers_with_named_args)[1].slot_syms, "#self#\0i\0d\0") @test CppTestFunctions.concatenate_numbers_with_kwargs(d=2., i=4) == "42" @test CppTestFunctions.concatenate_numbers_with_default_values(3) == "35.2" @test CppTestFunctions.concatenate_numbers_with_default_values_of_different_type(3) == "35" From 2cdfcb3d455238d0eded9202cbb3cefe1124d810 Mon Sep 17 00:00:00 2001 From: Bart Janssens Date: Tue, 16 Jan 2024 21:38:35 +0100 Subject: [PATCH 20/44] Fix failing test on Julia 1.6 --- test/functions.jl | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/test/functions.jl b/test/functions.jl index 0dc6f5e..fbdfe36 100644 --- a/test/functions.jl +++ b/test/functions.jl @@ -73,7 +73,11 @@ end # Test functions from the CppTestFunctions module @test CppTestFunctions.concatenate_numbers(4, 2.) == "42" -@test startswith(methods(CppTestFunctions.concatenate_numbers_with_named_args)[1].slot_syms, "#self#\0i\0d\0") +if VERSION < v"1.7" + @test startswith(methods(CppTestFunctions.concatenate_numbers_with_named_args).ms[1].slot_syms, "#self#\0i\0d\0") +else + @test startswith(methods(CppTestFunctions.concatenate_numbers_with_named_args)[1].slot_syms, "#self#\0i\0d\0") +end @test CppTestFunctions.concatenate_numbers_with_kwargs(d=2., i=4) == "42" @test CppTestFunctions.concatenate_numbers_with_default_values(3) == "35.2" @test CppTestFunctions.concatenate_numbers_with_default_values_of_different_type(3) == "35" From 447cca22a59dd7f795ef39014fbf7340d1c044a1 Mon Sep 17 00:00:00 2001 From: Bart Janssens Date: Wed, 17 Jan 2024 08:42:45 +0100 Subject: [PATCH 21/44] Set version to v0.15.0 --- Project.toml | 4 ++-- src/CxxWrap.jl | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/Project.toml b/Project.toml index c5039e0..f41046e 100644 --- a/Project.toml +++ b/Project.toml @@ -1,7 +1,7 @@ name = "CxxWrap" uuid = "1f15a43c-97ca-5a2a-ae31-89f07a497df4" authors = ["Bart Janssens "] -version = "0.14.2" +version = "0.15.0" [deps] Libdl = "8f399da3-3557-5675-b5ff-fb832c97cbdb" @@ -11,7 +11,7 @@ libcxxwrap_julia_jll = "3eaa8342-bff7-56a5-9981-c04077f7cee7" [compat] MacroTools = "0.5.9" julia = "1.6" -libcxxwrap_julia_jll = "0.11.0" +libcxxwrap_julia_jll = "0.12.0" [extras] BenchmarkTools = "6e4b80f9-dd63-53aa-95a3-0cdb28fa8baf" diff --git a/src/CxxWrap.jl b/src/CxxWrap.jl index 4cfea73..c5b5b63 100644 --- a/src/CxxWrap.jl +++ b/src/CxxWrap.jl @@ -10,7 +10,7 @@ ConstCxxPtr, ConstCxxRef, CxxRef, CxxPtr, CppEnum, ConstArray, CxxBool, CxxLong, CxxULong, CxxChar, CxxChar16, CxxChar32, CxxWchar, CxxUChar, CxxSignedChar, CxxLongLong, CxxULongLong, ptrunion, gcprotect, gcunprotect, isnull, libcxxwrapversion -const libcxxwrap_version_range = (v"0.11.0", v"0.12") +const libcxxwrap_version_range = (v"0.12.0", v"0.13") using libcxxwrap_julia_jll # for libcxxwrap_julia and libcxxwrap_julia_stl From 166996cd4375e95b298ded507ea7fe241e0ab362 Mon Sep 17 00:00:00 2001 From: Bart Janssens Date: Thu, 8 Feb 2024 22:02:48 +0100 Subject: [PATCH 22/44] Properly deal with const template parameters Issue #405 --- src/CxxWrap.jl | 51 ++++++++++++++++++++++++++++++++++++++++---------- test/types.jl | 11 +++++------ 2 files changed, 46 insertions(+), 16 deletions(-) diff --git a/src/CxxWrap.jl b/src/CxxWrap.jl index c5b5b63..24ca494 100644 --- a/src/CxxWrap.jl +++ b/src/CxxWrap.jl @@ -129,6 +129,10 @@ Base.flipsign(x::T, y::T) where {T <: CxxSigned} = reinterpret(T, flipsign(to_ju struct IsCxxType end struct IsNormalType end +struct CxxConst{T} + cpp_object::Ptr{T} +end + @inline cpp_trait_type(::Type) = IsNormalType # Enum type interface @@ -152,29 +156,48 @@ Base.show(io::IO, x::SmartPointer) = print(io, "C++ smart pointer of type ", typ allocated_type(t::Type) = Any dereferenced_type(t::Type) = Any -__cxxwrap_smartptr_dereference(p::SmartPointer{T}) where {T} = __cxxwrap_smartptr_dereference(CxxRef(p)) -__cxxwrap_smartptr_construct_from_other(t::Type{<:SmartPointer{T}}, p::SmartPointer{T}) where {T} = __cxxwrap_smartptr_construct_from_other(t,CxxRef(p)) -__cxxwrap_smartptr_cast_to_base(p::SmartPointer{T}) where {T} = __cxxwrap_smartptr_cast_to_base(CxxRef(p)) +function __cxxwrap_smartptr_dereference end +function __cxxwrap_smartptr_construct_from_other end +function __cxxwrap_smartptr_cast_to_base end +function __cxxwrap_make_const_smartptr end function Base.getindex(p::SmartPointer{T}) where {T} - return __cxxwrap_smartptr_dereference(p) + return __cxxwrap_smartptr_dereference(CxxRef(p)) end # No conversion if source and target type are identical Base.convert(::Type{T}, p::T) where {PT,T <: SmartPointer{PT}} = p +# Conversion from non-const to const +Base.convert(::Type{CT}, p::T) where {PT,T <: SmartPointer{PT},CT <: SmartPointer{CxxConst{PT}}} = __cxxwrap_make_const_smartptr(ConstCxxRef(p)) + # Construct from a related pointer, e.g. a std::weak_ptr from std::shared_ptr function Base.convert(::Type{T1}, p::SmartPointer{T}) where {T, T1 <: SmartPointer{T}} - return __cxxwrap_smartptr_construct_from_other(T1, p) + return __cxxwrap_smartptr_construct_from_other(T1, CxxRef(p)) +end + +# Construct from a related pointer, e.g. a std::weak_ptr from std::shared_ptr. Const versions +function Base.convert(::Type{T1}, p::SmartPointer{CxxConst{T}}) where {T, T1 <: SmartPointer{CxxConst{T}}} + return __cxxwrap_smartptr_construct_from_other(T1, CxxRef(p)) +end +# Avoid improper method resolution on Julia < 1.10 +function Base.convert(::Type{T1}, p::T1) where {T, T1 <: SmartPointer{CxxConst{T}}} + return p end -# upcast to base class -function Base.convert(::Type{T1}, p::SmartPointer{<:BaseT}) where {BaseT, T1 <: SmartPointer{BaseT}} +function _base_convert_impl(::Type{T1}, p) where{T1} # First convert to base type - base_p = __cxxwrap_smartptr_cast_to_base(p) + base_p = __cxxwrap_smartptr_cast_to_base(ConstCxxRef(p)) return convert(T1, base_p) end +# upcast to base class, non-const version +Base.convert(::Type{T1}, p::SmartPointer{<:BaseT}) where {BaseT, T1 <: SmartPointer{BaseT}} = _base_convert_impl(T1, p) +# upcast to base class, non-const to const version +Base.convert(::Type{T1}, p::SmartPointer{<:BaseT}) where {BaseT, T1 <: SmartPointer{CxxConst{BaseT}}} = _base_convert_impl(T1, p) +# upcast to base, const version +Base.convert(::Type{T1}, p::SmartPointer{CxxConst{SuperT}}) where {BaseT, SuperT<:BaseT, T1<:SmartPointer{CxxConst{BaseT}}} = _base_convert_impl(T1, p) + struct StrictlyTypedNumber{NumberT} value::NumberT @static if Sys.iswindows() @@ -362,7 +385,7 @@ function _register_function_pointers(func, precompiling) if haskey(__global_method_map, mkey) existing = __global_method_map[mkey] if existing[3] == precompiling - error("Double registration for method $mkey") + error("Double registration for method $mkey: $(func.name); $(func.argument_types); $(func.return_type)") end end __global_method_map[mkey] = fptrs @@ -502,6 +525,7 @@ function ptrunion(::Type{T}) where {T} return result end +# valuetype is the non-reference, non-pointer type that is used in the method argument list. Overloading this allows mapping these types to something more useful valuetype(t::Type) = valuetype(Base.invokelatest(cpp_trait_type,t), t) valuetype(::Type{IsNormalType}, ::Type{T}) where {T} = T function valuetype(::Type{IsCxxType}, ::Type{T}) where {T} @@ -511,10 +535,16 @@ function valuetype(::Type{IsCxxType}, ::Type{T}) where {T} end return T end +# Smart pointer arguments can also take subclass arguments function valuetype(::Type{<:SmartPointer{T}}) where {T} result{T2 <: T} = SmartPointer{T2} return result end +# Smart pointers with const arguments can also take both const and non-const subclass arguments +function valuetype(::Type{<:SmartPointer{CxxConst{T}}}) where {T} + result{T2 <: T} = Union{SmartPointer{T2},SmartPointer{CxxConst{T2}}} + return result +end map_julia_arg_type(t::Type) = Union{valuetype(t), argument_overloads(t)...} map_julia_arg_type(a::Type{StrictlyTypedNumber{T}}) where {T} = T @@ -555,7 +585,8 @@ const __excluded_names = Set([ :cxxupcast, :__cxxwrap_smartptr_dereference, :__cxxwrap_smartptr_construct_from_other, - :__cxxwrap_smartptr_cast_to_base + :__cxxwrap_smartptr_cast_to_base, + :__cxxwrap_make_const_smartptr, ]) function Base.cconvert(to_type::Type{<:CxxBaseRef{T}}, x) where {T} diff --git a/test/types.jl b/test/types.jl index 51684f7..00a0978 100644 --- a/test/types.jl +++ b/test/types.jl @@ -160,12 +160,6 @@ w_copy = copy(w) # Destroy w: w and w_assigned should be dead, w_copy alive finalize(w) -#finalize(w_lambda) -if !(Sys.iswindows() && Sys.WORD_SIZE == 32) - @test_throws ErrorException CppTypes.greet(w) - @test_throws ErrorException CppTypes.greet(w_assigned) - #@test_throws ErrorException CppTypes.greet(w_lambda) -end @test CppTypes.greet(w_copy) == "constructed" println("completed copy test") @@ -299,3 +293,8 @@ let cd1 = CppTypes.UseCustomDelete(), cd2 = CppTypes.UseCustomClassDelete() finalize(cd2) @test CppTypes.get_custom_class_nb_deletes() == 1 end + +let v = CppTypes.shared_vector_factory(), cv = CppTypes.shared_const_vector_factory() + @test CppTypes.get_shared_vector_msg(v) == "shared vector hello" + @test CppTypes.get_shared_vector_msg(cv) == "shared vector const hello from const overload" +end \ No newline at end of file From 63daec0a201f8f1f595e5ae11914caaba1fea6c7 Mon Sep 17 00:00:00 2001 From: Bart Janssens Date: Thu, 7 Mar 2024 17:57:11 +0100 Subject: [PATCH 23/44] Fix tests --- src/CxxWrap.jl | 2 +- test/basic_types.jl | 12 +++++++++++- test/types.jl | 6 +++++- 3 files changed, 17 insertions(+), 3 deletions(-) diff --git a/src/CxxWrap.jl b/src/CxxWrap.jl index 24ca494..eb53fd3 100644 --- a/src/CxxWrap.jl +++ b/src/CxxWrap.jl @@ -10,7 +10,7 @@ ConstCxxPtr, ConstCxxRef, CxxRef, CxxPtr, CppEnum, ConstArray, CxxBool, CxxLong, CxxULong, CxxChar, CxxChar16, CxxChar32, CxxWchar, CxxUChar, CxxSignedChar, CxxLongLong, CxxULongLong, ptrunion, gcprotect, gcunprotect, isnull, libcxxwrapversion -const libcxxwrap_version_range = (v"0.12.0", v"0.13") +const libcxxwrap_version_range = (v"0.12.1", v"0.13") using libcxxwrap_julia_jll # for libcxxwrap_julia and libcxxwrap_julia_stl diff --git a/test/basic_types.jl b/test/basic_types.jl index 61f4d52..3c72ecb 100644 --- a/test/basic_types.jl +++ b/test/basic_types.jl @@ -34,9 +34,19 @@ end @testset "$(basename(@__FILE__)[1:end-3])" begin +function compare_collections(a, b) + equalities = a .== b + result = all(equalities) + if !result + neqs = (!).(equalities) + println("collections differ: $(a[neqs]) ≠ $(b[neqs])") + end + return result +end + let funcs = CxxWrap.CxxWrapCore.get_module_functions(CxxWrap.StdLib) @test CxxWrap.StdLib.__cxxwrap_methodkeys[1] == CxxWrap.CxxWrapCore.methodkey(funcs[1]) - @test all(CxxWrap.StdLib.__cxxwrap_methodkeys .== CxxWrap.CxxWrapCore.methodkey.(funcs)) + @test compare_collections(CxxWrap.StdLib.__cxxwrap_methodkeys, CxxWrap.CxxWrapCore.methodkey.(funcs)) end let a = BasicTypes.A(2,3) diff --git a/test/types.jl b/test/types.jl index 00a0978..4b53caf 100644 --- a/test/types.jl +++ b/test/types.jl @@ -235,7 +235,11 @@ empty!(warr1) @test bench_greet() == 1000*length(CppTypes.greet(CppTypes.World())) _, _, _, _, memallocs = @timed bench_greet() -@test 0 < memallocs.poolalloc < 100 +@show memallocs.poolalloc +@test 0 < memallocs.poolalloc < 400 # Jumped from +/- 6 to 360 in Julia 1.12 +if memallocs.poolalloc > 100 + @warn "Abnormally high number of allocations: $(memallocs.poolalloc)" +end if isdefined(CppTypes, :IntDerived) Base.promote_rule(::Type{<:CppTypes.IntDerived}, ::Type{<:Number}) = Int From eb8c2e6da4292f452c3e18678ee4954d935e8a13 Mon Sep 17 00:00:00 2001 From: Bart Janssens Date: Thu, 7 Mar 2024 19:53:09 +0100 Subject: [PATCH 24/44] Bump required libcxxwrap_julia version --- Project.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Project.toml b/Project.toml index f41046e..040e832 100644 --- a/Project.toml +++ b/Project.toml @@ -11,7 +11,7 @@ libcxxwrap_julia_jll = "3eaa8342-bff7-56a5-9981-c04077f7cee7" [compat] MacroTools = "0.5.9" julia = "1.6" -libcxxwrap_julia_jll = "0.12.0" +libcxxwrap_julia_jll = "0.12.1" [extras] BenchmarkTools = "6e4b80f9-dd63-53aa-95a3-0cdb28fa8baf" From cd7629617e866daa8ad924c54078afa9fc6c9ff2 Mon Sep 17 00:00:00 2001 From: PraneethJain Date: Sat, 24 Feb 2024 17:52:25 +0530 Subject: [PATCH 25/44] feat: add basic queue support --- src/CxxWrap.jl | 4 ++-- src/StdLib.jl | 5 +++++ 2 files changed, 7 insertions(+), 2 deletions(-) diff --git a/src/CxxWrap.jl b/src/CxxWrap.jl index eb53fd3..c18949d 100644 --- a/src/CxxWrap.jl +++ b/src/CxxWrap.jl @@ -935,8 +935,8 @@ ConstCxxPtr, ConstCxxRef, CxxRef, CxxPtr, CppEnum, ConstArray, CxxBool, CxxLong, CxxULong, CxxChar, CxxChar16, CxxChar32, CxxWchar, CxxUChar, CxxSignedChar, CxxLongLong, CxxULongLong, ptrunion, gcprotect, gcunprotect, isnull -using .StdLib: StdVector, StdString, StdWString, StdValArray, StdThread, StdDeque +using .StdLib: StdVector, StdString, StdWString, StdValArray, StdThread, StdDeque, StdQueue -export StdLib, StdVector, StdString, StdWString, StdValArray, StdThread, StdDeque +export StdLib, StdVector, StdString, StdWString, StdValArray, StdThread, StdDeque, StdQueue end # module diff --git a/src/StdLib.jl b/src/StdLib.jl index 82b849c..d77afae 100644 --- a/src/StdLib.jl +++ b/src/StdLib.jl @@ -207,4 +207,9 @@ Base.pushfirst!(v::StdDeque, x) = push_front!(v, x) Base.pop!(v::StdDeque) = pop_back!(v) Base.popfirst!(v::StdDeque) = pop_front!(v) Base.resize!(v::StdDeque, n::Integer) = resize!(v, n) + +Base.size(v::StdQueue) = (Int(cppsize(v)),) +Base.push!(v::StdQueue, x) = push_back!(v, x) +Base.first(v::StdQueue) = front(v) +Base.pop!(v::StdQueue) = pop_front!(v) end From 4503f7522a0016f8c4dac019a2e001317530131a Mon Sep 17 00:00:00 2001 From: PraneethJain Date: Sat, 24 Feb 2024 18:06:31 +0530 Subject: [PATCH 26/44] test: queue --- test/stdlib.jl | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/test/stdlib.jl b/test/stdlib.jl index ac0fd90..91676b5 100644 --- a/test/stdlib.jl +++ b/test/stdlib.jl @@ -314,4 +314,17 @@ let @test length(deque2) == 1 end +let + @show "test queue" + queue = StdQueue{Int64}() + @test length(queue) == 0 + push!(queue, 10) + push!(queue, 20) + @test length(queue) == 2 + @test first(queue) == 10 + pop!(queue) + @test first(queue) == 20 + @test length(queue) == 1 +end + end From c8da17bc88f3410e1586b70b66a4c1180122a45d Mon Sep 17 00:00:00 2001 From: PraneethJain Date: Mon, 4 Mar 2024 14:38:21 +0530 Subject: [PATCH 27/44] testing against correct version of libcxxwrap --- .github/workflows/test.yml | 23 ++++++++++++++++++----- 1 file changed, 18 insertions(+), 5 deletions(-) diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 904058f..ef315ad 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -20,21 +20,34 @@ jobs: matrix: version: - '1.6' - - '1.9' - - 'nightly' + - '1.10' os: - macOS-latest - - windows-latest - ubuntu-latest arch: - x64 steps: - - uses: actions/checkout@v2 + - uses: actions/checkout@v4 + with: + fetch-depth: 0 - uses: julia-actions/setup-julia@latest with: version: ${{ matrix.version }} arch: ${{ matrix.arch }} - - run: julia -e "using Pkg; Pkg.status(); Pkg.Registry.add(RegistrySpec(url = \"https://github.com/barche/CxxWrapTestRegistry.git\"))" + - run: | + mkdir libcxxwrap && cd libcxxwrap + body="${{github.event.pull_request.body}}" + echo $body + read -r repo_url + read -r branch + git clone --branch $branch --single-branch $repo_url . . + if [[ "$OSTYPE" != "darwin"* ]]; then + rm -f /opt/hostedtoolcache/julia/1.6*/x64/lib/julia/libstdc++.so.6 + fi + mkdir build && cd build + cmake -DCMAKE_INSTALL_PREFIX=$HOME/install -DAPPEND_OVERRIDES_TOML=ON -DOVERRIDE_VERSION_TO_JLL=ON -DCMAKE_BUILD_TYPE=Debug .. + VERBOSE=ON cmake --build . --config Debug --target install + cd ../.. - uses: julia-actions/julia-buildpkg@latest - uses: julia-actions/julia-runtest@latest From 47b1d2288a15bee33ee87f40632c6774dd4bc914 Mon Sep 17 00:00:00 2001 From: PraneethJain Date: Mon, 4 Mar 2024 14:41:44 +0530 Subject: [PATCH 28/44] fix: input from body --- .github/workflows/test.yml | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index ef315ad..01196cc 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -38,9 +38,17 @@ jobs: mkdir libcxxwrap && cd libcxxwrap body="${{github.event.pull_request.body}}" echo $body - read -r repo_url - read -r branch + while IFS= read -r line || [ -n "$line" ]; do + if [ -z "$line1" ]; then + repo_url="$line" + else + branch="$line" + fi + done <<< "$content" + echo $repo_url + echo $branch git clone --branch $branch --single-branch $repo_url . . + echo "done cloning" if [[ "$OSTYPE" != "darwin"* ]]; then rm -f /opt/hostedtoolcache/julia/1.6*/x64/lib/julia/libstdc++.so.6 fi From 3e0d88152867d3f43cdde449edfd21bb186ee415 Mon Sep 17 00:00:00 2001 From: PraneethJain Date: Mon, 4 Mar 2024 14:47:14 +0530 Subject: [PATCH 29/44] fix splitting --- .github/workflows/test.yml | 14 +++++--------- 1 file changed, 5 insertions(+), 9 deletions(-) diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 01196cc..8835ce5 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -36,18 +36,14 @@ jobs: arch: ${{ matrix.arch }} - run: | mkdir libcxxwrap && cd libcxxwrap + body="${{github.event.pull_request.body}}" - echo $body - while IFS= read -r line || [ -n "$line" ]; do - if [ -z "$line1" ]; then - repo_url="$line" - else - branch="$line" - fi - done <<< "$content" + repo_url=$(echo "$body" | sed -n '1p') + branch=$(echo "$body" | sed -n '2p') echo $repo_url echo $branch - git clone --branch $branch --single-branch $repo_url . . + + git clone --branch $branch --single-branch $repo_url . echo "done cloning" if [[ "$OSTYPE" != "darwin"* ]]; then rm -f /opt/hostedtoolcache/julia/1.6*/x64/lib/julia/libstdc++.so.6 From d55189279e6564c1d700fa266541fd103cfd520f Mon Sep 17 00:00:00 2001 From: PraneethJain Date: Mon, 4 Mar 2024 15:03:38 +0530 Subject: [PATCH 30/44] temp --- .github/workflows/test.yml | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 8835ce5..035bb40 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -34,7 +34,8 @@ jobs: with: version: ${{ matrix.version }} arch: ${{ matrix.arch }} - - run: | + - name: Build libcxxwrap + run: | mkdir libcxxwrap && cd libcxxwrap body="${{github.event.pull_request.body}}" @@ -42,14 +43,14 @@ jobs: branch=$(echo "$body" | sed -n '2p') echo $repo_url echo $branch - git clone --branch $branch --single-branch $repo_url . echo "done cloning" + if [[ "$OSTYPE" != "darwin"* ]]; then rm -f /opt/hostedtoolcache/julia/1.6*/x64/lib/julia/libstdc++.so.6 fi mkdir build && cd build - cmake -DCMAKE_INSTALL_PREFIX=$HOME/install -DAPPEND_OVERRIDES_TOML=ON -DOVERRIDE_VERSION_TO_JLL=ON -DCMAKE_BUILD_TYPE=Debug .. + cmake -DCMAKE_INSTALL_PREFIX=$HOME/install -DAPPEND_OVERRIDES_TOML=ON -DOVERRIDE_VERSION_TO_JLL=OFF -DCMAKE_BUILD_TYPE=Debug .. VERBOSE=ON cmake --build . --config Debug --target install cd ../.. - uses: julia-actions/julia-buildpkg@latest From a310b0ac3da8a8af406146c277152ee0f6996f44 Mon Sep 17 00:00:00 2001 From: PraneethJain Date: Mon, 4 Mar 2024 15:21:12 +0530 Subject: [PATCH 31/44] feat: separate testing workflows --- .../{test.yml => test-linux-mac.yml} | 2 +- .github/workflows/test-win.yml | 64 +++++++++++++++++++ 2 files changed, 65 insertions(+), 1 deletion(-) rename .github/workflows/{test.yml => test-linux-mac.yml} (98%) create mode 100644 .github/workflows/test-win.yml diff --git a/.github/workflows/test.yml b/.github/workflows/test-linux-mac.yml similarity index 98% rename from .github/workflows/test.yml rename to .github/workflows/test-linux-mac.yml index 035bb40..877ed69 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test-linux-mac.yml @@ -1,4 +1,4 @@ -name: test +name: test-linux-mac on: push: branches-ignore: diff --git a/.github/workflows/test-win.yml b/.github/workflows/test-win.yml new file mode 100644 index 0000000..cb41f6a --- /dev/null +++ b/.github/workflows/test-win.yml @@ -0,0 +1,64 @@ +name: test-win +on: + push: + branches-ignore: + - prerelease + pull_request: + branches-ignore: + - prerelease + +defaults: + run: + shell: bash + +jobs: + test: + name: Julia ${{ matrix.version }} - ${{ matrix.os }} - ${{ matrix.arch }} + runs-on: ${{ matrix.os }} + strategy: + fail-fast: false + matrix: + version: + - '1.6' + - '1.10' + os: + - windows-latest + arch: + - x64 + steps: + - uses: actions/checkout@v4 + with: + fetch-depth: 0 + - uses: julia-actions/setup-julia@latest + with: + version: ${{ matrix.version }} + arch: ${{ matrix.arch }} + - name: Clone libcxxwrap + run: | + mkdir libcxxwrap && cd libcxxwrap + + body="${{github.event.pull_request.body}}" + repo_url=$(echo "$body" | sed -n '1p') + branch=$(echo "$body" | sed -n '2p') + echo $repo_url + echo $branch + git clone --branch $branch --single-branch $repo_url . + echo "done cloning" + - name: Config 32bit + if: ${{ matrix.arch == 'x86'}} + run: | + cd libcxxwrap && mkdir build && cd build + cmake -G "Visual Studio 17 2022" -A Win32 -DOVERRIDES_PATH=$HOMEDRIVE/$HOMEPATH/.julia/artifacts/Overrides.toml -DOVERRIDE_ROOT=./ -DAPPEND_OVERRIDES_TOML=ON .. + - name: Config 64bit + if: ${{ matrix.arch == 'x64'}} + run: | + cd libcxxwrap mkdir build && cd build + cmake -G "Visual Studio 17 2022" -A x64 -DOVERRIDES_PATH=$HOMEDRIVE/$HOMEPATH/.julia/artifacts/Overrides.toml -DOVERRIDE_ROOT=./ -DAPPEND_OVERRIDES_TOML=ON .. + - name: Build libcxxwrap + run: | + cd libcxxwrap/build + cmake --build . --config Release + + - uses: julia-actions/julia-buildpkg@latest + - uses: julia-actions/julia-runtest@latest + From 11e1408c1a7da305d85674ac4d8c6f75f6bf7b69 Mon Sep 17 00:00:00 2001 From: PraneethJain Date: Mon, 4 Mar 2024 15:23:03 +0530 Subject: [PATCH 32/44] fix: typo --- .github/workflows/test-win.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/test-win.yml b/.github/workflows/test-win.yml index cb41f6a..0c48f2f 100644 --- a/.github/workflows/test-win.yml +++ b/.github/workflows/test-win.yml @@ -52,7 +52,7 @@ jobs: - name: Config 64bit if: ${{ matrix.arch == 'x64'}} run: | - cd libcxxwrap mkdir build && cd build + cd libcxxwrap && mkdir build && cd build cmake -G "Visual Studio 17 2022" -A x64 -DOVERRIDES_PATH=$HOMEDRIVE/$HOMEPATH/.julia/artifacts/Overrides.toml -DOVERRIDE_ROOT=./ -DAPPEND_OVERRIDES_TOML=ON .. - name: Build libcxxwrap run: | From 197a4b74f63a7c94a201dfbe26c8325e5eda70fd Mon Sep 17 00:00:00 2001 From: PraneethJain Date: Mon, 4 Mar 2024 15:34:00 +0530 Subject: [PATCH 33/44] test: add nightly --- .github/workflows/test-linux-mac.yml | 1 + .github/workflows/test-win.yml | 1 + 2 files changed, 2 insertions(+) diff --git a/.github/workflows/test-linux-mac.yml b/.github/workflows/test-linux-mac.yml index 877ed69..0e5438d 100644 --- a/.github/workflows/test-linux-mac.yml +++ b/.github/workflows/test-linux-mac.yml @@ -21,6 +21,7 @@ jobs: version: - '1.6' - '1.10' + - 'nightly' os: - macOS-latest - ubuntu-latest diff --git a/.github/workflows/test-win.yml b/.github/workflows/test-win.yml index 0c48f2f..586782d 100644 --- a/.github/workflows/test-win.yml +++ b/.github/workflows/test-win.yml @@ -21,6 +21,7 @@ jobs: version: - '1.6' - '1.10' + - 'nightly' os: - windows-latest arch: From 985c8454f762e70d5a2bbdabdbe2a242d029e7c0 Mon Sep 17 00:00:00 2001 From: Praneeth Jain Date: Fri, 8 Mar 2024 14:12:57 +0530 Subject: [PATCH 34/44] feat: change PR body format (#413) * feat: change body format * fix: clone in correct directory * fix: override version --- .github/workflows/test-linux-mac.yml | 20 ++++++++++++++------ .github/workflows/test-win.yml | 18 +++++++++++++----- 2 files changed, 27 insertions(+), 11 deletions(-) diff --git a/.github/workflows/test-linux-mac.yml b/.github/workflows/test-linux-mac.yml index 0e5438d..e3436ca 100644 --- a/.github/workflows/test-linux-mac.yml +++ b/.github/workflows/test-linux-mac.yml @@ -40,18 +40,26 @@ jobs: mkdir libcxxwrap && cd libcxxwrap body="${{github.event.pull_request.body}}" - repo_url=$(echo "$body" | sed -n '1p') - branch=$(echo "$body" | sed -n '2p') - echo $repo_url - echo $branch - git clone --branch $branch --single-branch $repo_url . + first_line=$(echo "$body" | sed -n '1p') + if [[ "$first_line" == *"#"* ]]; then + IFS='#' read -r left right <<< "$first_line" + + repo_url=$(echo "$left" | sed -e 's/^[[:space:]]*//' -e 's/[[:space:]]*$//') + branch=$(echo "$right" | sed -e 's/^[[:space:]]*//' -e 's/[[:space:]]*$//') + else + repo_url="$first_line" + branch="main" + fi + echo "repo_url: '$repo_url'" + echo "branch: '$branch'" + git clone --branch "$branch" --single-branch "$repo_url" . echo "done cloning" if [[ "$OSTYPE" != "darwin"* ]]; then rm -f /opt/hostedtoolcache/julia/1.6*/x64/lib/julia/libstdc++.so.6 fi mkdir build && cd build - cmake -DCMAKE_INSTALL_PREFIX=$HOME/install -DAPPEND_OVERRIDES_TOML=ON -DOVERRIDE_VERSION_TO_JLL=OFF -DCMAKE_BUILD_TYPE=Debug .. + cmake -DCMAKE_INSTALL_PREFIX=$HOME/install -DAPPEND_OVERRIDES_TOML=ON -DOVERRIDE_VERSION_TO_JLL=ON -DCMAKE_BUILD_TYPE=Debug .. VERBOSE=ON cmake --build . --config Debug --target install cd ../.. - uses: julia-actions/julia-buildpkg@latest diff --git a/.github/workflows/test-win.yml b/.github/workflows/test-win.yml index 586782d..c1d1ed5 100644 --- a/.github/workflows/test-win.yml +++ b/.github/workflows/test-win.yml @@ -39,11 +39,19 @@ jobs: mkdir libcxxwrap && cd libcxxwrap body="${{github.event.pull_request.body}}" - repo_url=$(echo "$body" | sed -n '1p') - branch=$(echo "$body" | sed -n '2p') - echo $repo_url - echo $branch - git clone --branch $branch --single-branch $repo_url . + first_line=$(echo "$body" | sed -n '1p') + if [[ "$first_line" == *"#"* ]]; then + IFS='#' read -r left right <<< "$first_line" + + repo_url=$(echo "$left" | sed -e 's/^[[:space:]]*//' -e 's/[[:space:]]*$//') + branch=$(echo "$right" | sed -e 's/^[[:space:]]*//' -e 's/[[:space:]]*$//') + else + repo_url="$first_line" + branch="main" + fi + echo "repo_url: '$repo_url'" + echo "branch: '$branch'" + git clone --branch "$branch" --single-branch "$repo_url" . echo "done cloning" - name: Config 32bit if: ${{ matrix.arch == 'x86'}} From 4896072fcf00d7d1dfaa8b6ff39d0a78dd572a8b Mon Sep 17 00:00:00 2001 From: Bart Janssens Date: Fri, 8 Mar 2024 10:05:42 +0100 Subject: [PATCH 35/44] Set continue-on-error for build step --- .github/workflows/test-linux-mac.yml | 1 + .github/workflows/test-win.yml | 4 ++++ 2 files changed, 5 insertions(+) diff --git a/.github/workflows/test-linux-mac.yml b/.github/workflows/test-linux-mac.yml index e3436ca..d610886 100644 --- a/.github/workflows/test-linux-mac.yml +++ b/.github/workflows/test-linux-mac.yml @@ -36,6 +36,7 @@ jobs: version: ${{ matrix.version }} arch: ${{ matrix.arch }} - name: Build libcxxwrap + continue-on-error: true run: | mkdir libcxxwrap && cd libcxxwrap diff --git a/.github/workflows/test-win.yml b/.github/workflows/test-win.yml index c1d1ed5..560c605 100644 --- a/.github/workflows/test-win.yml +++ b/.github/workflows/test-win.yml @@ -35,6 +35,7 @@ jobs: version: ${{ matrix.version }} arch: ${{ matrix.arch }} - name: Clone libcxxwrap + continue-on-error: true run: | mkdir libcxxwrap && cd libcxxwrap @@ -54,16 +55,19 @@ jobs: git clone --branch "$branch" --single-branch "$repo_url" . echo "done cloning" - name: Config 32bit + continue-on-error: true if: ${{ matrix.arch == 'x86'}} run: | cd libcxxwrap && mkdir build && cd build cmake -G "Visual Studio 17 2022" -A Win32 -DOVERRIDES_PATH=$HOMEDRIVE/$HOMEPATH/.julia/artifacts/Overrides.toml -DOVERRIDE_ROOT=./ -DAPPEND_OVERRIDES_TOML=ON .. - name: Config 64bit + continue-on-error: true if: ${{ matrix.arch == 'x64'}} run: | cd libcxxwrap && mkdir build && cd build cmake -G "Visual Studio 17 2022" -A x64 -DOVERRIDES_PATH=$HOMEDRIVE/$HOMEPATH/.julia/artifacts/Overrides.toml -DOVERRIDE_ROOT=./ -DAPPEND_OVERRIDES_TOML=ON .. - name: Build libcxxwrap + continue-on-error: true run: | cd libcxxwrap/build cmake --build . --config Release From ca9083323dcb0d7e820a402ceb2f8fa9270a1d92 Mon Sep 17 00:00:00 2001 From: Bart Janssens Date: Fri, 8 Mar 2024 10:17:39 +0100 Subject: [PATCH 36/44] Bump required libcxxwrap-julia version --- Project.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Project.toml b/Project.toml index 040e832..7551273 100644 --- a/Project.toml +++ b/Project.toml @@ -11,7 +11,7 @@ libcxxwrap_julia_jll = "3eaa8342-bff7-56a5-9981-c04077f7cee7" [compat] MacroTools = "0.5.9" julia = "1.6" -libcxxwrap_julia_jll = "0.12.1" +libcxxwrap_julia_jll = "0.12.2" [extras] BenchmarkTools = "6e4b80f9-dd63-53aa-95a3-0cdb28fa8baf" From 3f1a013e16b2052f9b62b068134e34a1cfa38812 Mon Sep 17 00:00:00 2001 From: Michael Schlottke-Lakemper Date: Fri, 8 Mar 2024 23:14:15 +0100 Subject: [PATCH 37/44] Document memory management for return values (#406) --- README.md | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index 4114f30..f3bb86b 100644 --- a/README.md +++ b/README.md @@ -385,7 +385,8 @@ There is also an `apply_combination` method to make applying all combinations of Full example and test including non-type parameters at: [`examples/parametric.cpp`](https://github.com/JuliaInterop/libcxxwrap-julia/tree/master/examples/parametric.cpp) and [`test/parametric.jl`](test/parametric.jl). -## Constructors and destructors +## Memory management +### Constructors and destructors The default constructor and any manually added constructor using the `constructor` function will automatically create a Julia object that has a finalizer attached that calls delete to free the memory. To write a C++ function that returns a new object that can be garbage-collected in Julia, use the `jlcxx::create` function: @@ -405,6 +406,13 @@ wvec = cpp_function_returning_vector() julia_array = copy.(wvec) ``` +### Return values +If a wrapped C++ function returns an object by value, the wrapped object gets a finalizer +and is owned by Julia. The same holds if a smart pointer such as `shared_ptr` (automatically +wrapped in a `SharedPtr`) is returned by value. In contrast to that, if a reference or raw +pointer is returned from C++, then the default assumption is that the pointed-to object +lifetime is managed by C++. + ## Call operator overload Since Julia supports overloading the function call operator `()`, this can be used to wrap `operator()` by just omitting the method name: From e9ca4319f7b1676a907f1fe28d1824c474df4f55 Mon Sep 17 00:00:00 2001 From: Bart Janssens Date: Fri, 8 Mar 2024 23:19:20 +0100 Subject: [PATCH 38/44] Remove test badge (#415) --- README.md | 2 -- 1 file changed, 2 deletions(-) diff --git a/README.md b/README.md index f3bb86b..1a7f0ad 100644 --- a/README.md +++ b/README.md @@ -1,7 +1,5 @@ # CxxWrap -[![test](https://github.com/JuliaInterop/CxxWrap.jl/actions/workflows/test.yml/badge.svg?branch=main)](https://github.com/JuliaInterop/CxxWrap.jl/actions/workflows/test.yml?query=branch%3Amain) - This package aims to provide a Boost.Python-like wrapping for C++ types and functions to Julia. The idea is to write the code for the Julia wrapper in C++, and then use a one-liner on the Julia side to make the wrapped C++ library available there. From c445909a218e367f3cd8da873e8f3dedb2cab4d7 Mon Sep 17 00:00:00 2001 From: Bart Janssens Date: Sat, 9 Mar 2024 09:46:18 +0100 Subject: [PATCH 39/44] Add breaking changes for v0.15 (#417) --- README.md | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/README.md b/README.md index 1a7f0ad..979bd82 100644 --- a/README.md +++ b/README.md @@ -974,6 +974,10 @@ Often, new releases of `CxxWrap` also require a new release of the C++ component ## Breaking changes in v0.13 * Automatic dereferencing of smart pointers was removed, so some code may require adding the dereferencing operator `[]` explicitly. See [PR #338](https://github.com/JuliaInterop/CxxWrap.jl/pull/338). +## Breaking changes in v0.15 +* This release is based on `libcxxwrap-julia` 0.12, which is binary incompatible with previous versions, so JLLs should be rebuilt to use CxxWrap 0.15 +* The `constructor` method now takes a `jlcxx::finalize_policy` instead of a `bool`, e.g. `.constructor(false)` becomes `.constructor(jlcxx::finalize_policy::no)` + ## References * [JuliaCon 2020 Talk: Julia and C++: a technical overview of CxxWrap.jl](https://www.youtube.com/watch?v=u7IaXwKSUU0) * [JuliaCon 2020 Workshop: Wrapping a C++ library using CxxWrap.jl](https://www.youtube.com/watch?v=VoXmXtqLhdo) From 9d06993170bcbef11bf41a652112558c492442c8 Mon Sep 17 00:00:00 2001 From: Praneeth Jain Date: Sun, 31 Mar 2024 20:33:33 +0530 Subject: [PATCH 40/44] Add StdFill (#420) * feat: fill algorithm * test: StdFill * build: macos-13 * bump: Julia 1.7 * test: switch back to Julia 1.6 * fix: fill! returns the container --- .github/workflows/test-linux-mac.yml | 11 +++++------ .github/workflows/test-win.yml | 23 +++++++++++------------ src/StdLib.jl | 10 ++++++---- test/stdlib.jl | 28 ++++++++++++++++++++++++++++ 4 files changed, 50 insertions(+), 22 deletions(-) diff --git a/.github/workflows/test-linux-mac.yml b/.github/workflows/test-linux-mac.yml index d610886..2013b78 100644 --- a/.github/workflows/test-linux-mac.yml +++ b/.github/workflows/test-linux-mac.yml @@ -10,7 +10,7 @@ on: defaults: run: shell: bash - + jobs: test: name: Julia ${{ matrix.version }} - ${{ matrix.os }} - ${{ matrix.arch }} @@ -19,11 +19,11 @@ jobs: fail-fast: false matrix: version: - - '1.6' - - '1.10' - - 'nightly' + - "1.6" + - "1.10" + - "nightly" os: - - macOS-latest + - macos-13 - ubuntu-latest arch: - x64 @@ -65,4 +65,3 @@ jobs: cd ../.. - uses: julia-actions/julia-buildpkg@latest - uses: julia-actions/julia-runtest@latest - diff --git a/.github/workflows/test-win.yml b/.github/workflows/test-win.yml index 560c605..7d35a75 100644 --- a/.github/workflows/test-win.yml +++ b/.github/workflows/test-win.yml @@ -10,7 +10,7 @@ on: defaults: run: shell: bash - + jobs: test: name: Julia ${{ matrix.version }} - ${{ matrix.os }} - ${{ matrix.arch }} @@ -19,9 +19,9 @@ jobs: fail-fast: false matrix: version: - - '1.6' - - '1.10' - - 'nightly' + - "1.7" + - "1.10" + - "nightly" os: - windows-latest arch: @@ -58,20 +58,19 @@ jobs: continue-on-error: true if: ${{ matrix.arch == 'x86'}} run: | - cd libcxxwrap && mkdir build && cd build - cmake -G "Visual Studio 17 2022" -A Win32 -DOVERRIDES_PATH=$HOMEDRIVE/$HOMEPATH/.julia/artifacts/Overrides.toml -DOVERRIDE_ROOT=./ -DAPPEND_OVERRIDES_TOML=ON .. + cd libcxxwrap && mkdir build && cd build + cmake -G "Visual Studio 17 2022" -A Win32 -DOVERRIDES_PATH=$HOMEDRIVE/$HOMEPATH/.julia/artifacts/Overrides.toml -DOVERRIDE_ROOT=./ -DAPPEND_OVERRIDES_TOML=ON .. - name: Config 64bit continue-on-error: true if: ${{ matrix.arch == 'x64'}} run: | - cd libcxxwrap && mkdir build && cd build - cmake -G "Visual Studio 17 2022" -A x64 -DOVERRIDES_PATH=$HOMEDRIVE/$HOMEPATH/.julia/artifacts/Overrides.toml -DOVERRIDE_ROOT=./ -DAPPEND_OVERRIDES_TOML=ON .. + cd libcxxwrap && mkdir build && cd build + cmake -G "Visual Studio 17 2022" -A x64 -DOVERRIDES_PATH=$HOMEDRIVE/$HOMEPATH/.julia/artifacts/Overrides.toml -DOVERRIDE_ROOT=./ -DAPPEND_OVERRIDES_TOML=ON .. - name: Build libcxxwrap continue-on-error: true run: | - cd libcxxwrap/build - cmake --build . --config Release - + cd libcxxwrap/build + cmake --build . --config Release + - uses: julia-actions/julia-buildpkg@latest - uses: julia-actions/julia-runtest@latest - diff --git a/src/StdLib.jl b/src/StdLib.jl index d77afae..225533f 100644 --- a/src/StdLib.jl +++ b/src/StdLib.jl @@ -194,10 +194,6 @@ Base.size(v::StdValArray) = (Int(cppsize(v)),) Base.getindex(v::StdValArray, i::Int) = cxxgetindex(v,i)[] Base.setindex!(v::StdValArray{T}, val, i::Int) where {T} = cxxsetindex!(v, convert(T,val), i) -function StdDeque(v::Vector{T}) where {T} - return StdDeque{T}(v, length(v)) -end - Base.IndexStyle(::Type{<:StdDeque}) = IndexLinear() Base.size(v::StdDeque) = (Int(cppsize(v)),) Base.getindex(v::StdDeque, i::Int) = cxxgetindex(v,i)[] @@ -212,4 +208,10 @@ Base.size(v::StdQueue) = (Int(cppsize(v)),) Base.push!(v::StdQueue, x) = push_back!(v, x) Base.first(v::StdQueue) = front(v) Base.pop!(v::StdQueue) = pop_front!(v) + +function Base.fill!(v::T, x) where T <: Union{StdVector, StdValArray, StdDeque} + StdFill(v, x) + return v +end + end diff --git a/test/stdlib.jl b/test/stdlib.jl index 91676b5..7d1d739 100644 --- a/test/stdlib.jl +++ b/test/stdlib.jl @@ -327,4 +327,32 @@ let @test length(queue) == 1 end +@testset "StdFill" begin + @testset "fill StdVector" begin + v = StdVector{Int64}([1, 2, 3, 4, 5]) + fill!(v, 1) + for x in v + @test x == 1 + end + end + + @testset "fill StdValArray" begin + v = StdValArray([1.0, 2.0, 3.0]) + fill!(v, 2) + for x in v + @test x == 2 + end + end + + @testset "fill StdDeque" begin + deq = StdDeque{Int64}() + for i = 1:10 + push!(deq, i) + end + fill!(deq, 3) + for x in deq + @test x == 3 + end + end +end end From bc6eeff3d64c4878d60f4cd5df56a13e855321a4 Mon Sep 17 00:00:00 2001 From: Bart Janssens Date: Sat, 27 Apr 2024 14:02:58 +0200 Subject: [PATCH 41/44] Add support for dynamic casting on the Julia side (#427) --- README.md | 23 +++++++++++++++++++++++ src/CxxWrap.jl | 7 ++++++- test/inheritance.jl | 10 +++++++++- 3 files changed, 38 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 979bd82..47b7b20 100644 --- a/README.md +++ b/README.md @@ -294,6 +294,29 @@ auto multi_vector_base = mod.add_type>>("MultiVectorBase") auto vector_base = mod.add_type>>("VectorBase", multi_vector_base.dt()); ``` +### Conversion + +Conversion to the base type happens automatically, or can be forced by calling convert, e.g. + +```julia +convert(A,b) +``` + +Where we have `b::B` and `B <: A` + +For the equivalent of a C++ `dynamic_cast`, we need to use pointers because the conversion may fail, i.e: + +```julia +convert(CxxPtr{B},CxxPtr(a)) +``` + +This is equivalent to the C++ code: +```c++ +dynamic_cast(&a); +``` + +Use `isnull` on the result to check if the conversion was successful or not. + See the test at [`examples/inheritance.cpp`](https://github.com/JuliaInterop/libcxxwrap-julia/tree/master/examples/inheritance.cpp) and [`test/inheritance.jl`](test/inheritance.jl). ## Enum types diff --git a/src/CxxWrap.jl b/src/CxxWrap.jl index c18949d..421bffe 100644 --- a/src/CxxWrap.jl +++ b/src/CxxWrap.jl @@ -294,6 +294,7 @@ Base.setindex!(x::CxxBaseRef, val, i::Int) = Base.setindex!(x[], val, i) Base.unsafe_convert(to_type::Type{<:CxxBaseRef}, x) = to_type(x.cpp_object) # This is defined on the C++ side for each wrapped type +cxxdowncast(::Type{T}, x::CxxPtr{T}) where {T} = x cxxupcast(x) = cxxupcast(CxxRef(x)) cxxupcast(x::CxxRef) = error("No upcast for type $(supertype(typeof(x))). Did you specialize SuperType to enable automatic upcasting?") function cxxupcast(::Type{T}, x) where {T} @@ -301,6 +302,9 @@ function cxxupcast(::Type{T}, x) where {T} end cxxupcast(::Type{T}, x::CxxBaseRef{T}) where {T} = x +# Dynamic cast equivalent +Base.convert(::Type{CxxPtr{DerivedT}}, x::CxxPtr{SuperT}) where {SuperT, DerivedT <: SuperT} = cxxdowncast(DerivedT, x) + struct ConstArray{T,N} <: AbstractArray{T,N} ptr::ConstCxxPtr{T} size::NTuple{N,Int} @@ -582,6 +586,7 @@ map_julia_arg_type(t::Type{ConstCxxPtr{CxxChar}}) = Union{ConstPtrTypes{Cchar}, # names excluded from julia type mapping const __excluded_names = Set([ + :cxxdowncast, :cxxupcast, :__cxxwrap_smartptr_dereference, :__cxxwrap_smartptr_construct_from_other, @@ -808,7 +813,7 @@ function readmodule(so_path_cb::Function, funcname, m::Module, flags) so_path = so_path_cb() fptr = Libdl.dlsym(Libdl.dlopen(so_path, flags), funcname) register_julia_module(m, fptr) - include_dependency(so_path) + include_dependency(Libdl.dlpath(so_path)) end function wrapmodule(so_path::String, funcname, m::Module, flags) diff --git a/test/inheritance.jl b/test/inheritance.jl index f2d2c1a..0eb960a 100644 --- a/test/inheritance.jl +++ b/test/inheritance.jl @@ -40,7 +40,15 @@ global d = D() @test take_ref(d) == "D" # factory function returning an abstract type A -@test message(create_abstract()) == "B" +let abstract_b = create_abstract() + @test message(abstract_b) == "B" + abstract_b_ptr = CxxPtr(abstract_b) + @test !isnull(convert(CxxPtr{B},abstract_b_ptr)) + @test message(convert(CxxPtr{B},abstract_b_ptr)) == "B" + @test isnull(convert(CxxPtr{C},abstract_b_ptr)) + @test isnull(convert(CxxPtr{D},abstract_b_ptr)) + @test convert(CxxPtr{A},abstract_b_ptr) === abstract_b_ptr +end @test dynamic_message_c(c) == "C" From 5925baf947b1d00d9449978ee9b3909e2f65479d Mon Sep 17 00:00:00 2001 From: Bart Janssens Date: Sun, 28 Apr 2024 20:15:48 +0200 Subject: [PATCH 42/44] Test conditionally on ranges (#428) --- test/stdlib.jl | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/test/stdlib.jl b/test/stdlib.jl index 7d1d739..5c08b65 100644 --- a/test/stdlib.jl +++ b/test/stdlib.jl @@ -327,6 +327,8 @@ let @test length(queue) == 1 end +@static if isdefined(StdLib, :HAS_RANGES) + @testset "StdFill" begin @testset "fill StdVector" begin v = StdVector{Int64}([1, 2, 3, 4, 5]) @@ -355,4 +357,7 @@ end end end end + +end + end From 00e85128ea67fcbaf2e35baf6722ef53f664e9dd Mon Sep 17 00:00:00 2001 From: Bart Janssens Date: Tue, 30 Apr 2024 22:33:38 +0200 Subject: [PATCH 43/44] Set version to v0.15.1 (#429) --- Project.toml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Project.toml b/Project.toml index 7551273..fd39918 100644 --- a/Project.toml +++ b/Project.toml @@ -1,7 +1,7 @@ name = "CxxWrap" uuid = "1f15a43c-97ca-5a2a-ae31-89f07a497df4" authors = ["Bart Janssens "] -version = "0.15.0" +version = "0.15.1" [deps] Libdl = "8f399da3-3557-5675-b5ff-fb832c97cbdb" @@ -11,7 +11,7 @@ libcxxwrap_julia_jll = "3eaa8342-bff7-56a5-9981-c04077f7cee7" [compat] MacroTools = "0.5.9" julia = "1.6" -libcxxwrap_julia_jll = "0.12.2" +libcxxwrap_julia_jll = "0.12.3" [extras] BenchmarkTools = "6e4b80f9-dd63-53aa-95a3-0cdb28fa8baf" From a31cbdecaf6900152fda14edd519825822b08132 Mon Sep 17 00:00:00 2001 From: Abdo Eid Date: Sat, 4 May 2024 04:34:55 +0300 Subject: [PATCH 44/44] Added the StdDequeIterator and its testset --- src/StdLib.jl | 29 +++++++++++++++++++++-------- test/stdlib.jl | 12 ++++++++++++ 2 files changed, 33 insertions(+), 8 deletions(-) diff --git a/src/StdLib.jl b/src/StdLib.jl index 225533f..511fcef 100644 --- a/src/StdLib.jl +++ b/src/StdLib.jl @@ -194,20 +194,33 @@ Base.size(v::StdValArray) = (Int(cppsize(v)),) Base.getindex(v::StdValArray, i::Int) = cxxgetindex(v,i)[] Base.setindex!(v::StdValArray{T}, val, i::Int) where {T} = cxxsetindex!(v, convert(T,val), i) +# Deque Base.IndexStyle(::Type{<:StdDeque}) = IndexLinear() Base.size(v::StdDeque) = (Int(cppsize(v)),) Base.getindex(v::StdDeque, i::Int) = cxxgetindex(v,i)[] -Base.setindex!(v::StdDeque{T}, val, i::Int) where {T} = cxxsetindex!(v, convert(T,val), i) -Base.push!(v::StdDeque, x) = push_back!(v, x) -Base.pushfirst!(v::StdDeque, x) = push_front!(v, x) -Base.pop!(v::StdDeque) = pop_back!(v) -Base.popfirst!(v::StdDeque) = pop_front!(v) -Base.resize!(v::StdDeque, n::Integer) = resize!(v, n) +Base.setindex!(v::StdDeque{T}, val, i::Int) where {T} = cxxsetindex(v, convert(T,val), i) +Base.push!(v::StdDeque, x) = push_back(v, x) +Base.pushfirst!(v::StdDeque, x) = push_front(v, x) +Base.pop!(v::StdDeque) = pop_back(v) +Base.popfirst!(v::StdDeque) = pop_front(v) +Base.resize!(v::StdDeque, n::Integer) = resize(v, n) +Base.empty!(v::StdDeque) = clear(v) +Base.:(==)(a::StdDequeIterator, b::StdDequeIterator) = iterator_is_equal(a,b) +function _iteration_tuple(d::StdDeque, state::StdDequeIterator) + if state == iteratorend(d) return nothing end + return (iterator_value(state), state) +end +Base.iterate(d::StdDeque) = _iteration_tuple(d, iteratorbegin(d)) +Base.iterate(d::StdDeque, state::StdDequeIterator) = _iteration_tuple(d, iterator_next(state)) + + + +# Queue Base.size(v::StdQueue) = (Int(cppsize(v)),) -Base.push!(v::StdQueue, x) = push_back!(v, x) +Base.push!(v::StdQueue, x) = push_back(v, x) Base.first(v::StdQueue) = front(v) -Base.pop!(v::StdQueue) = pop_front!(v) +Base.pop!(v::StdQueue) = pop_front(v) function Base.fill!(v::T, x) where T <: Union{StdVector, StdValArray, StdDeque} StdFill(v, x) diff --git a/test/stdlib.jl b/test/stdlib.jl index 5c08b65..870b1f9 100644 --- a/test/stdlib.jl +++ b/test/stdlib.jl @@ -358,6 +358,18 @@ end end end +@testset "StdDequeIterator" begin + d = StdDeque{Int64}() + for i = 1:4 + push!(d, i) + end + iteration_tuple = iterate(d) + for i = 1:4 + @test iteration_tuple[1] == i + iteration_tuple = iterate(d, iteration_tuple[2]) + end end end + +end # StdLib