I want to catch c++ exceptions that comes from third-party c++ lib that I use in my nodejs addon:
// hello.cc
#include <node.h>
#include <v8.h>
#include <string>
using namespace v8;
using namespace std;
void Method(const v8::FunctionCallbackInfo<Value>& args) {
try {
std::string s("abc");
throw std::runtime_error(s); // third-party c++ lib throw this, I want to catch and get message
}
catch (const std::runtime_error &e) {
printf("err is %s\n", e.what()); // e.what() is empty here!
}
}
void Init(Handle<Object> exports) {
Isolate* isolate = Isolate::GetCurrent();
exports->Set(String::NewFromUtf8(isolate, "hello"),
FunctionTemplate::New(isolate, Method)->GetFunction());
}
NODE_MODULE(hello, Init)
I use Visual Studio 2013 and I enabled c++ exception like this:
// binding.gyp
{
"targets": [
{
"target_name": "hello",
"sources": [ "hello.cc" ],
"msvs_settings": {
"VCCLCompilerTool": {
"ExceptionHandling": 1
}
}
}
]
}
Then I call my addon like this:
// hello.js
var addon = require('./build/Release/hello');
addon.hello();
And here is output:
err is
exception message is empty! It should contain "abc" string. When I remove msvs_settings section from binding.gyp file it works as expected(output abc string) but compilation gives warning C4530. I don't understand why it doesn't work when I enable exception handling. Looks like when I enable exceptions string is destroyed during stack unwind. However when create new c++ console app(c++ exceptions enabled by default) same code works fine(output abc string). Could you please help me to understand what is wrong?
Node and V8 are compiled without exceptions and RTTI so I assume it has something to do with that.
Beyond that, I can't say, I don't use Windows myself. On UNIX platforms, it's critical that you never let the exception cross over into -fno-exceptions code but you don't seem to be doing that.
I think the reason is that the string s is destructed before e.what() is called in case "ExceptionHandling": 1 is set. The std::runtime_error class holds just a pointer to s.c_str() so in the end it's most likely luck that it didn't crash.
Not specifying ExceptionHandling in binding.gyp falls back to /EH which means exceptions are handled but no objects are destroyed (usually not what you want).
"ExceptionHandling": 1 sets /EHsc which is usually what you want; see https://msdn.microsoft.com/en-us/library/1deeycx5.aspx.
If you move the string s outside of the try block it will work as expected.
BTW, I tested this with MSVC 2015.
Seems the std::runtime_error implementation in GCC is significant different - it holds a std::string so it holds a copy and therefore your code will be fine there.
I was wondering about the behaviour I described above so I tried to create a standalone reproducer. Using a "default" VS project above code works, the string is copied.
But for addon builds _HAS_EXCEPTIONS==0 is set which changes the behaviour of MSVC STL to above described behaviour.
@Flarna thank you very much, It works as expected when I set _HAS_EXCEPTIONS=1(or remove this define). I updated binding.gyp like this;
{
"targets": [
{
"target_name": "hello",
"sources": [ "hello.cc" ],
"msvs_settings": {
"VCCLCompilerTool": {
"ExceptionHandling": 1
}
},
"conditions": [
["OS=='win'", {
"defines": [
"_HAS_EXCEPTIONS=1"
]
}]
]
}
]
}
Most helpful comment
@Flarna thank you very much, It works as expected when I set _HAS_EXCEPTIONS=1(or remove this define). I updated binding.gyp like this;