I’m dealing with a vendor-provided C-library (let’s call it foo
), that offers a multitude of API-calls. All of these functions take a (pre-initialized) handle (of type FOOHandle
) and return 0 on success, and some other number on failure. In the latter case, the caller can call the foo_get_errmsg()
to get the textual description of the error saved in his own buffer. Nothing too unusual.
Programming in C++, I’d like to turn these errors into exception of my own type. To that end, I created my own class FOOException
with the following constructor:
private:
char buf[1024];
int code;
public:
FOOException(FOOHandle h, int32_t _code) noexcept : code(_code)
{
foo_get_errmsg(code, h, buf);
}
So far so good.
To help convert existing code, I’d like to add a generic wrapper-function, which would invoke the specified API-call, check its result, then throw a new exception, if warranted:
static auto wrapper(const auto &foofunc, auto&&... args) const
{
auto code = foofunc(args...);
switch (code) {
case 0:
return 0;
default:
throw FOOException(handle, code);
}
}
My problem is in obtaining that handle
… Though the argument is always present in all calls to the FOO-functions — thus always among the args...
— it is not always the first:
foo_analyze(a, handle);
...
foo_meow(handle, a, b, c, d);
Is there some way I can “fish” the handle out from the args...
(either by name or by type) for the wrapper to use too, or must I duplicate it in all calls to the wrapper()
— once for the wrapper itself, and then again for the function being wrapped?
2
Assuming there is only one FooHandle
from the Args
, you might do something like (create std::tuple
and extract by type):
auto& handle = std::get<FooHandle&>(std::tie(args...));
Demo
7
You can manually unpack the template parameters until you find the handle, eg:
FOOHandle getfoohandle() {
return nullptr; // or whatever constitutes an "invalid" handle...
}
FOOHandle getfoohandle(FOOHandle h, auto&&...) {
return h;
}
FOOHandle getfoohandle(auto&&, auto&&... args) {
return getfoohandle(std::forward<decltype(args)>(args)...);
}
static void wrapper(const auto &foofunc, auto&&... args) const
{
auto code = foofunc(std::forward<decltype(args)>(args)...);
if (code != 0) {
FOOHandle handle = getfoohandle(std::forward<decltype(args)>(args)...);
throw FOOException(handle, code);
}
}
Online Demo
UPDATE: for pre-C++20 compilers, auto
is not allowed in function parameters, so you will have to use template
parameters instead, eg:
FOOHandle getfoohandle() {
return nullptr; // or whatever constitutes an "invalid" handle...
}
template<typename... Args>
FOOHandle getfoohandle(FOOHandle h, Args&&...) {
return h;
}
template<typename Param1, typename... Args>
FOOHandle getfoohandle(Param1&&, Args&&... args) {
return getfoohandle(std::forward<Args>(args)...);
}
template<typename Func, typename... Args>
static void wrapper(const Func &foofunc, Args&&... args) const
{
auto code = foofunc(std::forward<Args>(args)...);
if (code != 0) {
FOOHandle handle = getfoohandle(std::forward<Args>(args)...);
throw FOOException(handle, code);
}
}
Online Demo
7