glfw: Error query
Add a query for the latest per-thread error code. This would be backed by a TLS slot maintained by GLFW.
1 - Simplified OpenGL style, error query that returns the last error and clears it as it’s returned.
int glfwGetError(void);
2 - OpenGL style, error query that returns one of the last n errors and clears each as it’s returned.
int glfwGetError(void);
3 - Win32 GetLastError style, explicit getter and setter, no implicit behavior.
int glfwGetError(void);
void glfwSetError(int error);
4 - Error code and description query, requires locking and heap allocation on errors.
const char* glfwGetError(int* error);
void glfwSetError(int error, const char* description);
5 - @tombsar style, explicit getter and clear, no implicit behavior.
int glfwGetError(void);
void glfwClearError(void);
6 - @felipefs style, error code and description query, requires locking and heap allocation on errors.
#define GLFW_ERROR_MESSAGE_LENGTH 1024
bool glfwGetError(int* error, char** description);
7 - error-description branch, error code and description query, requires locking and heap allocation on errors.
int glfwGetError(const char** description);
Feedback and more alternatives are very welcome.
About this issue
- Original URL
- State: closed
- Created 7 years ago
- Comments: 21 (17 by maintainers)
Commits related to this issue
- Add internal TLS support Related to #970. — committed to glfw/glfw by elmindreda 7 years ago
- Add glfwGetError Related to #970. If you have opinions on the design or implementation of this function, please come join us in #970 before it is frozen for release. — committed to glfw/glfw by elmindreda 7 years ago
- Make glfwGetError return description Related to #970. — committed to glfw/glfw by elmindreda 7 years ago
- Make glfwGetError also provide description Related to #970. — committed to glfw/glfw by elmindreda 7 years ago
- Report invalid constants in the appropriate base If the expected constants are defined in hexadecimal in the header then the error string should also use hexadecimal. Idea by IntellectualKitty. Rel... — committed to glfw/glfw by elmindreda 7 years ago
@IntellectualKitty Not too late at all!
It’s not my intent to replace the error callback, even in the long term. They’re both useful in their own ways and the callback provides some benefits that polling just can’t.
I object to locking and heap allocation on errors not because of the performance impact, which as you say is negligible, but because it introduces complexity and the possibility of failure into the part of the code that should be the most simple and robust.
So 4b80443199d6e11f6c72ef78e6f9d41bff1b016c (see the
error-descriptionbranch) is something like what I had in mind for option 4, with a bit of option 6 for the interface. It’s the simplest thing I could think of without having a portable compare-and-swap.Error checking wrapper macros are very handy, but I don’t know that GLFW should provide its own, though it could show an example of one in the documentation. I suspect everyone will want to write their own that works the way they want and integrates with their specific systems. Providing a fully generic one doesn’t seem like it’d save the user a lot of time, as it would likely require customization macros to plug in their magic function.
Yup, the contortions projects like go-glfw have to go through to get local access to the error code is one reason for this feature.
However, even C code needs that sometimes. A complex application may want to change or downgrade its requirements ifThis was already mentioned above.glfwCreateWindowfails withGLFW_VERSION_UNAVAILABLE,GLFW_FORMAT_UNAVAILABLEor evenGLFW_API_UNAVAILABLE. A caller ofglfwGetClipboardStringmay want to tellGLFW_FORMAT_UNAVAILABLEapart fromGLFW_PLATFORM_ERROR.I originally imagined the error callback as an analog to
GL_KHR_debug, helping the application developer figure out what they’re doing wrong, but some errors are pretty useful to have access to locally in release code without having to set up a callback and a TLS slot oneself.It may also have a positive pedagogical effect. I’ve started seeing people exiting from inside the error callback on the assumption that errors are fatal.
Any objections to the current API? Is it time to close this issue?
I hope it’s not too late to comment.
First of all, this proposal addresses an issue that I had with GLFW since I first started using it since I was worried about the difficulty of associating the independent error callback with the GLFW function call that caused it. That said, I’ve grown quite attached to the error callback since it means that I don’t have to check for errors at every single line of GLFW code, and it feels a lot freer and easier to program that way. So, even though I didn’t like the error callback at first, I now feel like it’s one of the many features that makes GLFW so appealing. Lastly, I don’t recall ever having an error with GLFW anyway, so I have no idea how difficult it would be in practice to track an error down using the callback, but since GLFW is far, far easier to use than OpenGL itself I doubt that it would really be very hard.
Second, I think it’s extremely important to report an error description as well as an error code. Getting the error message “Context profiles are only defined for OpenGL version 3.2 and above” is far more helpful than simply reporting
GLFW_INVALID_VALUE. Again, this is one of the really nice features of GLFW’s existing error callback system. If you’re trying to track down a problem during development, it’s really helpful to have good diagnostic messages. If you’re trying to track down a problem in an actual release, say platform-specific issues, it’s really helpful to have good diagnostic messages.Third, I’m not sure that locking and heap allocation on error is significant enough of a concern to affect a critical design decision like this. The majority of errors are going to occur during development rather than in release, and errors should generally occur infrequently anyway unlike operations in a high performance section of code. Another way of saying this is that errors should be a special case issue, and if you have to be concerned about reporting so many errors that performance and resource utilization considerations become significant then you’ve probably done something extremely stupid. Not reporting critical details about an error condition because of the performance implications is a bit like jumping out of an airplane and not taking a parachute because the extra weight will make you fall faster. So, in practical terms, I think the cost of locking and heap allocation should be irrelevant compared to the value of the diagnostic information you receive.
Fourth, the proposed error query mechanism makes GLFW look more like OpenGL itself. An error query mechanism does, of course, make it easier to track down where an error is occurring, and that’s a valuable feature in any library. This is extremely important in OpenGL which often tends to be difficult and picky, whereas GLFW is very simple and easy to use. That said, with GL_DEBUG_OUTPUT, OpenGL itself is now providing a feature-rich callback system that reports not only errors but deprecations and performance hints as well.
So, I hope that the error query system won’t replace the error callback system, particularly in cases where multiple errors may be produced by a single function call. It seems unlikely that novice users would generally think to query for more than one error – the standard that we’re mostly all used to is reporting the first error encountered – and looping to check for multiple errors may be intimidating. What makes GLFW so nice – and presumably so popular (most of the OpenGL tutorials I’ve come across use GLFW) – is that it is so easy to use and to learn, and looping to check for multiple errors (and even having to check for errors manually instead of using the error callback) may jeopardize that simplicity and user-friendliness.
Fifth, it may be helpful to add a
GLFW_CHECK_ERRORmacro to support the error query mechanism. I use this type of error support system for OpenGL calls, for example:GL_CHECK_ERROR(glBindBuffer(GL_ARRAY_BUFFER,m_uiVertexBufferID));The
GL_CHECK_ERRORmacro is defined using a mechanism similar to the one used to define the standard Cassert, with the addition of including the OpenGL function call:The actual error reporting code uses spdlog but stderr could of course be used instead:
Using this system to check for and report OpenGL errors makes error handling extremely easy and transparent, and it is silently disabled in release builds (which may not be appropriate for GLFW).
I use (and help maintain) the Go bindings for GLFW.
In idiomatic Go, functions that can fail typically return multiple values, the result and an
errorvariable (reference). That way, the caller can immediately check if the function call failed, and handle the error appropriately. It typically looks like this:When making the Go bindings, we wanted to stay as true as possible to the GLFW C API, but we decided that it will be worth it to make the bindings have idiomatic signatures that return errors right away, so that the users of the Go library have the best experience (see https://github.com/go-gl/glfw/issues/8, https://github.com/go-gl/glfw/issues/27, https://github.com/go-gl/glfw/issues/56).
For example:
So, for our use case, the simplest API that allows fetching the error (if any) from last call would be sufficient. We don’t need the ability to track last n errors, since our bindings can recover and return the error details from each failed GLFW call, immediately as it happens. The user doesn’t have to worry about that. If the user doesn’t care about some error value, they can ignore it.
Also relevant here is #361, because we ideally want to be able to differentiate between errors caused by invalid API usage, a situation where it doesn’t make sense for the Go bindings to return an error, since the programmer should simply fix the invalid API usage, instead of trying to handle the error as it’s returned.
On the other hand, errors that happen because of system issues, such as “unable to create window” or “unable to fetch clipboard contents because insufficient permissions”, those are errors that the programmer should handle.
The Go bindings currently use
glfwSetErrorCallbackto perform all of the above, so this is not critical, but I wanted to talk about a use case I’m aware of and our needs/wants. I hope this is useful.Does anyone have any thoughts about prefacing an error message with the function name in which it was generated? This may be more helpful/appropriate for the error callback, but it still may make diagnosing the source of problems a little easier.
Currently
GLFW_INVALID_ENUMerrors are reported using%ibut0x%08Xmatches the #define style used in GLFW. Also, if there isn’t already a function to convert a GLFW enum to a string, would anyone else think this is helpful?I’ve only looked at this briefly at a high level. No big concerns from me and my Go perspective.
I think this is a case where it’s reasonable to prioritize more helpful and friendly error messages over some (minor?) performance. If performance turns out to be a bottleneck, after benchmarking and profiling, I’m sure that it’d be possible to address it later. So IMO it’s better to err more on side of helpful/friendly first.
If
glfwGetErrorreturns an error description along with the error code,go-gl/glfwcan expose/pass through the error description in its error value.A question: what benefit does this function add to glfw? I honestly can’t see myself using it over the existing error callback API (which I like more than what most other libraries provide).
Assuming such a function is to be added: are there any realistic cases where GLFW will generate multiple errors in response to a single API call? If not, option 1 (where you can query just the latest error) might be sufficient. If errors pile up and some get lost, it’s probably the client code’s fault for not polling often enough.
Allowing the user to set the error state feels like a design misstep to me. If the client code wants to generate its own errors, it can surely do so without needing to go through glfw. I would have to see some example code of how this feature could be put to good use.
I would be in favour of
glfwGetErrorsimply returning the latest error with no side-effects, and an additional function likeglfwClearErrorto actually clear it.I am definitely against option 4 because of the “locking and heap allocation on errors” requirements.