-
-
Notifications
You must be signed in to change notification settings - Fork 22.7k
Add RETURN_IF_ERR
macro, propagating an error upwards
#107760
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
base: master
Are you sure you want to change the base?
Conversation
If the expression results in an error, it is propagated and returned from the current function.
…w message to the log.
/** | ||
* If the return value of the expression is not OK, returns it as an error. | ||
*/ | ||
#define RETURN_IF_ERR_MSG(m_error, m_msg) \ |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
There should probably be an EDMSG version for completeness
My concern with this is that it's unintuitive, I'd say |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Now these messages might not be helpful, but they:
- Provide a code location for failure, which is important for testing
- Was meant to trigger an error originally, otherwise the code would have been the
if (err != OK)
format
So should either be replaced by a new macro with a message, or restored
We could go over some of these discussing if they are worth changing away from errors, but we should assume the original macro was used intentionally
I'm also not sure about the readability in some of these cases
@@ -197,8 +195,7 @@ Error image_to_png(const Ref<Image> &p_image, Vector<uint8_t> &p_buffer) { | |||
} | |||
|
|||
// trim buffer size to content | |||
Error err = p_buffer.resize(buffer_offset + compressed_size); | |||
ERR_FAIL_COND_V(err, err); | |||
RETURN_IF_ERR(p_buffer.resize(buffer_offset + compressed_size)); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is incorrect, this removes an error print
@@ -186,8 +185,7 @@ Error image_to_png(const Ref<Image> &p_image, Vector<uint8_t> &p_buffer) { | |||
ERR_FAIL_COND_V(compressed_size <= png_size_estimate, FAILED); | |||
|
|||
// write failed due to buffer size, resize and retry | |||
Error err = p_buffer.resize(buffer_offset + compressed_size); | |||
ERR_FAIL_COND_V(err, err); | |||
RETURN_IF_ERR(p_buffer.resize(buffer_offset + compressed_size)); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is incorrect, this removes an error print
@@ -173,8 +173,7 @@ Error image_to_png(const Ref<Image> &p_image, Vector<uint8_t> &p_buffer) { | |||
size_t compressed_size = png_size_estimate; | |||
int success = 0; | |||
{ // scope writer lifetime | |||
Error err = p_buffer.resize(buffer_offset + png_size_estimate); | |||
ERR_FAIL_COND_V(err, err); | |||
RETURN_IF_ERR(p_buffer.resize(buffer_offset + png_size_estimate)); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is incorrect, this removes an error print
@@ -206,8 +206,7 @@ class CowData { | |||
Error insert(Size p_pos, const T &p_val) { | |||
Size new_size = size() + 1; | |||
ERR_FAIL_INDEX_V(p_pos, new_size, ERR_INVALID_PARAMETER); | |||
Error err = resize(new_size); | |||
ERR_FAIL_COND_V(err, err); | |||
RETURN_IF_ERR(resize(new_size)); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is incorrect, this removes an error print
@@ -1886,8 +1835,7 @@ Error encode_variant(const Variant &p_variant, uint8_t *r_buffer, int &r_len, bo | |||
|
|||
for (const Variant &elem : array) { | |||
int len; | |||
Error err = encode_variant(elem, buf, len, p_full_objects, p_depth + 1); | |||
ERR_FAIL_COND_V(err, err); | |||
RETURN_IF_ERR(encode_variant(elem, buf, len, p_full_objects, p_depth + 1)); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is incorrect, this removes an error print
|
||
Vector<String> embedded_frameworks = export_plugins[i]->get_apple_embedded_platform_embedded_frameworks(); | ||
err = _export_additional_assets(p_preset, p_out_dir, embedded_frameworks, true, true, r_exported_assets); | ||
ERR_FAIL_COND_V(err, err); | ||
RETURN_IF_ERR(_export_additional_assets(p_preset, p_out_dir, embedded_frameworks, true, true, r_exported_assets)); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is incorrect, this removes an error print
|
||
Vector<String> project_static_libs = export_plugins[i]->get_apple_embedded_platform_project_static_libs(); | ||
for (int j = 0; j < project_static_libs.size(); j++) { | ||
project_static_libs.write[j] = project_static_libs[j].get_file(); // Only the file name as it's copied to the project | ||
} | ||
err = _export_additional_assets(p_preset, p_out_dir, project_static_libs, true, false, r_exported_assets); | ||
ERR_FAIL_COND_V(err, err); | ||
RETURN_IF_ERR(_export_additional_assets(p_preset, p_out_dir, project_static_libs, true, false, r_exported_assets)); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is incorrect, this removes an error print
|
||
Vector<String> apple_embedded_platform_bundle_files = export_plugins[i]->get_apple_embedded_platform_bundle_files(); | ||
err = _export_additional_assets(p_preset, p_out_dir, apple_embedded_platform_bundle_files, false, false, r_exported_assets); | ||
ERR_FAIL_COND_V(err, err); | ||
RETURN_IF_ERR(_export_additional_assets(p_preset, p_out_dir, apple_embedded_platform_bundle_files, false, false, r_exported_assets)); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is incorrect, this removes an error print
} | ||
|
||
Vector<String> library_paths; | ||
for (int i = 0; i < p_libraries.size(); ++i) { | ||
library_paths.push_back(p_libraries[i].path); | ||
} | ||
Error err = _export_additional_assets(p_preset, p_out_dir, library_paths, true, true, r_exported_assets); | ||
ERR_FAIL_COND_V(err, err); | ||
RETURN_IF_ERR(_export_additional_assets(p_preset, p_out_dir, library_paths, true, true, r_exported_assets)); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is incorrect, this removes an error print
@@ -174,8 +174,7 @@ Error EditorRun::run(const String &p_scene, const String &p_write_movie, const V | |||
} | |||
|
|||
OS::ProcessID pid = 0; | |||
Error err = OS::get_singleton()->create_instance(instance_args, &pid); | |||
ERR_FAIL_COND_V(err, err); | |||
RETURN_IF_ERR(OS::get_singleton()->create_instance(instance_args, &pid)); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is incorrect, this removes an error print
Agreed. I'd actually argue that all |
I'd avoid replacing non-printing cases, again, we should assume the choice was conscious. It would at least require a more detailed review to assess what and why to change, most of these are near other code that does use error printing. Some of these are cases where the source code prints an error, and there's little reason to have a print on the caller, so I'd say:
|
I anticipated there might be resistance to the Before any action though, I'd suggest we wait for more people to weigh in on this question. |
if (error) { | ||
return error; | ||
} | ||
RETURN_IF_ERR(parse_variant(value)); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Incorrect as well, this mutates a class variable not a local one, should be:
RETURN_IF_ERR(parse_variant(value)); | |
error = parse_variant(value); | |
RETURN_IF_ERR(error); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Oh man, good catch! I definitely should have noticed that.
for (const String &F : remaps) { | ||
String remap = F; | ||
if (remap == "path") { | ||
String remapped_path = config->get_value("remap", remap); | ||
Vector<uint8_t> array = FileAccess::get_file_as_bytes(remapped_path); | ||
err = save_proxy.save_file(p_udata, remapped_path, array, idx, total, enc_in_filters, enc_ex_filters, key, seed); | ||
RETURN_IF_ERR(save_proxy.save_file(p_udata, remapped_path, array, idx, total, enc_in_filters, enc_ex_filters, key, seed)); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This changes the behavior of the loop, which should be investigated, but this changes the behavior, so should be excluded
Also, as a tangent but relevant due to the introduction of a new macro, I have some concerns about further encouraging moving functional code into checking macros. This isn't a problem in these cases, and it's not a hill I'm dying on, but it's a thing I've been thinking about and growing concerned about in some cases that I think is worth mentioning. Since we have some macros that are stripped out on certain builds, like And for that I feel it would be good to reduce the amount of functional code that is put inside these macros in some cases, because: Err err = foo();
DEV_ASSERT(err == OK); Is not the same as: DEV_ASSERT(foo() == OK); And it might, in the future, be a good idea to fully enforce that these kind of macros should never have functional code in them except when that's explicitly meant to be error only, or even straight up forbid mutating code there at all to prevent some strange edge cases Just a thought, but relevant to the changes away from having separate calls Edit: To me this removes a few lines of code and reduces the number of local declarations, but adds a layer of obscurity However one thing I think would be valuable would be a set of |
Godot does not use exceptions. This often results in patterns where errors are propagated upwards:
I propose to support this paradigm with a dedicated macro:
RETURN_IF_ERROR(expression());
The benefit of this is threefold:
An arguable downside is that it's 'another godot-thing' to remember about the code base. I personally think the naming makes it clear enough what the macro does, though.
Relations
The idea of this macro may be familiar to Rust programmers. Rust embraces exception-free coding so much it made this concept into a core part of its grammar, with the question mark ? operator.
It has been proposed to add this operator to C++ too, for exception-free projects like Godot, but it's unlikely it will make it into the standard anytime soon.