Error handling in protobuf - c++

I am new to protobuf (C++) and try to write first test program with protobuf.
Code is
#include <cstdlib>
#include <iostream>
#include "proto/req.pb.h"
using namespace std;
using namespace google::protobuf;
int main(int argc, char** argv) {
std::string s = "asdfasdf";
auto MyLogHandler = [] (google::protobuf::LogLevel level, const char* filename, int line, const std::string& message)
{
std::cout << "message " << message << std::endl;
};
google::protobuf::SetLogHandler(MyLogHandler);
Request req;
if ( req.ParseFromString(s)){
cout << "Parse - OK" << endl;
}else{
cout << "Parse - ERROR" << endl;
}
return 0;
}
When program runs - it show only error message, but not any reason about it. How can I get a reason for the error?

There are two reasons Protobuf parsing can fail:
The input data is missing required fields. In this case the Protobuf library writes a log message describing the problem and then returns false. Your log handler will receive the error message in this case.
The input data is not a valid Protobuf (it is corrupt, or was never a protobuf in the first place). In this case the Protobuf library simply returns false without any error message. There is really no useful information that the library could provide here. If this happens to you, the best way to debug is to dump the exact bytes right after you serialized the message and right before you parsed it, then look for differences.

Related

Display runtime error message in gRPC server side and pass it to the client

I am using gRPC in a project where I have to set and get values from some separate/outside functions. Some functions has case that if they get unwanted value they will throw runtime error. By following this I have got an idea to catch a error_state from inside of the gRPC function.
I am giving here some of my approach. A demo source is this
proto file where only including here the client message part
message NameQuery {
string name = 1;
int32 cl_value = 2; // This is a user input data which will be passed to the server and then to a outside function
client/src/main.cpp
int main(int argc, char* argv[])
{
// Setup request
expcmake::NameQuery query;
expcmake::Address result;
query.set_name("John");
int x;
cout << "give value of x: ";
cin>> x;
query.set_cl_value(x);
// remaining are as like as before
server/src/main.cpp
#include <iostream>
using namespace std;
void Check_Value(const ::expcmake::NameQuery* request)
{
if (request->cl_value() < 5)
cout << "request->cl_value(): " << request->cl_value() << endl;
else
throw std::runtime_error("********** BAD VALUE **********");
}
class AddressBookService final : public expcmake::AddressBook::Service {
public:
virtual ::grpc::Status GetAddress(::grpc::ServerContext* context, const ::expcmake::NameQuery* request, ::expcmake::Address* response)
{
std::cout << "Server: GetAddress for \"" << request->name() << "\"." << std::endl;
Check_Value(request);
// remaining are as like as before
After building the project if from client side 5 or greater than 5 is given server didn't show any message but running continuously(which is obvious for the Wait function of gRPC) where my expectation was it should print in server console
********** BAD VALUE **********
Though, in client side I have got all passed value as BLANK where I can assume that, server didn't perform any process after the runtime_error.
So my query is:
1/ How can I see the runtime_error message in server side console?
2/ Any gRPC default system to pass this incident to the client (Any generic message).
Note: In my real example this runtime_error message related function is coming from another project where I cannot access to modify.
Found the solution.
In server side I have missed to use the try ... catch in a right way.
server.cpp
try
{
Check_Value(request);
}
catch(const std::exception& e)
{
std::cout << e.what() << std::endl; // This will display the error message in server console
return grpc::Status(grpc::StatusCode::INVALID_ARGUMENT, e.what());
}
An awesome resource regarding error handler in client side is found here.
client.cpp
// Class instance, message instances declaration
grpc::Status status = stub->GetAddress(&context, request_, &response);
if (status.ok())
// print server response, do the required job
else
std::cout << status.error_message() << std::endl;

Add values to map using rapidjson

I get a raw json string
{"vehicle": {"brand": "zonda","color": "blue"},"username": {"brand": "doyota","color": "red"}}
from a get call i make.
I read that rapidjson is the best way to parse a json string in cpp.
So I tried doing something like this:
const char* json = data.c_str();
rapidjson::Document document;
if (document.Parse(json).HasParseError()) {
cout << "has parse error" << endl;
return 1;
}
else {
assert(document.IsObject());
}
Here it says that the json has a parse error. Any idea why this could be?
Also once I am able to parse the values I want to add them as key value pairs to a standard map. Could anyone point me in the right direction to proceed with this?
This gave me no error:
#include <iostream>
#include "rapidjson/document.h"
#include "rapidjson/error/en.h"
using namespace rapidjson;
int main() {
Document d;
std::string json = R"raw({"vehicle": {"brand": "zonda","color": "blue"},"username": {"brand": "doyota","color": "red"}})raw";
if (d.Parse(json.c_str()).HasParseError()) {
std::cout << "has error\n";
} else {
std::cout << "no error\n";
}
}
Tried C++11 - C++20 and it all seems fine. My guess is that you've got some non UTF8 character in the data.

After convert string to const *json, when pasing json object, shows failed: (IsObject()), how to solve this?

After convert string strjson to const char* json, when interate, shows
failed: (IsObject()), function FindMember,failed, I don't understand why showed this, I think this the json object is correct format.
//
// main.cpp
// rapid
//
// Created by Shi Yan on 10/7/17.
// Copyright © 2017 Shi Yan. All rights reserved.
//
#include <iostream>
#include "rapidjson.h"
#include "document.h"
#include <fstream>
using namespace std;
using namespace rapidjson;
void readjson(){
ifstream handle("meta_Books.json");
if(handle.is_open()){
//cout<<"open success"<<endl;
const char* json;
string strjson;
int i=1;
while(getline(handle,strjson)){
if(i>4)
break;
cout<<strjson<<endl;
cout<<strjson.length()<<endl;
i++;
json=strjson.c_str();
cout<<"*********************"<<endl;
cout<<*json<<endl;
StringStream s (json);
Document document;
document.ParseStream(s);
Value::ConstMemberIterator itr = document.FindMember("asin");
cout<<itr->name.GetString()<<" = "<< itr->value.GetString()<<endl;
}
}
}
int main() {
readjson();
return 0;
}
I think the format of json object , so why failed?
As you can see , the getline() method works well, because the output of string is an complete string
The assertion error means that FindMember() is being called on a Value that does not represent a JSON object (IsObject() is false).
Since there is only 1 FindMember() in the code you showed, that implies that document.IsObject() is false when document.FindMember() fails. Either the JSON you are parsing does not start with an object in its root, or the parse failed. Neither condition of which you are testing for in your code.
If I had to guess (and please don't make people guess!), the failing JSON document likely contains an unencoded line break in it (that is not illegal inside of JSON string values). That would cause std::getline() to exit prematurely, thus causing parsing issues.
The 1st screenshot you showed supports that conclusion, showing that strjson is being split between 2 separate "lines" when the error occurs.
Rather than using std::getline() to read the file line-by-line, risking errors on embedded line breaks, I suggest you try using RapidJSON's BasicIStreamWrapper class to read the file document-by-document instead. ParseStream() has a kParseStopWhenDoneFlag flag that allows parsing multiple root documents from a single input stream:
kParseStopWhenDoneFlag 
After parsing a complete JSON root from stream, stop further processing the rest of stream. When this flag is used, parser will not generate kParseErrorDocumentRootNotSingular error.
For example:
#include <iostream>
#include <fstream>
#include "rapidjson.h"
#include "document.h"
#include "istreamwrapper.h"
using namespace std;
using namespace rapidjson;
void readjson()
{
ifstream handle("meta_Books.json");
if (!handle.is_open())
{
// handle error...
cout << "error opening file" << endl;
}
else
{
BasicIStreamWrapper<ifstream> s(handle);
for(int i = 1; i <= 4; ++i)
{
Document document;
ParseResult pr = document.ParseStream<kParseStopWhenDoneFlag>(s);
if (!pr)
{
// handle error...
cout << "error parsing document " << i << endl;
}
else if (!document.IsObject())
{
cout << "document " << i << " is not an object" << endl;
}
else
{
Value::ConstMemberIterator itr = document.FindMember("asin");
if (itr != document.MemberEnd())
cout << "asin = " << itr->value.GetString() << endl;
else
cout << "asin not found" << endl;
}
}
}
}
int main()
{
readjson();
return 0;
}

boost::filesystem::create_directories(".") fails

boost::filesystem::create_directories(".") always fails on my system. It seems like this might be a bug, but after reading the documentation I'm not entirely sure. Here's an example program:
#include <iostream>
#include <boost/filesystem.hpp>
int main(int argc, char* argv[])
try
{
namespace fs = boost::filesystem;
std::cerr << "is_directory: " << fs::is_directory(argv[1]) << '\n';
std::cerr << "create_directory: " << fs::create_directory(argv[1]) << '\n';
std::cerr << "create_directories: " << fs::create_directories(argv[1]) << '\n';
}
catch (const std::exception& ex)
{
std::cerr << ex.what() << '\n';
}
If I run this with the argument . (meaning the current directory), it prints:
is_directory: 1
create_directory: 0
boost::filesystem::create_directories: Invalid argument
The first two lines are unsurprising: . is a directory, and create_directory() is documented saying:
Creation failure because p resolves to an existing directory shall not be treated as an error.
But the third line is a surprise to me: create_directories(".") failed even though . exists. There is no such failure if you use a different name, like foo - it will happily create that directory or just return false if it exists already.
The docs have this to say:
Effect: Establishes the postcondition by calling create_directory() for any element of p that does not exist.
Postcondition: is_directory(p)
We have shown that the postcondition is true beforehand, so why does it fail?
Edit: I'm using Boost 1.63. I see now that a commit three months ago to "Fix #12495" probably causes this problem: https://github.com/boostorg/filesystem/commit/216720de55359828c2dc915b50e6ead44e00cd15 - and even includes a unit test which demands that create_directories(".") must fail while create_directory(".") must succeed, which is bizarre .

Linux command to find a c++ method

I am new on Linux and would like to know the command line that would allow me to have see the interface of the smthg::smthg1::smthg2::smthg3() method in order to see if I am correctly catching an exception.
You can launch the following command to find that :
grep -rn "smthg::smthg1::smthg2::smthg3()"
It will find the all lines matching the specified pattern.
Edit : One way to know if you catch an exception at runtime, it's to print one message like this :
#include <iostream>
using namespace std;
int main(int argc, char **argv) {
try {
throw 20;
}catch(int x) {
cout << "Exception catch with status " << x << endl;
}
return 0;
}
If the message is printed then the exception is catched else it's not.