Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fix error message because of macro #126

Merged
merged 7 commits into from
May 27, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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)
kdheepak marked this conversation as resolved.
Show resolved Hide resolved
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