Throw instead of exiting from the tile clipping and overzooming code

The code reachable from overzooming a tile no longer calls exit(). It
reports errors by throwing tippecanoe_error, and owns its FILE * and
json_pull * handles through RAII wrappers so that nothing leaks when an
error unwinds the stack.

New files:

* errors.hpp/errors.cpp: the tippecanoe_error exception class, plus
  throw_tippecanoe_error() and throw_perror() helpers.
* raii.hpp: unique_file and unique_json_pull, both std::unique_ptr
  aliases, for FILE * and json_pull *.

exit() becomes throw in clip.cpp, mvt.cpp, mvt.hpp, read_json.cpp,
evaluator.cpp, text.cpp, geometry.cpp, attribute.cpp, and
write_json.hpp. Each tool's main() is now a try/catch around an
inner_main(), so the command-line tools exit with the status they
always did.

Along the way this fixes a z_stream leaked when compress() failed, an
exit() that could run from json_writer's destructor during unwinding,
and an exit() in mvt_value's hash function.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01DuCSVGssWNUCpVEE8Yp5HT
This commit is contained in:
Claude
2026-08-07 18:46:09 +00:00
parent 734bba7c78
commit 85547150c2
19 changed files with 272 additions and 170 deletions
+36
View File
@@ -1,3 +1,6 @@
#ifndef ERRORS_HPP
#define ERRORS_HPP
#define EXIT_INCOMPLETE 100
#define EXIT_ARGS 101
#define EXIT_CLOSE 102
@@ -21,3 +24,36 @@
#define EXIT_WRITE 120
// avoid 124, 125, 126, 127, 137, which are used by GNU timeout
#include <stdexcept>
#include <string>
#include <exception>
class tippecanoe_error : public std::runtime_error {
public:
int exit_code;
tippecanoe_error(int code, const std::string &message)
: std::runtime_error(message), exit_code(code) {
}
};
// Format and throw a tippecanoe_error. Use in place of fprintf(stderr,...)+exit().
[[noreturn]] void throw_tippecanoe_error(int code, const char *fmt, ...)
__attribute__((format(printf, 2, 3)));
// Throw from errno-based errors (replaces perror()+exit() pattern)
[[noreturn]] void throw_perror(int code, const char *msg);
// Check the void* return value from pthread_join. If a thread caught an
// exception, the return value is a heap-allocated std::exception_ptr.
// This re-throws it on the joining thread, or does nothing if retval is NULL.
inline void rethrow_if_thread_failed(void *retval) {
if (retval != NULL) {
std::exception_ptr *ep = (std::exception_ptr *) retval;
std::exception_ptr copy = *ep;
delete ep;
std::rethrow_exception(copy);
}
}
#endif