Function Pointer Which Accepts Both With And Without Noexcept
I have some utility code that I've been using for years to safely call the ctype family of functions, it looks like this:
template<int (&F)(int)>
int safe_ctype(unsigned char c) {
return F(c);
}
And is used like this:
int r = safe_ctype<std::isspace>(ch);
The idea being that it handles the need to cast the input int
to an unsigned value for you in order to prevent undefined behavior. The specifics of this function is somewhat irrelivant though. Here's my question:
Now that in C++17 and later, noexcept
is part of the type system, this is a compile error! Because all of the ctype functions are now noexcept
.
EDIT: The above sentence is incorrect. the ctype family of functions are notnoexcept
. I was however getting a compiler error in gcc < 11.2. https://godbolt.org/z/cTq94q5xE
The code works as expected (despite being technically not allowed due to these functions not being addressable) with the latest versions of all 3 major compilers.
I can of course change my function to look like this:
template<int (&F)(int) noexcept>
int safe_ctype(unsigned char c) noexcept {
return F(c);
}
But now it doesn't work when compiled as C++11 or C++14. So I end up having to do something like this:
#if __cplusplus >= 201703L
template<int (&F)(int) noexcept>
int safe_ctype(unsigned char c) noexcept {
return F(c);
}
#else
template<int (&F)(int)>
int safe_ctype(unsigned char c) {
return F(c);
}
#endif
Which is getting increasingly complex for such a simple task. So is there a way to make the function pointer:
- valid for C++11 - C++20
- Accept both noexcept and non-noexcept when in C++17+
?
I tried doing something like this:
template<class F>
int safe_ctype(unsigned char c) noexcept {
return F(c);
}
In the hopes that it would accept "anything", but sadly, no go.
Thoughts?
Answer
Now that in C++17 and later, noexcept is part of the type system, this is a compile error! Because all of the ctype functions are now noexcept.
It is not a compile error. Pointers to noexcept functions are implicitly convertible to pointers to potentially throwing functions, and thus the template accepting a pointer to potentially throwing functions works with both potentially throwing and noexcept functions. Only caveat is that the noexceptedness information is lost and might not be used for optimisation purposes.
Hence, the original solution satisfies both points 1. and 2.
Another problem pointed out in the comments is that the standard library functions (std::isspace
) that you intend to use are not designated "addressable". Hence the behaviour of the program is unspecified (possibly ill-formed) due to forming a pointer to them.
To wrap such callable, you could use a lambda instead of a function pointer. But that makes the template itself obsolete since you can change the argument type of the lambda directly:
auto safe_isspace = [](unsigned char c){ return std::isspace(c); };
int r = safe_isspace(ch);
Though we no longer need to pass this into a template, so the same can be achieved with a plain function:
int // or bool?
safe_isspace(unsigned char c) noexcept // ...
Since this involves a bit of identical boilerplate for multiple functions, this is a good candidate for meta-programming.
Related Questions
- → Comparing two large files are taking over four hours
- → Setting JSON node name to variable value
- → Compiling GLUT using Emscripten
- → Evaluate check box from a scanned image in node.js
- → Find an easy web server framework for mobile game
- → my https C++ code doesn't work on some sites (binance)
- → Error while opening pivx wallet on ubuntu
- → Why sending a POST by AJAX is interpreted by the HTTP Server as OPTIONS and sending by CURL is effectively a PUT?
- → Python reading in one line multiple types for a calculator
- → How do I properly pass an argument to a function
- → Accessing Websql database with Qt
- → Using Mysql C API for c++ codes
- → How do I set constants at run-time in a c++ header file, imported through Cython?