Skip to content

Refactor section creation errors, and simplify output for single errors#1964

Open
Rangi42 wants to merge 2 commits into
gbdev:masterfrom
Rangi42:sect-errors
Open

Refactor section creation errors, and simplify output for single errors#1964
Rangi42 wants to merge 2 commits into
gbdev:masterfrom
Rangi42:sect-errors

Conversation

@Rangi42
Copy link
Copy Markdown
Contributor

@Rangi42 Rangi42 commented May 1, 2026

The refactoring here is because the #define sectError macro was an awkward hack, relying on each of its callers to have a variable specifically called nbSectErrors.

The simplification is because most sectErrors are exclusive (note all the if-else chains) so you rarely end up with more than one (note that I had to add a new test for multiple errors with the same section). Comparing these two outputs:

  • A non-fatal error that says "Section had this specific flaw", followed by FATAL "Cannot create section "<name>" (1 error)"
  • A FATAL "Section "<name>" had this specific flaw"

I find the latter new output easier to read, and less likely for inexperienced users to tunnel-vision read the very last error and miss its context. (Especially when they just post a screenshot/quote of the last error.)

@Rangi42 Rangi42 added this to the 1.0.2 milestone May 1, 2026
@Rangi42 Rangi42 requested a review from ISSOtm May 1, 2026 11:17
@Rangi42 Rangi42 added enhancement Typically new features; lesser priority than bugs rgbasm This affects RGBASM refactoring This PR is intended to clean up code more than change functionality labels May 1, 2026
Copy link
Copy Markdown
Member

@ISSOtm ISSOtm left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

There's a reason I'm not writing C++ anymore...

Comment thread include/asm/warning.hpp

// Used for fatal errors that handle their own backtrace output.
[[noreturn]]
void fatalNoTrace(std::function<void()> callback);
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please do not use std::function unless you have to. Its indirection has a lot of overhead, including in codegen, due to the type-erasure that it performs.

If you want a callable that can hold onto state (thus, not just a function pointer), please template this function. (Using concepts to document the expected parameters and return type, of course.)

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It's a fatal error; the overhead of a memory allocation compared to passing a function pointer really won't matter. Regardless, I'd be fine with changing the implementation, if we can do so without moving the whole implementation into the .hpp file as a template (and thereby also having to include the appropriate headers, helper functions, etc).

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🦀

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I remain concerned about how much extra C++ standard library this pulls in, the extra headers involved, and all the extra codegen regardless of the runtime cost.

Copy link
Copy Markdown
Contributor Author

@Rangi42 Rangi42 May 1, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'll see if it can be done another way, but I suspect I chose std::function in the first place for errorNoTrace because the callbacks need to capture variables, and the function can't/shouldn't be defined as a template in the asm/warning.hpp header file. If the #include <functional> has enough extra codegen to matter, it should be measurable, not just "avoid std::function all the time".

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🦀

How would this be solved in Rust? Can format! args be bundled into a Vec of deferred arg packs?

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, using format_args!(); but rather, you don't need to provide declaration separately from implementation and thus there are no header woes anymore.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If we move fatalNoTrace into the warning.hpp header file, we should do the same for the already-existing errorNoTrace function, which also takes a std::function<void()> callback. However, errorNoTrace calls the static void incrementErrors() function, so then that whole function would have to be non-static to be visible in the header file. Also the header would have to have #include <stdio.h> and #include "style.hpp" for the functions' implementations. I just doing think avoiding std::function is worth that cost.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

std::function is likely to have a "small buffer optimization", and since sectAlreadyDefinedCallback only captures one reference-pointer, it's likely to fit in a small buffer and not require an extra heap allocation. Unlike a template parameter, it won't be inlined, but that's just the time+space cost of one vtable'd function call. It's not done in some tight loop, it's only done in error conditions, and fatalNoTrace exits immediately so it will only happen at most once. So the runtime+space cost of std::function is low, and the code quality benefit is worthwhile.

Comment thread src/asm/section.cpp
Comment on lines +120 to +138
void sectError(char const *fmt, ...) {
std::string result;
va_list args1, args2;
va_start(args1, fmt);
va_copy(args2, args1);
int len = vsnprintf(nullptr, 0, fmt, args1);
va_end(args1);
if (len < 0) {
// LCOV_EXCL_START
va_end(args2);
fatal("Error describing the error that occurred when merging a section");
// LCOV_EXCL_STOP
} else if (len > 0) {
result.resize(len);
vsnprintf(result.data(), len + 1, fmt, args2);
}
va_end(args2);
sectErrors.push_back(result);
}
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I really hate the logic of this function, and would rather use std::format.

Copy link
Copy Markdown
Contributor Author

@Rangi42 Rangi42 May 1, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can we rely on std::format being available on all platforms/compilers? (Like how std::filesystem isn't quite usable either.) If so, I'd like to replace all our message-formatting code with it, instead of fprintfs.

Even without std::function, I could make this be a template function instead of a stdarg.h one, if you prefer?

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please. As for availability, I have no idea.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We could use a template function instead:

template<typename... Args>
void sectError(char const *fmt, Args... args) {
	std::string result;
	int len = snprintf(nullptr, 0, fmt, std::forward<Args>(args)...);
	if (len < 0) {
		fatal("Error describing the error that occurred when merging a section"); // LCOV_EXCL_LINE
	} else if (len > 0) {
		result.resize(len);
		snprintf(result.data(), len + 1, fmt, std::forward<Args>(args)...);
	}
	sectErrors.push_back(result);
}

...however, snprintf then complains "warning: format not a string literal and no format arguments", and I'd rather not disable -Wno-format-security just for this.

Switching to std::format can be done for #1973.

Comment thread src/asm/section.cpp Outdated
Comment thread src/asm/warning.cpp
@Rangi42 Rangi42 requested a review from ISSOtm May 11, 2026 13:07
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

enhancement Typically new features; lesser priority than bugs refactoring This PR is intended to clean up code more than change functionality rgbasm This affects RGBASM

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants