we are facing an issue where the RAPIDJSON_ASSERT(IsObject()) called by MemberEnd() which is called by HasMember() fails. However, that rapidjson::Value is guaranteed to be an object by other logic.
Here is the code snippet:
const std::string str = "{\"outer_key\":{\"inner_key\":\"value\", \"foo\":\"bar\"}}";
rapidjson::Document doc;
if (doc.Parse(str.c_str()).HasParseError()) {
return -1;
}
rapidjson::Value::MemberIterator it = doc.FindMember("outer_key");
// make sure the member is of Object type
if (it == doc.MemberEnd() || !it->value.IsObject()) {
return -1;
}
rapidjson::Value* p_json;
p_json = &(it->value);
rapidjson::Document new_doc;
rapidjson::Document::AllocatorType& new_doc_allocator = new_doc.GetAllocator();
// if SOME_FLAG is set and "outer_key" exists in the input JSON string,
// use "outer_key"'s value so that `new_doc` will have other properties such as `foo` for free.
if (SOME_FLAG && p_json != NULL) {
new_doc.CopyFrom(*p_json, new_doc_allocator);
// The value of "inner_key" will be re-added later, so remove it for now.
// assert(IsObject()) fails here and core is dumped.
if (new_doc.HasMember("inner_key")) {
new_doc.RemoveMember("inner_key");
}
} else {
// Otherwise, start from an empty object.
new_doc.SetObject();
}
// Add "inner_key" for both cases above.
const std::string new_value_str = "new_value";
new_doc.AddMember("inner_key", rapidjson::StringRef(new_value_str.c_str()), new_doc_allocator);
We suspect that the new_doc.CopyFrom(*p_json, new_doc_allocator); line is the culprit, but are kind of new to rapidjson and not sure whether it is the correct way to use new_doc_allocator to copy *p_json to new_doc.
The weird part is that this issue only happened online for a small number of requests. We extracted the input JSON string, checked that it is valid, but couldn't reproduce the failure offline.
It would be good if someone could point out where we did wrong. Thanks in advance.
Related
I have a TADOConnection pointing to a MySQL 8.0 instance. The connection is tested and it works. Following this example on how to use prepared statement, I'm having an error and I have no idea why.
The following code works fine, it will return true from the very last statement. No errors, no warnings.
AnsiString sqlQuery = "SELECT e.name FROM employee e WHERE e.id = 1;";
if (!_query->Connection->Connected) {
try {
_query->Connection->Connected = true;
} catch (EADOError& e) {
return false;
}
}
_query->SQL->Clear();
_query->SQL->Add(sqlQuery);
_query->Prepared = true;
try {
_query->Active = true;
if (_query->RecordCount == 0) {
return false;
}
} catch (EADOError& e) {
return false;
}
return true;
However, the following code fails executing _query->SQL->Add(sqlQuery); with this error:
Arguments are of the wrong type, are out of acceptable range, or are in conflict with one another.
AnsiString sqlQuery = "SELECT e.name FROM employee e WHERE e.id = :id;";
if (!_query->Connection->Connected) {
try {
_query->Connection->Connected = true;
} catch (EADOError& e) {
return false;
}
}
_query->SQL->Clear();
_query->SQL->Add(sqlQuery); // <---- EOleException here
_query->Parameters->ParamByName("id")->Value = id;
_query->Prepared = true;
try {
_query->Active = true;
if (_query->RecordCount == 0) {
return false;
}
} catch (EADOError& e) {
return false;
}
return true;
Everywhere I find examples, all of them use :paramName to specify parameters. What am I missing?
Update 1
I have tried changing the code like this :
_query->SQL->Clear();
TParameter * param = _query->Parameters->AddParameter();
param->Name = "id";
param->Value = 1;
_query->SQL->Add(sqlQuery); // <---- EOleException still here
Some forum post suggests to switch the Advanced Compiler option "Register Variables" to "None", but this is already the setting of my project, and the exception is still thrown.
Update 2
I can ignore the error, and everything gets executed just fine, however it fails whenever I perform a step-by-step execution. Of course, I can still put a breakpoint after, and jump right over the faulty line, but it's still annoying and does not explain why there is this error there in the first place.
The exception is on setting the SQL string - which tells you that it's wrong. As per #RogerCigol's comment, you should NOT have the ; at the end of your SQL string.
Kudos to Roger for that.
If you want to access parameters, you MUST set the SQL string first, it will be parsed to identify the parameters. The parameters will not exist until the string is parsed, or you manually create them (which is pointless as they would be recreated on parsing the string).
You can also access the parameters as an ordered index, and I have always been able to use ? as an anonymous parameter with MySQL.
So the question explains the problem...
Background:
I'm trying to solve this problem from HackerRank.
It's basically an html tag parser. Valid input guaranteed, attributes are strings only.
My Approach
I created a custom Tag class that can store a map<string,Tag> of other Tag's, as well as a map<string,string> of attributes. The parsing seems to be working correctly.
The Problem
During the querying part, I get a BAD_ACCESS error on the following query/html combo:
4 1
<a value = "GoodVal">
<b value = "BadVal" size = "10">
</b>
</a>
a.b~size
The error occurs when I try to access the b Tag from a. Specifically, it's in the t=t.tags[tag_name], Line 118 below.
Code
#include <cmath>
#include <cstdio>
#include <vector>
#include <iostream>
#include <algorithm>
#include <sstream>
#include <map>
#include <stack>
using namespace std;
class Tag {
public:
Tag(){};
Tag(string name):name(name){};
string name;
map<string,Tag> tags = map<string, Tag>();
map<string,string> attribs=map<string,string>();
};
int main() {
int lines, queries;
std::cin>>lines>>queries;
std:string str;
getline(cin, str);
stack<string> open;
auto tags = map<string, Tag>();
for (int i = 0; i < lines; i++) {
getline(cin, str);
if (str.length()>1){
// If it's not </tag>, then it's an opening tag
if (str[1] != '/') {
// Parse tag name
auto wordidx = str.find(" ");
if (wordidx == -1) {
wordidx = str.length()-1.f;
}
string name = str.substr(1,wordidx-1);
auto t = Tag(name);
string sub = str.substr(wordidx);
auto equalidx=sub.find("=");
// Parse Attributes
while (equalidx != std::string::npos) {
string key = sub.substr(1,equalidx-2);
sub = sub.substr(equalidx);
auto attrib_start = sub.find("\"");
sub = sub.substr(attrib_start+1);
auto attrib_end = sub.find("\"");
string val = sub.substr(0, attrib_end);
sub = sub.substr(attrib_end+1);
t.attribs[key] = val;
equalidx=sub.find("=");
}
// If we're in a tag, push to that, else push to the base tags
if (open.size() == 0) {
tags[name] = t;
} else {
tags[open.top()].tags[name]=t;
}
open.push(name);
} else {
// Pop the stack if we reached a closing tag
auto wordidx = str.find(">");
string name = str.substr(2,wordidx-2);
// Sanity check, but we're assuming valid input
if (name.compare(open.top())) {
cout<<"FUCK"<<name<<open.top()<<endl;
return 9;
}
open.pop();
}
} else {
std::cout<<"FUCK\n";
}
}
//
// Parse in queries
//
for (int i = 0; i < queries; i++) {
getline(cin, str);
Tag t = Tag();
bool defined = false;
auto next_dot = str.find(".");
while (next_dot!=string::npos) {
string name = str.substr(0,next_dot);
if (defined && t.tags.find(name) == t.tags.end()) {
//TAG NOT IN T
cout<<"Not Found!"<<endl;
continue;
}
t = !defined ? tags[name] : t.tags[name];
defined = true;
str = str.substr(next_dot+1);
next_dot = str.find(".");
}
auto splitter = str.find("~");
string tag_name = str.substr(0,splitter);
string attrib_name = str.substr(splitter+1);
if (!defined) {
t = tags[tag_name];
} else if (t.tags.find(tag_name) == t.tags.end()) {
//TAG NOT IN T
cout<<"Not Found!"<<endl;
continue;
} else {
t = t.tags[tag_name];
}
// T is now set, check the attribute
if (t.attribs.find(attrib_name) == t.attribs.end()) {
cout<<"Not Found!"<<endl;
} else {
cout<<t.attribs[attrib_name]<<endl;
}
}
return 0;
}
What I've tried
This is fixed by just defining Tag x = t.tags[tag_name]; in the line above as a new variable, and then doing t = x; but why is this even happening?
Also, the following query also then fails: a.b.c~height, but it fails on Line 99 when it tried to get a.tags["b"]. No idea why. I was gonna just go with the hacky fix above, but this seems like a big core issue that i'm doing wrong.
I would suggest running this on an IDE and verifying that the parsing is indeed correct.
t=t.tags[tag_name]
This expression is unsafe because you are copy-assigning an object that is owned by that object over the owning object.
Consider what happens on this line:
The map lookup is performed and returns a Tag&.
You try to copy-assign this to t, invoking the implicit copy-assigment operator.
This operator copy-assigns t.tags from the tags attribute of the copy source -- which lives in t.tags.
The result is that the object you're copying into t is destroyed in the middle of that copy. This causes undefined behavior, and an immediate crash is honestly the best possible outcome as it told you exactly where the problem was. (This kind of problem frequently manifests at some point later in the program, at which point you've lost the state necessary to figure out what caused the UB.)
One workaround would be to move the source object into a temporary and then move-assign that temporary over t:
t = Tag{std::move(t.tags[tag_name])};
This lifts the data we want to assign to t out of t before we try to put it in t. Then, when t's assignment operator goes to replace t.tags, the data you're trying to assign to t doesn't live there anymore.
However, this overall approach involves a lot of unnecessary copying. It would be better to declare t as Tag const *t; instead -- have it be a pointer to a tag. Then you can just move that pointer around to point at other tags in your data structure without making copies.
Side note: I just did this problem the other day! Here's a hint that might help you simplify things: do you actually need a structure of tags? Is there a simpler type of lookup structure that would work instead of nested tags?
I am trying to write a function with multiple return values. I need to handle the scenario when if there is no image in the path. How can i do this?
I have tried to use map. But it is more for Key, Value pairs is what I think(new to C++).
Below is my code:
tuple<Mat, Mat> imageProcessing(boost::filesystem::path pickPath){
Mat img1, img2;
// Check if file exists, if not return NULL
if (!boost::filesystem::is_regular_file(pickPath)) {
return make_tuple(NULL, NULL);
}
imageFile = imread(pickPath.string());
// Preprocess code (return 2 mat files)
return make_tuple(img_1, img_2);
}
int main(){
path = "img.jpeg"
tie(img1, img2) = imageProcessing(path);
}
Use std::vector if you want a contiguous collection of objects or use std::set if your function guarantees to return only unique results.
Your method should also handle errors gracefully. In general there are two approaches in C++ for that:
Exceptions. When your function encounters bad data/error codes from your 3rd party libraries or any other unexpected scenario you can throw an exception and the function caller needs to write code to handle it. Your method will probably look something like this:
std::vector<Mat> ProcessImages(const boost:filesystem::path filePath)
{
if (!boost::filesystem::is_regular_file(pickPath)) {
throw std::invalid_argument("file does not exist"!); //probably there's a better exception you could throw or you can define your own.
}
...
The caller would look like this:
try{
auto images = ProcessImage(myFilePath)
}
catch(const std::invalid_argument& e ) {
// write something to console, log the exception, terminate your process... choose your poison.
}
Return an error code. There are several ways of doing this - return a tuple with the first item being the error code and the second the vector, or pass the vector by reference and return only the error code:
// if successful the function will return 0.
enum ErrorCode
{
Successful = 0,
InvalidArgs = 1,
...
}
ErrorCode ProcessImages(const boost:filesystem::path filePath, std::vector<Mat>& outImages)
{
if (!boost::filesystem::is_regular_file(pickPath)) {
{
return InvalidArgs;
}
imageFile = imread(pickPath.string());
outImages.insert(img1);
outImages.insert(img2);
return Successful;
}
int main(){
path = "img.jpeg"
std::vector<Mat> images;
auto result = ProcessImages(path, images);
if (result != Successfull)
{
//error
}
}
I think you can use vector<Mat> and push all images to this vector and return it. Then you can check the length of this vector after you call the function. If it's zero, that means you didn't push anything(if there's no file found in the path). Otherwise, extract all the images from the vector as you want.
I'm writing a tool in c++ using RTI DDS 5.2 that needs to detect when DataWriters are deleted and know the type_name of the related data. I'm using code similar to this and this.
I'm using a DDSWaitSet and it is getting triggered when a DataWriter is deleted with delete_datawriter but the SampleInfo indicates that the data is not valid and sure enough, the data sample type_name is empty.
Is there a way to delete a DataWriter in such a was as to cause the built in topic subscription to get the type_name? Or is there a QOS setting I can set to fix this behavior?
Found a workaround for this problem. I still don't know how to make it so the data sample is valid when a DataWriter goes away, but the SampleInfo does have the field instance_state which uniquely identifies the writer. The solution is to keep track of type_names when the data is valid and just look it up when it's not. Here is the gist of the code I am using to solve the issue:
struct cmp_instance_handle {
bool operator()(const DDS_InstanceHandle_t& a, const DDS_InstanceHandle_t& b) const {
return !DDS_InstanceHandle_equals(&a, &b);
}
};
void wait_for_data_writer_samples()
{
ConditionSeq cs;
DDSWaitSet* ws = new DDSWaitSet();
StatusCondition* condition = _publication_dr->get_statuscondition();
DDS_Duration_t timeout = {DDS_DURATION_INFINITE_SEC, DDS_DURATION_INFINITE_NSEC};
std::map<DDS_InstanceHandle_t, std::string, cmp_instance_handle> instance_handle_map;
ws->attach_condition(condition);
condition->set_enabled_statuses(DDS_STATUS_MASK_ALL);
while(true) {
ws->wait(cs, timeout);
PublicationBuiltinTopicDataSeq data_seq;
SampleInfoSeq info_seq;
_publication_dr->take(
data_seq,
info_seq,
DDS_LENGTH_UNLIMITED,
ANY_SAMPLE_STATE,
ANY_VIEW_STATE,
ANY_INSTANCE_STATE
);
int len = data_seq.length();
for(int i = 0; i < len; ++i) {
DDS_InstanceHandle_t instance_handle = info_seq[i].instance_handle;
if(info_seq[i].valid_data) {
std::string type_name(data_seq[i].type_name);
// store the type_name in the map for future use
instance_handle_map[instance_handle] = type_name;
if(info_seq[i].instance_state == DDS_InstanceStateKind::DDS_ALIVE_INSTANCE_STATE) {
do_data_writer_alive_callback(type_name);
}
else {
// If the data is valid, but not DDS_ALIVE_INSTANCE_STATE, the DataWriter died *and* we can
// directly access the type_name so we can handle that case here
do_data_writer_dead_callback(type_name);
}
}
else {
// If the data is not valid then the DataWriter is definitely not alive but we can't directly access
// the type_name. Fortunately we can look it up in our map.
do_data_writer_dead_callback(instance_handle_map[instance_handle]);
// at this point the DataWriter is gone so we remove it from the map.
instance_handle_map.erase(instance_handle);
}
}
_publication_dr->return_loan(data_seq, info_seq);
}
}
I'm having some trouble with the following method and I need some help trying to figure out what I am doing wrong.
I want to return a reference to a Value in a document. I am passing the Document from outside the function so that when I read a json file into it I don't "lose it".
const rapidjson::Value& CTestManager::GetOperations(rapidjson::Document& document)
{
const Value Null(kObjectType);
if (m_Tests.empty())
return Null;
if (m_current > m_Tests.size() - 1)
return Null;
Test& the_test = m_Tests[m_current];
CMyFile fp(the_test.file.c_str()); // non-Windows use "r"
if (!fp.is_open())
return Null;
u32 operations_count = 0;
CFileBuffer json(fp);
FileReadStream is(fp.native_handle(), json, json.size());
if (document.ParseInsitu<kParseCommentsFlag>(json).HasParseError())
{
(...)
}
else
{
if (!document.IsObject())
{
(...)
}
else
{
auto tests = document.FindMember("td_tests");
if (tests != document.MemberEnd())
{
for (SizeType i = 0; i < tests->value.Size(); i++)
{
const Value& test = tests->value[i];
if (test["id"].GetInt() == the_test.id)
{
auto it = test.FindMember("operations");
if (it != test.MemberEnd())
{
//return it->value; is this legitimate?
return test["operations"];
}
return Null;
}
}
}
}
}
return Null;
}
Which I am calling like this:
Document document;
auto operations = TestManager().GetOperations(document);
When I inspect the value of test["operations"] inside the function I can see everything I would expect (debug code removed from the abode code).
When I inspect the returned value outside the function I can see that it's an array (which I expect). the member count int the array is correct as well, but when print it out, I only see garbage instead.
When I "print" the Value to a string inside the methods, I get what I expect (i.e. a well formated json), but when I do it outside all keys show up as "IIIIIIII" and values that aren't strings show up correctly.
rapidjson::StringBuffer strbuf2;
rapidjson::PrettyWriter<rapidjson::StringBuffer> writer2(strbuf2);
ops->Accept(writer2);
As this didn't work I decided to change the method to receive a Value as a parameter and do a deep copy into it like this
u32 CTestManager::GetOperationsEx(rapidjson::Document& document, rapidjson::Value& operations)
{
(...)
if (document.ParseInsitu<kParseCommentsFlag>(json).HasParseError())
{
(...)
}
else
{
if (!document.IsObject())
{
(...)
}
else
{
auto tests = document.FindMember("tests");
if (tests != document.MemberEnd())
{
for (SizeType i = 0; i < tests->value.Size(); i++)
{
const Value& test = tests->value[i];
if (test["id"].GetInt() == the_test.id)
{
const Value& opv = test["operations"];
Document::AllocatorType& allocator = document.GetAllocator();
operations.CopyFrom(opv, allocator); //would Swap work?
return operations.Size();
}
}
}
}
}
return 0;
}
Which I'm calling like this:
Document document;
Value operations(kObjectType);
u32 count = TestManager().GetOperationsEx(document, operations);
But... I get same thing!!!!
I know that it's going to be something silly but I can't put my hands on it!
Any ideas?
The problem in this case lies with the use of ParseInSitu. When any of the GetOperations exist the CFileBuffer loses scope and is cleaned up. Because the json is being parsed in-situ when the buffer to the file goes, so goes the data.