I have a vendor library that throws exceptions, which makes wrapping them a bit more miserable. I only have access to headers and shared libraries, and I couldn’t figure out a way to link the libraries and prevent exceptions (it is probably impossible to prevent it).
Having the REPL abort because of an exception is not good, but wrapping the calls seems clunky (but at least it doesn’t abort). I was looking for anyone’s advice on how to handle this?
TLDR: I created a struct that had a boolean representing if the call was exception-free, and then a sequence of struct members representing the various forms of return types (crude sum type I guess) and then a const char *
field that would contain the exception text. It sort of is okay-ish, but I would be glad to hear about anything more sleek.
Example C++ code
#include <cmath>
#include <exception>
#include <iostream>
#include <string>
#include <string.h>
constexpr size_t N = 2048;
extern "C" {
//------------------//
// notional library //
//------------------//
typedef struct Result {
bool success;
int as_int;
double as_double;
char what[N];
} Result;
double my_sqrt(double f) {
if (f < 0.0)
throw std::domain_error("negative value");
return sqrt(f);
}
int add1(int f) {
return f + 1;
}
//----------------------//
// notional wrapper API //
//----------------------//
Result my_sqrt_wrapper(double f) {
Result ret;
try {
ret.as_double = my_sqrt(f);
ret.success = true;
} catch (const std::exception& e) {
ret.success = false;
strncpy(ret.what, e.what(), N);
ret.what[N] = '\0';
}
return ret;
}
Result add1_wrapper(int f) {
Result ret;
try {
ret.as_int = add1(f);
ret.success = true;
} catch (const std::exception& e) {
ret.success = false;
strncpy(ret.what, e.what(), N);
ret.what[N] = '\0';
}
return ret;
}
Example Julia Wrapper
struct Result
success::Cuchar
as_int::Cint
as_double::Cdouble
what::NTuple{2048, Cchar}
end
function my_sqrt(x)
a = @ccall "./libstuff.so".my_sqrt_wrapper(x::Cdouble)::Result
if iszero(a.success)
string_end = findfirst(iszero, a.what) - 1
message = join(convert.(Char, a.what[1:string_end]))
throw(DomainError(x, message))
else
a.as_double
end
end
function add1(x)
a = @ccall "./libstuff.so".add1_wrapper(x::Cint)::Result
if iszero(a.success)
string_end = findfirst(iszero, a.what) - 1
message = join(convert.(Char, a.what[1:string_end]))
throw(DomainError(x, message))
else
a.as_int
end
end
@show add1(3)
@show my_sqrt(4)
@show my_sqrt(-4)