Skip to content

Commit

Permalink
Merge pull request #126 from jump-dev/kd/fix-error-message-macro
Browse files Browse the repository at this point in the history
Fix error message because of macro
  • Loading branch information
joaquimg authored May 27, 2021
2 parents e325c68 + 3d067d6 commit f15573f
Show file tree
Hide file tree
Showing 5 changed files with 75 additions and 22 deletions.
31 changes: 19 additions & 12 deletions src/api.jl
Original file line number Diff line number Diff line change
Expand Up @@ -73,7 +73,7 @@ Initializes the Optimizer library. This must be called before any other library
"""
function init()
@checked Lib.XPRSinit(C_NULL)
Lib.XPRSinit(C_NULL)
end

"""
Expand All @@ -83,7 +83,7 @@ Frees any allocated memory and closes all open files.
"""
function free()
@checked Lib.XPRSfree()
Lib.XPRSfree()
end

"""
Expand Down Expand Up @@ -125,7 +125,7 @@ You can use this function to disable some of the checking and validation of func
"""
function setcheckedmode(checked_mode)
@checked Lib.XPRSsetcheckedmode(checked_mode)
Lib.XPRSsetcheckedmode(checked_mode)
end

"""
Expand All @@ -135,19 +135,19 @@ You can use this function to interrogate whether checking and validation of all
"""
function getcheckedmode(r_checked_mode)
@checked Lib.XPRSgetcheckedmode(r_checked_mode)
Lib.XPRSgetcheckedmode(r_checked_mode)
end

function license(lic, path)
r = Lib.XPRSlicense(lic, path)
end

function beginlicensing(r_dontAlreadyHaveLicense)
@checked Lib.XPRSbeginlicensing(r_dontAlreadyHaveLicense)
Lib.XPRSbeginlicensing(r_dontAlreadyHaveLicense)
end

function endlicensing()
@checked Lib.XPRSendlicensing()
Lib.XPRSendlicensing()
end

"""
Expand All @@ -162,11 +162,12 @@ Retrieves an error message describing the last licensing error, if any occurred.
"""
function getlicerrmsg(; len = 1024)
msg = Cstring(pointer(Array{Cchar}(undef, len*8)))
Lib.XPRSgetlicerrmsg(msg, len)
# TODO - @ checked version does not work
# @checked Lib.XPRSgetlicerrmsg(msg, len)
return unsafe_string(msg)
buffer = Array{Cchar}(undef, len*8)
buffer_p = pointer(buffer)
GC.@preserve buffer begin
Lib.XPRSgetlicerrmsg(msg, len)
return unsafe_string(msg)
end
end

"""
Expand Down Expand Up @@ -2788,7 +2789,13 @@ Returns the error message corresponding to the last error encountered by a libra
"""
function getlasterror(prob::XpressProblem)
@invoke Lib.XPRSgetlasterror(prob, _)::String
end
buffer = Array{Cchar}(undef, 512)
buffer_p = pointer(buffer)
GC.@preserve buffer begin
s = Lib.XPRSgetlasterror(prob, buffer_p)
return s == 0 ? unsafe_string(buffer_p) : "Unable to get last error"
end
end

"""
int XPRS_CC XPRSbasiscondition(XPRSprob prob, double *condnum, double *scondnum);
Expand Down
4 changes: 4 additions & 0 deletions src/helper.jl
Original file line number Diff line number Diff line change
Expand Up @@ -62,6 +62,10 @@ mutable struct XpressProblem <: CWrapper
end
end

function get_xpress_error_message(prob::XpressProblem)
lstrip(Xpress.getlasterror(prob), ['?'])
end

function XpressProblem(; logfile = "")
ref = Ref{Lib.XPRSprob}()
createprob(ref)
Expand Down
7 changes: 5 additions & 2 deletions src/license.jl
Original file line number Diff line number Diff line change
Expand Up @@ -74,8 +74,11 @@ function userlic(; verbose::Bool = true, liccheck::Function = emptyliccheck, xpa
lic = liccheck(lic)

# Send GIVEN LIC to XPRESS lib
slicmsg = Cstring(pointer(Array{Cchar}(undef, 1024*8)))
ierr = license(lic, slicmsg)
buffer = Array{Cchar}(undef, 1024*8)
buffer_p = pointer(buffer)
ierr = GC.@preserve buffer begin
license(lic, Cstring(buffer_p))
end

# check LIC TYPE
if ierr == 16
Expand Down
53 changes: 45 additions & 8 deletions src/utils.jl
Original file line number Diff line number Diff line change
Expand Up @@ -19,13 +19,15 @@ function invoke(f::Function, pos::Int, ::Type{Int}, args...)
end

function invoke(f::Function, pos::Int, ::Type{String}, args...)
out = Cstring(pointer(Array{Cchar}(undef, 1024)))

args = collect(Any, args)
insert!(args, pos-1, out)

r = f(args...)
r != 0 ? throw(XpressError(r, "Unable to invoke $f")) : unsafe_string(out)
buffer = Array{Cchar}(undef, 1024)
buffer_p = pointer(buffer)
GC.@preserve buffer begin
out = Cstring(buffer_p)
args = collect(Any, args)
insert!(args, pos-1, out)
r = f(args...)
return r != 0 ? throw(XpressError(r, "Unable to invoke $f")) : unsafe_string(out)
end
end

"""
Expand Down Expand Up @@ -88,15 +90,50 @@ macro invoke(expr)
return f
end

function get_xpress_error_message(xprs_ptr)
"(Unable to extract error message for $(typeof(xprs_ptr)).)"
end

"""
@checked f(prob)
Lets you invoke a lower level `Lib` function and check that Xpress does not error.
Use this macro to minimize repetition and increase readability.
The first argument must be a object that can be cast into an Xpress pointer, e.g. `Ptr{XpressProblem}`.
This is passed to `get_xpress_error_message(xprs_ptr)` to get the error message.
Examples:
@checked Lib.XPRSsetprobname(prob, name)
As an example of what @checked expands to:
```
julia> @macroexpand @checked Lib.XPRSsetprobname(prob, name)
quote
r = Lib.XPRSsetprobname(prob, name)
if r != 0
xprs_ptr = prob
e = get_xpress_error_message(xprs_ptr)
throw(XpressError(r, "Unable to call `Xpress.setprobname`:\n\n\$(e).\n"))
else
nothing
end
end
```
"""
macro checked(expr)
@assert expr.head == :call "Can only use @checked on function calls"
@assert ( expr.args[1].head == :(.) ) && ( expr.args[1].args[1] == :Lib) "Can only use @checked on Lib.\$function"
@assert length(expr.args) >= 2 "Lib.\$function must be contain atleast one argument and the first argument must be of type XpressProblem"
f = replace(String(expr.args[1].args[2].value), "XPRS" => "")
return esc(quote
r = $(expr)
if r != 0
e = lstrip(Xpress.getlasterror(prob), ['?'])
xprs_ptr = $(expr.args[2])
e = get_xpress_error_message(xprs_ptr)
throw(XpressError(r, "Unable to call `Xpress.$($f)`:\n\n$e.\n"))
else
nothing
Expand Down
2 changes: 2 additions & 0 deletions test/runtests.jl
Original file line number Diff line number Diff line change
Expand Up @@ -16,4 +16,6 @@ end
@test Xpress.getcontrol(prob, "HEURTHREADS") == 0
@test Xpress.getcontrol(prob, :HEURTHREADS) == 0

msg = "Unable to call `Xpress.copyprob`:\n\n91 Error: No problem has been input.\n"
@test_throws Xpress.XpressError(32,msg) Xpress.copyprob(prob, prob)
end

0 comments on commit f15573f

Please sign in to comment.