THRIFT-727 - C++: what() method of generated exceptions will provide more information
Patch: Anna Dymek <aadymek@gmail.com>
Client: C++
This closes #469
diff --git a/compiler/cpp/src/generate/t_cpp_generator.cc b/compiler/cpp/src/generate/t_cpp_generator.cc
index 3edd7c8..426434f 100644
--- a/compiler/cpp/src/generate/t_cpp_generator.cc
+++ b/compiler/cpp/src/generate/t_cpp_generator.cc
@@ -138,6 +138,7 @@
void generate_struct_result_writer(std::ofstream& out, t_struct* tstruct, bool pointers = false);
void generate_struct_swap(std::ofstream& out, t_struct* tstruct);
void generate_struct_ostream_operator(std::ofstream& out, t_struct* tstruct);
+ void generate_exception_what_method(std::ofstream& out, t_struct* tstruct);
/**
* Service-level generation functions
@@ -239,6 +240,9 @@
bool include_values);
void generate_struct_ostream_operator_decl(std::ofstream& f, t_struct* tstruct);
+ void generate_exception_what_method_decl(std::ofstream& f,
+ t_struct* tstruct,
+ bool external = false);
// These handles checking gen_dense_ and checking for duplicates.
void generate_local_reflection(std::ofstream& out, t_type* ttype, bool is_definition);
@@ -767,6 +771,9 @@
generate_move_assignment_operator(f_types_impl_, tstruct);
}
generate_struct_ostream_operator(f_types_impl_, tstruct);
+ if (is_exception) {
+ generate_exception_what_method(f_types_impl_, tstruct);
+ }
}
void t_cpp_generator::generate_copy_constructor(ofstream& out,
@@ -1113,7 +1120,15 @@
// ostream operator<<
out << indent() << "friend ";
generate_struct_ostream_operator_decl(out, tstruct);
- out << ";" << endl;
+ out << ";" << endl << endl;
+
+ // std::exception::what()
+ if (is_exception) {
+ out << indent() << "mutable std::string thriftTExceptionMessageHolder_;" << endl;
+ out << indent();
+ generate_exception_what_method_decl(out, tstruct, false);
+ out << ";" << endl;
+ }
indent_down();
indent(out) << "};" << endl << endl;
@@ -1626,6 +1641,16 @@
out << "std::ostream& operator<<(std::ostream& out, const " << tstruct->get_name() << "& obj)";
}
+void t_cpp_generator::generate_exception_what_method_decl(std::ofstream& out,
+ t_struct* tstruct,
+ bool external) {
+ out << "const char* ";
+ if (external) {
+ out << tstruct->get_name() << "::";
+ }
+ out << "what() const throw()";
+}
+
namespace struct_ostream_operator_generator {
void generate_required_field_value(std::ofstream& out, const t_field* field) {
out << " << to_string(obj." << field->get_name() << ")";
@@ -1700,6 +1725,37 @@
}
/**
+ * Generates what() method for exceptions
+ */
+void t_cpp_generator::generate_exception_what_method(std::ofstream& out, t_struct* tstruct) {
+ out << indent();
+ generate_exception_what_method_decl(out, tstruct, true);
+ out << " {" << endl;
+
+ indent_up();
+ out << indent() << "try {" << endl;
+
+ indent_up();
+ out << indent() << "std::stringstream ss;" << endl;
+ out << indent() << "ss << \"TException - service has thrown: \" << *this;" << endl;
+ out << indent() << "this->thriftTExceptionMessageHolder_ = ss.str();" << endl;
+ out << indent() << "return this->thriftTExceptionMessageHolder_.c_str();" << endl;
+ indent_down();
+
+ out << indent() << "} catch (const std::exception& e) {" << endl;
+
+ indent_up();
+ out << indent() << "return \"TException - service has thrown: " << tstruct->get_name() << "\";"
+ << endl;
+ indent_down();
+
+ out << indent() << "}" << endl;
+
+ indent_down();
+ out << "}" << endl << endl;
+}
+
+/**
* Generates a thrift service. In C++, this comprises an entirely separate
* header and source file. The header file defines the methods and includes
* the data types defined in the main header file, and the implementation
diff --git a/lib/cpp/test/processor/ProcessorTest.cpp b/lib/cpp/test/processor/ProcessorTest.cpp
index 655f481..40d926e 100644
--- a/lib/cpp/test/processor/ProcessorTest.cpp
+++ b/lib/cpp/test/processor/ProcessorTest.cpp
@@ -765,6 +765,9 @@
BOOST_FAIL("expected MyError to be thrown");
} catch (const MyError& e) {
BOOST_CHECK_EQUAL(message, e.message);
+ // Check if std::exception::what() is handled properly
+ size_t message_pos = std::string(e.what()).find("TException - service has thrown: MyError");
+ BOOST_CHECK_NE(message_pos, std::string::npos);
}
// Now we should see the events for a normal call finish
diff --git a/tutorial/c_glib/c_glib_server.c b/tutorial/c_glib/c_glib_server.c
index a20ab54..000d97c 100644
--- a/tutorial/c_glib/c_glib_server.c
+++ b/tutorial/c_glib/c_glib_server.c
@@ -231,7 +231,7 @@
setting the corresponding output parameter to a new instance
of its type and returning FALSE. */
*ouch = g_object_new (TYPE_INVALID_OPERATION,
- "what", op,
+ "whatOp", op,
"why", g_strdup ("Cannot divide by 0"),
NULL);
result = FALSE;
@@ -249,7 +249,7 @@
default:
*ouch = g_object_new (TYPE_INVALID_OPERATION,
- "what", op,
+ "whatOp", op,
"why", g_strdup ("Invalid Operation"),
NULL);
result = FALSE;
diff --git a/tutorial/cpp/CppClient.cpp b/tutorial/cpp/CppClient.cpp
index 258cbd6..2763fee 100644
--- a/tutorial/cpp/CppClient.cpp
+++ b/tutorial/cpp/CppClient.cpp
@@ -58,6 +58,7 @@
} catch (InvalidOperation& io) {
cout << "InvalidOperation: " << io.why << endl;
// or using generated operator<<: cout << io << endl;
+ // or by using std::exception native method what(): cout << io.what() << endl;
}
work.op = Operation::SUBTRACT;
diff --git a/tutorial/cpp/CppServer.cpp b/tutorial/cpp/CppServer.cpp
index 84b79d4..6b22193 100644
--- a/tutorial/cpp/CppServer.cpp
+++ b/tutorial/cpp/CppServer.cpp
@@ -71,7 +71,7 @@
case Operation::DIVIDE:
if (work.num2 == 0) {
InvalidOperation io;
- io.what = work.op;
+ io.whatOp = work.op;
io.why = "Cannot divide by 0";
throw io;
}
@@ -79,7 +79,7 @@
break;
default:
InvalidOperation io;
- io.what = work.op;
+ io.whatOp = work.op;
io.why = "Invalid Operation";
throw io;
}
diff --git a/tutorial/d/server.d b/tutorial/d/server.d
index d32b937..cbcedcc 100644
--- a/tutorial/d/server.d
+++ b/tutorial/d/server.d
@@ -62,7 +62,7 @@
case Operation.DIVIDE:
if (work.num2 == 0) {
auto io = new InvalidOperation();
- io.what = work.op;
+ io.whatOp = work.op;
io.why = "Cannot divide by 0";
throw io;
}
@@ -70,7 +70,7 @@
break;
default:
auto io = new InvalidOperation();
- io.what = work.op;
+ io.whatOp = work.op;
io.why = "Invalid Operation";
throw io;
}
diff --git a/tutorial/erl/server.erl b/tutorial/erl/server.erl
index 4915606..f1e7357 100644
--- a/tutorial/erl/server.erl
+++ b/tutorial/erl/server.erl
@@ -44,12 +44,12 @@
?tutorial_Operation_MULTIPLY -> Num1 * Num2;
?tutorial_Operation_DIVIDE when Num2 == 0 ->
- throw(#invalidOperation{what=Op, why="Cannot divide by 0"});
+ throw(#invalidOperation{whatOp=Op, why="Cannot divide by 0"});
?tutorial_Operation_DIVIDE ->
Num1 div Num2;
_Else ->
- throw(#invalidOperation{what=Op, why="Invalid operation"})
+ throw(#invalidOperation{whatOp=Op, why="Invalid operation"})
end.
getStruct(Key) ->
diff --git a/tutorial/haxe/src/CalculatorHandler.hx b/tutorial/haxe/src/CalculatorHandler.hx
index 3376fb6..e9752db 100644
--- a/tutorial/haxe/src/CalculatorHandler.hx
+++ b/tutorial/haxe/src/CalculatorHandler.hx
@@ -67,7 +67,7 @@
if (work.num2 == 0)
{
var io = new InvalidOperation();
- io.what = work.op;
+ io.whatOp = work.op;
io.why = "Cannot divide by 0";
throw io;
}
@@ -75,7 +75,7 @@
default:
var io = new InvalidOperation();
- io.what = work.op;
+ io.whatOp = work.op;
io.why = "Unknown operation";
throw io;
}
diff --git a/tutorial/hs/HaskellServer.hs b/tutorial/hs/HaskellServer.hs
index 77f1679..cfe1344 100644
--- a/tutorial/hs/HaskellServer.hs
+++ b/tutorial/hs/HaskellServer.hs
@@ -74,7 +74,7 @@
if num2 work == 0 then
throw $
InvalidOperation {
- invalidOperation_what = fromIntegral $ fromEnum $ op work,
+ invalidOperation_whatOp = fromIntegral $ fromEnum $ op work,
invalidOperation_why = "Cannot divide by 0"
}
else
diff --git a/tutorial/java/src/CalculatorHandler.java b/tutorial/java/src/CalculatorHandler.java
index 4216be5..92944b0 100644
--- a/tutorial/java/src/CalculatorHandler.java
+++ b/tutorial/java/src/CalculatorHandler.java
@@ -58,7 +58,7 @@
case DIVIDE:
if (work.num2 == 0) {
InvalidOperation io = new InvalidOperation();
- io.what = work.op.getValue();
+ io.whatOp = work.op.getValue();
io.why = "Cannot divide by 0";
throw io;
}
@@ -66,7 +66,7 @@
break;
default:
InvalidOperation io = new InvalidOperation();
- io.what = work.op.getValue();
+ io.whatOp = work.op.getValue();
io.why = "Unknown operation";
throw io;
}
diff --git a/tutorial/nodejs/NodeServer.js b/tutorial/nodejs/NodeServer.js
index ba36105..55d3817 100644
--- a/tutorial/nodejs/NodeServer.js
+++ b/tutorial/nodejs/NodeServer.js
@@ -48,7 +48,7 @@
} else if (work.op === ttypes.Operation.DIVIDE) {
if (work.num2 === 0) {
var x = new ttypes.InvalidOperation();
- x.what = work.op;
+ x.whatOp = work.op;
x.why = 'Cannot divide by 0';
result(x);
return;
@@ -56,7 +56,7 @@
val = work.num1 / work.num2;
} else {
var x = new ttypes.InvalidOperation();
- x.what = work.op;
+ x.whatOp = work.op;
x.why = 'Invalid operation';
result(x);
return;
diff --git a/tutorial/nodejs/NodeServerPromise.js b/tutorial/nodejs/NodeServerPromise.js
index 4fd0a35..bff287b 100644
--- a/tutorial/nodejs/NodeServerPromise.js
+++ b/tutorial/nodejs/NodeServerPromise.js
@@ -47,14 +47,14 @@
} else if (work.op === ttypes.Operation.DIVIDE) {
if (work.num2 === 0) {
var x = new ttypes.InvalidOperation();
- x.what = work.op;
+ x.whatOp = work.op;
x.why = 'Cannot divide by 0';
throw x;
}
val = work.num1 / work.num2;
} else {
var x = new ttypes.InvalidOperation();
- x.what = work.op;
+ x.whatOp = work.op;
x.why = 'Invalid operation';
throw x;
}
diff --git a/tutorial/ocaml/CalcServer.ml b/tutorial/ocaml/CalcServer.ml
index 24d7d03..b5facb7 100755
--- a/tutorial/ocaml/CalcServer.ml
+++ b/tutorial/ocaml/CalcServer.ml
@@ -49,7 +49,7 @@
| Operation.DIVIDE ->
if w#grab_num2 = Int32.zero then
let io = new invalidOperation in
- io#set_what (Operation.to_i w#grab_op) ;
+ io#set_whatOp (Operation.to_i w#grab_op) ;
io#set_why "Cannot divide by 0" ;
raise (InvalidOperation io)
else
diff --git a/tutorial/perl/PerlServer.pl b/tutorial/perl/PerlServer.pl
index a40ec69..adec978 100644
--- a/tutorial/perl/PerlServer.pl
+++ b/tutorial/perl/PerlServer.pl
@@ -68,14 +68,14 @@
if ($num2 == 0)
{
my $x = new tutorial::InvalidOperation;
- $x->what($op);
+ $x->whatOp($op);
$x->why('Cannot divide by 0');
die $x;
}
$val = $num1 / $num2;
} else {
my $x = new tutorial::InvalidOperation;
- $x->what($op);
+ $x->whatOp($op);
$x->why('Invalid operation');
die $x;
}
diff --git a/tutorial/php/PhpServer.php b/tutorial/php/PhpServer.php
index 4af70a4..22ae43e 100755
--- a/tutorial/php/PhpServer.php
+++ b/tutorial/php/PhpServer.php
@@ -79,7 +79,7 @@
case \tutorial\Operation::DIVIDE:
if ($w->num2 == 0) {
$io = new \tutorial\InvalidOperation();
- $io->what = $w->op;
+ $io->whatOp = $w->op;
$io->why = "Cannot divide by 0";
throw $io;
}
@@ -87,7 +87,7 @@
break;
default:
$io = new \tutorial\InvalidOperation();
- $io->what = $w->op;
+ $io->whatOp = $w->op;
$io->why = "Invalid Operation";
throw $io;
}
diff --git a/tutorial/py.tornado/PythonServer.py b/tutorial/py.tornado/PythonServer.py
index 52932ff..7a34107 100755
--- a/tutorial/py.tornado/PythonServer.py
+++ b/tutorial/py.tornado/PythonServer.py
@@ -62,13 +62,13 @@
elif work.op == Operation.DIVIDE:
if work.num2 == 0:
x = InvalidOperation()
- x.what = work.op
+ x.whatOp = work.op
x.why = "Cannot divide by 0"
raise x
val = work.num1 / work.num2
else:
x = InvalidOperation()
- x.what = work.op
+ x.whatOp = work.op
x.why = "Invalid operation"
raise x
diff --git a/tutorial/py.twisted/PythonServer.py b/tutorial/py.twisted/PythonServer.py
index f023cac..227f6d4 100755
--- a/tutorial/py.twisted/PythonServer.py
+++ b/tutorial/py.twisted/PythonServer.py
@@ -59,13 +59,13 @@
elif work.op == Operation.DIVIDE:
if work.num2 == 0:
x = InvalidOperation()
- x.what = work.op
+ x.whatOp = work.op
x.why = 'Cannot divide by 0'
raise x
val = work.num1 / work.num2
else:
x = InvalidOperation()
- x.what = work.op
+ x.whatOp = work.op
x.why = 'Invalid operation'
raise x
diff --git a/tutorial/py/PythonServer.py b/tutorial/py/PythonServer.py
index 014a12e..533b0ea 100755
--- a/tutorial/py/PythonServer.py
+++ b/tutorial/py/PythonServer.py
@@ -56,13 +56,13 @@
elif work.op == Operation.DIVIDE:
if work.num2 == 0:
x = InvalidOperation()
- x.what = work.op
+ x.whatOp = work.op
x.why = 'Cannot divide by 0'
raise x
val = work.num1 / work.num2
else:
x = InvalidOperation()
- x.what = work.op
+ x.whatOp = work.op
x.why = 'Invalid operation'
raise x
diff --git a/tutorial/rb/RubyServer.rb b/tutorial/rb/RubyServer.rb
index 22cec31..1d707d7 100755
--- a/tutorial/rb/RubyServer.rb
+++ b/tutorial/rb/RubyServer.rb
@@ -52,14 +52,14 @@
elsif work.op == Operation::DIVIDE
if work.num2 == 0
x = InvalidOperation.new()
- x.what = work.op
+ x.whatOp = work.op
x.why = "Cannot divide by 0"
raise x
end
val = work.num1 / work.num2
else
x = InvalidOperation.new()
- x.what = work.op
+ x.whatOp = work.op
x.why = "Invalid operation"
raise x
end
diff --git a/tutorial/tutorial.thrift b/tutorial/tutorial.thrift
index c8710d4..571ab8e 100644
--- a/tutorial/tutorial.thrift
+++ b/tutorial/tutorial.thrift
@@ -113,7 +113,7 @@
* Structs can also be exceptions, if they are nasty.
*/
exception InvalidOperation {
- 1: i32 what,
+ 1: i32 whatOp,
2: string why
}