I am currently using Qt5.0 with the core QJson library to handle some data for the program I am developing.
To set the scene for this question I will provide you with some JSON data that illustrates my problem:
{
"CLOCKS": [
{
"ID": "clk",
"MAX": 2e+08,
"MIN": 1e+07,
"VALUE": "no_clock"
},
{
"ID": "memclk",
"MAX": 2e+08,
"MIN": 1e+07,
"VALUE": "memclk"
}
]
}
Here we have a parent QJsonObject containing a single key 'CLOCKS'. The value for this key is a QJsonArray of QJsonObjects that contain a number of key/value pairs that contain my data.
If I wanted to retrieve the QJsonObject with id 'clk' I am currently using code like this:
// imagine m_data is my parent QJsonObject
QJsonArray clocks = m_data["CLOCKS"].toArray();
foreach (const QJsonValue & value, clocks) {
QJsonObject obj = value.toObject();
if (obj["ID"].toString() == "clk") {
return obj;
}
}
This works fine and the library has been great so far. However, I have started running into issues recently when I want to obtain a QJsonObject reference for modification instead of a copy.
So my question is, given the sample data provided how do I obtain a QJsonObject reference in order to modify the key/value pairs in the desired clock data object. The problem manifests itself, IMO due to the fact that you can obtain QJsonValueRefs, which are references to the value entries... but to actually access the data inside this (if the value is another array/object) you must convert using the toArray(), toObject() functions etc. This functions only return copies and not references creating a barrier to iterating down the object hierarchy with references.
The only way I have come up with so far to get around this is to create a copy of the entire "CLOCKS" QJsonArray, find the object I want then delete it and reinsert it with the changed data... and finally assign the entire array back to the "CLOCKS" key in the parent object. This seems cumbersome enough to me to me that I feel like I am doing something wrong and there must be a better way.
To support this here is what my code looks like so far... just to change the "VALUE" for one of the clock QJsonObjects:
QJsonArray resets = m_data.value(TAG_RESETS).toArray();
// get a copy of the QJsonObject
QJsonObject obj;
foreach (const QJsonValue & value, resets) {
QJsonObject o = value.toObject();
if (o.value(TAG_ID).toString() == id) {
obj = o;
break;
}
}
// modify the object
obj[TAG_VALUE] = "NEW VALUE";
// erase the old instance of the object
for (auto it = resets.begin(); it != resets.end(); ++it) {
QJsonObject obj = (*it).toObject();
if (obj.value(TAG_ID).toString() == id) {
resets.erase(it);
// assign the array copy which has the object deleted
m_data[TAG_RESETS] = resets;
break;
}
}
// add the modified QJsonObject
resets.append(obj);
// replace the original array with the array containing our modified object
m_data[TAG_RESETS] = resets;
I know this could be shortened a little bit but it still seems like there must be a better way to change a single value in a QJson object hierarchy without going to all this effort!!!
After wasting three hours of my life I can confirm that as of today this is still impossible with Qt 5.4. You can modify JSON objects, but not nested JSON objects.
The problem is that the code such as:
json["aa"].toObject()["bb"] = 123;
essentially means the following:
QJsonObject temp = json["aa"].toObject();
temp["bb"] = 123;
and since temp is not a reference but object (and toObject() doesn't return a reference), the assignment is compiled but then discarded.
Essentially it breaks down to the fact that it is impossible to obtain the reference to an object you just created, meaning you cannot create them from left to right, i.e. aa["bb"] -> aa["bb"]["cc"] etc - you cannot obtain reference to aa["bb"], only a copy of its value.
What IS possible though is to recreate the JSON with a new value added, as described here: https://forum.qt.io/topic/25096/modify-nested-qjsonvalue/4 - note that this keeps recreating the object each time it is called, and is essentially memory usage disaster, but this is all Qt currently allows.
According to information from Qt developer who actually wrote QJson in Qt5 -
What's currently included in Qt is a 'read-only' implementation to provide parsing facilities. He has an intention to extend design with 'references' support in future, but it's not yet done.
I have had a similar problem for a couple of days and I have managed to find a workaround which works for me and I thought I should share it here.
You can navigate to the object whose key-value you wish to update. Then use the "remove" method to delete the key-value pair and then use the "insert" method to insert it again with the new value.
This might ruin the order of key-value pairs in your object but since you will anyways access by a key, it should not be a problem.
The in-place changing of values is not supported as I found out the hard way :)
Related
I have a QList and I'm trying to replace the objects in the list with new objects. The context is that I have a list of custom objects (the class name is "Conversation") to represent a list of group chats in a messaging platform. I use std::find_if to iterate through the list of pointers to find one with the right ID, and I want to take the pointer to that found object, deallocate it (delete?), and reassign that pointer to point at an object I generate with the "new" keyword. I think I'm doing this right but I'm not sure how to verify.
I tried a couple different iterations, ran into some issues where I realized I was using a const_iterator rather than just an iterator, so I couldn't modify any data. But I've fixed that and it seems like it's working, but I'm not positive.
Here's what I've got:
GroupChat *gc = new GroupChat(); // extends Conversation
// ...I update the member data here...
auto foundChat = std::find_if(conversations_.Conversations.begin2(),
conversations_.Conversations.end2(),
[this, gc](Conversation* o) { // my code to find the correct one...
}
if (foundChat != conversations_.Conversations.end()) {
auto c = (*foundChat);
delete c; // Is this right? Not positive...
//*foundChat = nullptr; // do I need this?
c = gc;
}
It seems like it's working but I'm worried about dangling pointers and incorrect memory deallocation/allocation. Could someone spot check me on this? Thanks for any help!
I have a function that stores the value of an argument to an std::vector<v8::Local<v8::Value>> property of a C++ class exposes as an ObjectWrap like this:
NAN_METHOD(MyObject::Write) {
MyObject* obj = Nan::ObjectWrap::Unwrap<MyObject>(info.This());
obj->data.push_back(info[0]);
}
However, when I try to read back the value from another C++ function, the value is lost, and becomes undefined.
I'm passing a number to MyObject::Write, and I can confirm info[0]->IsNumber() returns true before pushing it to the vector, however when reading it back, the value it not a number, and in fact returns false for all the types I tested using Is<Type> methods from v8::Value, but still returns true for BooleanValue().
My guess is that the variable is being garbage collected after MyObject::Write returns, however I have no idea how to prevent this from happening.
I'm currently trying to initialise the value as a Persistent value. I tried the following attempts without success:
Nan::CopyablePersistentTraits<v8::Value>::CopyablePersistent p;
Nan::Persistent<v8::Value> persistent(info[0]);
Nan::CopyablePersistentTraits::Copy(persistent, p);
And:
v8::Isolate *isolate = info.GetIsolate();
v8::Persistent<v8::Value, v8::CopyablePersistentTraits<v8::Value>> persistent(isolate, info[0]);
But getting tons of C++ errors.
I was running into problems untangling this mess myself. There's a lot of template stuff going on here that we both missed. Here was the solution I found most readable:
// Define the copyable persistent
v8::CopyablePersistentTraits<v8::Value>::CopyablePersistent p;
// Create the local value
auto val = v8::Local<v8::Value>::New(
v8::Isolate::GetCurrent(), //< Isolate required
v8::Integer::New(v8::Isolate::GetCurrent(), v) //< Isolate required
);
// Reset() is a TEMPLATE FUNCTION, you have to template it with the same
// template type parameter as the v8::Local you are passing
p.Reset<v8::Value>(v8::Isolate::GetCurrent(), val); //< Isolate required
By "info" I assume you are referring to a v8::FunctionCallbackInfo reference. If so the above code would collapse to the following:
void SomeFunc(v8::FunctionCallbackInfo<v8::Value>& info) {
v8::CopyablePersistentTraits<v8::Value>::CopyablePersistent p;
p.Reset<v8::Value>(info[0]);
}
Because the persistent is now copyable you can do things like store it inside a standard library container. This was my use case. This is an example of storing a value in a vector:
std::vector<v8::CopyablePersistentTraits<v8::Value>::CopyablePersistent> vect;
void AccumulateData(v8::FunctionCallbackInfo<v8::Value>& info) {
v8::CopyablePersistentTraits<v8::Value>::CopyablePersistent p;
p.Reset<v8::Value>(info[0]);
vect.push_back(p);
}
I hope this helps someone out there.
If you plan on storing v8 values in C++, you need to make them persistent instead of local so they're independent of handle scope and not garbage-collected when the handle scope is released.
Nan has version-independant wrappers for v8::Persistent and Co. Because of using inside std::vector<>, you'll also need to initialize Nan::Persistent with Nan::CopyablePersistentTraits so it becomes copyable (or make an own reference-counted container for it).
To all those that are familiar with rapidjson i have the following issue:
I have a certain function that accepts as parameter a date and if that date exists in the json file the method does some operations and if not some other operations.
Generally it looks like this: (not actual code more like pseudo)
Function:
void updateData(string date) {
//
//code to turn date from string to const char* (tested)
//
if (v.HasMember(date)) { //v is a value
Value d;
d=v[date];
//
//code that involves getting data from d (d is object) using HasMember
//
} else {
//generic code that has nothing to do with json
}
JSON file:
{
"main": {
"v": {
"2014-10-02" : {
//some fields
},
"2014-10-03" : {
//some fields
}
}
}
}
So the first time that i call updateData for date "2014-10-02" it runs correctly(executes the if part).
The problem is when i call updateData for another date (like "2014-10-03" that is supposed to work well) it always executes the wrong part(else part) and even when i switch back to the first date it still executes the else part. (while popping many assertions (mostly isString())).
So is HasMember really the problem here, in the sense that it is maybe altering the object?
And are there any alternative ways to search for a member, other than that?
Any tip is appreciated...
Its hard to tell without the actual code, but I think problem might be that you are treating "v" as a Value instead of an Object. "v" isn't a value, its the name of the object. So what you have is a nested object. In order to do this I think you would have to used MemberIterators and iterate through the child objects in the v object.
rapidjson has a pretty good example on how to use iterators.
there is also this question here, which has a pretty good answer on how to use nested objects
Retrieving a nested object inside a JSON string using rapidjson
I'm making Json format data editor with Qt treeview and Qt Json support.
I wanna pass QJsonObject or QJsonArray reference parameter to function.
This works:
void makeJsonData(QJsonObject &obj) {
obj.insert("key", 1234);
}
//call makeJsonData()
QJsonObject jobj;
makeJsonData(jobj);
int keysize = jobj.keys().size(); //1, OK.
But not with this:
//QJsonValue, because it can handle both QJsonObject and QJsonArray
void makeJsonData(QJsonValue &obj) {
obj.toObject().insert("key", 1234); //obj is QJsonObject
}
//call makeJsonData()
QJsonObject jobj;
makeJsonData(QJsonValue::fromVariant(jobj)); //fromVariant() to cast QJsonObject to QJsonValue
int keysize = jobj.keys().size(); //0, Fail.
It looks like QJsonValue::toObject() just copies parameter..
How can I use reference of both QJsonObject and QJsonArray with one parameter type?
There are a couple ways I see to solve your problem:
Option 1 (as mentioned in my comment)
A dynamic cast can be used like so:
bool makeJsonData(void* obj) {
QJsonObject* asObj = dynamic_cast<QJsonObject*>(obj);
QJsonArray* asArray = dynamic_cast<QJsonArray*>(obj);
if (asObj) {
//do what you would if it were an object
}
else if (asArray) {
//do what you would if it were an array
}
else {
//cast fail. Returning false to tell the caller that they passed bad data
//an alternate (probably better) would be to throw an exception
return false;
}
}
Option 2
I honestly feel that this business with void* is the wrong way to do it. Doing void* stuff is almost always a code smell (it removes compile-time checks that save us from stepping on their own feet) and in this case I think that the way you are doing this needs work. Also, dynamic_cast requires RTTI which may not always be turned on (compiler support, performance issues, etc).
I took a look at the Qt headers on my machine and as far as I can tell, QJsonObject and QJsonArray don't really inherit from anything, so going down the route of changing the void* to a base type in order to keep a semblance of type checking won't quite work.
What I would do would be this:
Make two separate methods. One for handling arrays and one for handling objects. They have different methods and different things you can do, so this makes sense to me. You could even keep the same name so that they are overloaded.
Have another method with your common stuff in it. I assume that your function is trying to add some data to either the array or object that is passed. Make a method that creates the data (i.e. QJsonObject createJsonData()) and call it inside both of your methods mentioned above.
The idea is to keep code repetition down while still preserving type checking. The time you spend making the one extra method to handle both cases could be far less than the time you will spend debugging code after accidentally passing in something to a void* pointer that you never meant to pass.
Option 3
Alternately, you could use QJsonValue, change the return type of the function to QJsonValue, and make it return the new object without modifying the original. Further, the QJsonValue class has those fun isArray/isObject methods that you could use to do something like mentioned earlier. An example:
QJsonValue makeJsonData(const QJsonValue& val) {
if (val.isObject()) {
QJsonObject obj = val.toObject();
//do your stuff, modifying obj as you please (perhaps calling another method so that this can have less repetition
return QJsonValue(obj);
}
else if (val.isArray()) {
QJsonArray arr = val.toArray();
//do your stuff, modifying arr as you please (perhaps calling another method so that this can have less repetition
return QJsonValue(arr);
}
else {
throw "Invalid Value Type";
}
}
I honestly prefer this pattern, but I know there are reasons for going the way you have mentioned such as avoiding gratuitous memory allocations.
You may need to add this:
#include <QJsonArray>
I'm using luabind 0.9.1 from Ryan Pavlik's master distribution with Lua 5.1, cygwin on Win XP SP3 + latest patches x86, boost 1.48, gcc 4.3.4. Lua and boost are cygwin pre-compiled versions.
I've successfully built luabind in both static and shared versions.
Both versions pass all the tests EXCEPT for the test_object_identity.cpp test which fails in both versions.
I've tracked down the problem to the following issue:
If an entry in a table is created for NON built-in class (i.e., not int, string, etc), the value CANNOT be retrieved.
Here's a code piece that demonstrates this:
#include "test.hpp"
#include <luabind/luabind.hpp>
#include <luabind/detail/debug.hpp>
using namespace luabind;
struct test_param
{
int obj;
};
void test_main(lua_State* L)
{
using namespace luabind;
module(L)
[
class_<test_param>("test_param")
.def_readwrite("obj", &test_param::obj)
];
test_param temp_object;
object tabc = newtable(L);
tabc[1] = 10;
tabc[temp_object] = 30;
TEST_CHECK( tabc[1] == 10 ); // passes
TEST_CHECK( tabc[temp_object] == 30 ); // FAILS!!!
}
tabc[1] is indeed 10 while tabc[temp_object] is NOT 30! (actually, it seems to be nil)
However, if I use iterate to go over tabc entries, there're the two entries with the CORRECT key/value pairs.
Any ideas?
BTW, overloading the == operator like this:
#include <luabind/operator.hpp>
struct test_param
{
int obj;
bool operator==(test_param const& rhs) const
{
return obj == rhs.obj;
}
};
and
module(L)
[
class_<test_param>("test_param")
.def_readwrite("obj", &test_param::obj)
.def(const_self == const_self)
];
Doesn't change the result.
I also tried switching to settable() and gettable() from the [] operator. The result is the same. I can see with the debugger that default conversion of the key is invoked, so I guess the error arises from somewhere therein, but it's beyond me to figure out what exactly the problem is.
As the following simple test case show, there're definitely a bug in Luabind's conversion for complex types:
struct test_param : wrap_base
{
int obj;
bool operator==(test_param const& rhs) const
{ return obj == rhs.obj ; }
};
void test_main(lua_State* L)
{
using namespace luabind;
module(L)
[
class_<test_param>("test_param")
.def(constructor<>())
.def_readwrite("obj", &test_param::obj)
.def(const_self == const_self)
];
object tabc, zzk, zzv;
test_param tp, tp1;
tp.obj = 123456;
// create new table
tabc = newtable(L);
// set tabc[tp] = 5;
// o k v
settable( tabc, tp, 5);
// get access to entry through iterator() API
iterator zzi(tabc);
// get the key object
zzk = zzi.key();
// read back the value through gettable() API
// o k
zzv = gettable(tabc, zzk);
// check the entry has the same value
// irrespective of access method
TEST_CHECK ( *zzi == 5 &&
object_cast<int>(zzv) == 5 );
// convert key to its REAL type (test_param)
tp1 = object_cast<test_param>(zzk);
// check two keys are the same
TEST_CHECK( tp == tp1 );
// read the value back from table using REAL key type
zzv = gettable(tabc, tp1);
// check the value
TEST_CHECK( object_cast<int>(zzv) == 5 );
// the previous call FAILS with
// Terminated with exception: "unable to make cast"
// this is because gettable() doesn't return
// a TRUE value, but nil instead
}
Hopefully, someone smarter than me can figure this out,
Thx
I've traced the problem to the fact that Luabind creates a NEW DISTINCT object EVERY time you use a complex value as key (but it does NOT if you use a primitive one or an object).
Here's a small test case that demonstrates this:
struct test_param : wrap_base
{
int obj;
bool operator==(test_param const& rhs) const
{ return obj == rhs.obj ; }
};
void test_main(lua_State* L)
{
using namespace luabind;
module(L)
[
class_<test_param>("test_param")
.def(constructor<>())
.def_readwrite("obj", &test_param::obj)
.def(const_self == const_self)
];
object tabc, zzk, zzv;
test_param tp;
tp.obj = 123456;
tabc = newtable(L);
// o k v
settable( tabc, tp, 5);
iterator zzi(tabc), end;
std::cerr << "value = " << *zzi << "\n";
zzk = zzi.key();
// o k v
settable( tabc, tp, 6);
settable( tabc, zzk, 7);
for (zzi = iterator(tabc); zzi != end; ++zzi)
{
std::cerr << "value = " << *zzi << "\n";
}
}
Notice how tabc[tp] first has the value 5 and then is overwritten with 7 when accessed through the key object. However, when accessed AGAIN through tp, a new entry gets created. This is why gettable() fails subsequently.
Thx,
David
Disclaimer: I'm not an expert on luabind. It's entirely possible I've missed something about luabind's capabilities.
First of all, what is luabind doing when converting test_param to a Lua key? The default policy is copy. To quote the luabind documentation:
This will make a copy of the parameter. This is the default behavior when passing parameters by-value. Note that this can only be used when passing from C++ to Lua. This policy requires that the parameter type has an accessible copy constructor.
In pratice, what this means is that luabind will create a new object (called "full userdata") which is owned by the Lua garbage collector and will copy your struct into it. This is a very safe thing to do because it no longer matters what you do with the c++ object; the Lua object will stick around without really any overhead. This is a good way to do bindings for by-value sorts of objects.
Why does luabind create a new object each time you pass it to Lua? Well, what else could it do? It doesn't matter if the address of the passed object is the same, because the original c++ object could have changed or been destroyed since it was first passed to Lua. (Remember, it was copied to Lua by value, not by reference.) So, with only ==, luabind would have to maintain a list of every object of that type which had ever been passed to Lua (possibly weakly) and compare your object against each one to see if it matches. luabind doesn't do this (nor do I think should it).
Now, let's look at the Lua side. Even though luabind creates two different objects, they're still equal, right? Well, the first problem is that, besides certain built-in types, Lua can only hold objects by reference. Each of those "full userdata" that I mentioned before is actually a pointer. That means that they are not identical.
But they are equal, if we define an __eq meta operation. Unfortunately, Lua itself simply does not support this case. Userdata when used as table keys are always compared by identity, no matter what. This actually isn't special for userdata; it is also true for tables. (Note that to properly support this case, Lua would need to override the hashcode operation on the object in addition to __eq. Lua also does not support overriding the hashcode operation.) I can't speak for the authors of Lua why they did not allow this (and it has been suggested before), but there it is.
So, what are the options?
The simplest thing would be to convert test_param to an object once (explicitly), and then use that object to index the table both times. However, I suspect that while this fixes your toy example, it isn't very helpful in practice.
Another option is simply not to use such types as keys. Actually, I think this is a very good suggestion, since this kind of light-weight binding is quite useful, and the only other option is to discard it.
It looks like you can define a custom conversion on your type. In your example, it might be reasonable to convert your type to a Lua number which will behave well as a table index.
Use a different kind of binding. There will be some overhead, but if you want identity, you'll have to live with it. It sounds like luabind has some support for wrappers, which you may need to use to preserve identity:
When a pointer or reference to a registered class with a wrapper is passed to Lua, luabind will query for it's dynamic type. If the dynamic type inherits from wrap_base, object identity is preserved.