diff --git a/Doc/Manual/C.html b/Doc/Manual/C.html index 356380210..03ac9954a 100644 --- a/Doc/Manual/C.html +++ b/Doc/Manual/C.html @@ -683,6 +683,10 @@ void SomeIntTemplateClass_delete(SomeIntTemplateClass * carg1);

36.5 Exception handling

+

+Any call to a C++ function may throw an exception, which cannot be caught by C code. Instead, the special SWIG_CException_get_pending() function must be called to check for this. If it returns a non-null pointer, SWIG_CException_msg_get() can be called to retrieve the error message associated with the exception. Finally, SWIG_CException_reset_pending() must be called to free the exception object and reset the current pending exception. Note that exception handling is much simpler when using C++, rather than C, wrappers, see sections 36.6.2. +

+

36.6 C++ Wrappers

@@ -702,8 +706,52 @@ Other ones are due to things that could be supported but haven't been implemente

-Note that cxxheader section can be used to output additional -declarations to the C++-only part of the generated header. +

36.6.1 Additional sections

+ +Generated C++ code can be customized by inserting custom code in the following sections: + + + +

36.6.2 Exception handling

+ +

+Exception handling in C++ is more natural, as the exceptions are re-thrown when using C++ wrappers and so can be caught, as objects of the special SWIG_CException type, using the usual try/catch statement. The objects of SWIG_CException class have code() and msg() methods, with the latter returning the error message associated with the exception. +

+ +

+If necessary, a custom exception type may be used instead of SWIG_CException. To do this, a custom implementation of swig_check() function, called to check for the pending exception and throw the corresponding C++ exception if necessary, must be provided and SWIG_swig_check_DEFINED preprocessor symbol must be defined to prevent the default implementation of this function from being compiled: +

+
+%insert(cxxheader) %{
+#ifndef SWIG_swig_check_DEFINED
+#define SWIG_swig_check_DEFINED 1
+
+#include 
+
+class Exception : public std::runtime_error {
+public:
+    explicit Exception(const char* msg) : std::runtime_error{msg} {}
+};
+
+inline void swig_check() {
+  if (auto* swig_ex = SWIG_CException_get_pending()) {
+    Exception const e{SWIG_CException_msg_get(swig_ex)};
+    SWIG_CException_reset_pending();
+    throw e;
+  }
+}
+
+template  T swig_check(T x) {
+  swig_check();
+  return x;
+}
+
+#endif // SWIG_swig_check_DEFINED
+%}
+
diff --git a/Lib/c/cexcept.swg b/Lib/c/cexcept.swg index 8c9545657..57171273d 100644 --- a/Lib/c/cexcept.swg +++ b/Lib/c/cexcept.swg @@ -62,6 +62,39 @@ SWIGEXPORTC void SWIG_CException_Raise(int code, const char* msg) { #endif // SWIG_CException_DEFINED %} +#ifdef SWIG_CXX_WRAPPERS + +// This is somewhat of a hack, but our generated header may include another +// generated header, when using multiple modules, and defining swig_check() in +// all of them would result in errors, so we use SWIG_swig_check_DEFINED to +// prevent this from happening. +// +// This also has a nice side effect of allowing the user code to predefine this +// symbol and provide their own SWIG_swig_check_DEFINED implementation to +// customize exception handling. +%insert("cxxcode") %{ +#ifndef SWIG_swig_check_DEFINED +#define SWIG_swig_check_DEFINED 1 + +inline void swig_check() { + if (SWIG_CException* swig_ex = SWIG_CException::get_pending()) { + SWIG_CException swig_ex_copy{*swig_ex}; + delete swig_ex; + SWIG_CException::reset_pending(); + throw swig_ex_copy; + } +} + +template T swig_check(T x) { + swig_check(); + return x; +} + +#endif // SWIG_swig_check_DEFINED +%} + +#endif // SWIG_CXX_WRAPPERS + %insert("runtime") "swigerrors.swg" #define SWIG_exception(code, msg)\ diff --git a/Source/Modules/c.cxx b/Source/Modules/c.cxx index 073b95bc2..120a9fe1c 100644 --- a/Source/Modules/c.cxx +++ b/Source/Modules/c.cxx @@ -419,6 +419,7 @@ struct cxx_wrappers // Allow using SWIG directive to inject code here. Swig_register_filebyname("cxxheader", sect_cxx_h); + Swig_register_filebyname("cxxcode", sect_impls); } // This function must be called after initialize(). The two can't be combined because we don't yet know if we're going to use exceptions or not when we @@ -426,26 +427,6 @@ struct cxx_wrappers void initialize_exceptions(exceptions_support support) { switch (support) { case exceptions_support_enabled: - // Generate the functions which will be used in all wrappers to check for the exceptions only in this case, i.e. do not do it if they're already defined - // in another module imported by this one. - Printv(sect_impls, - "inline void swig_check() {\n", - cindent, "if (SWIG_CException* swig_ex = SWIG_CException::get_pending()) {\n", - cindent, cindent, "SWIG_CException swig_ex_copy{*swig_ex};\n", - cindent, cindent, "delete swig_ex;\n", - cindent, cindent, "SWIG_CException::reset_pending();\n", - cindent, cindent, "throw swig_ex_copy;\n", - cindent, "}\n", - "}\n\n", - "template T swig_check(T x) {\n", - cindent, "swig_check();\n", - cindent, "return x;\n", - "}\n\n", - NIL - ); - - // fall through - case exceptions_support_imported: except_check_start = "swig_check("; except_check_end = ")"; @@ -1756,6 +1737,8 @@ public: Preprocessor_define("SWIG_C_EXCEPT 1", 0); if (CPlusPlus) Preprocessor_define("SWIG_CPPMODE 1", 0); + if (use_cxx_wrappers) + Preprocessor_define("SWIG_CXX_WRAPPERS 1", 0); SWIG_library_directory("c");