V8, append text to the last element of the array - c++

I'm building a native NodeJS C++ module based on V8. I got the following code in loop:
Local<Array> nodes = Array::New();
/********** INSIDE THE LOOP ************/
Local<Object> node_obj = Object::New();
node_obj->Set(data_symbol, String::New(input.substr(openPos + (lastTag > 1 ? 3 : 2), pos - openPos - (lastTag > 1 ? 3 : 2) - 1).c_str()));
node_obj->Set(tag_symbol, Integer::New(lastTag));
nodes->Set(id, node_obj);
And I'm populating an array with objects, so the output (in JS) will look like this:
[
{tag: 2, data: "asdsadsadasfddgdfgdfg"},
{tag: 1, data: "afg235235232fgdfg"}
]
My questions is how I can append a string to the data_symbol of the last object of the array?
Full code can be found here: http://pastebin.com/tCgWCxyA
Example of what I'm trying to do:
Lets take this structure for example:
struct Node {
short tag;
std::string data;
Node(const std::string& input, short tagId) : tag(tagId), data(input) {}
};
std::vector<Node> elems;
My question is how I can do
elems.back().data.append("SomeString");
in V8?

You can use String::Concat(Handle<String> left, Handle<String>right) like so:
HandleScope scope;
Local<Object> lastnode = nodes->Get(nodes->Length() - 1)->ToObject();
Local<String> lastdatastr = lastnode->Get(data_symbol)->ToString();
lastnode->Set(data_symbol,
String::Concat(lastdatastr, String::New(" I'm appended!")));

Related

QT: Arbitrarily complex data structures with QVariant

Quoting the QT Docs:
You can even store QList and QMap values in
a variant, so you can easily construct arbitrarily complex data
structures of arbitrary types. This is very powerful and versatile,
but may prove less memory and speed efficient than storing specific
types in standard data structures.
Does anyone know of, or have, an example of doing exactly this?
I'm a long time C++ programmer, but a QT Nube, and the copy of write semantics are giving me fits. Maps and Lists of QVariants data structures seems immutable. Every time I try to modify a tree of values, I just end up modifying a copy.
Got some feedback from my first post that I should add an example. Here goes:
// Input Data:
//
// { "f1" : "field-1",
// "list" : [ 0, 1, 2, 3, 4 ] }
//
// Convert the data, commented above, into a QVariantMap with two
// values:
// "f1" - a string
// "list" - a QVariantList of integers
QVariant vData = ConvertJsonDocument(document);
// Dump
qWarning( VariantToString(vData).toLocal8Bit() );
// Convert vData to QVariantMap
QVariantMap vMap = vData.value<QVariantMap>();
// Get the list of integers as a QVariantList
QVariantList vList = vMap["list"].value<QVariantList>();
// Change the 0 to a 5
vList[0] = 5;
// Dump
qWarning( VariantToString(vData).toLocal8Bit() );
Output from above:
{ "f1" : "field-1", "list" : [ 0, 1, 2, 3, 4 ] }
{ "f1" : "field-1", "list" : [ 0, 1, 2, 3, 4 ] }
DESIRED output from above:
{ "f1" : "field-1", "list" : [ 0, 1, 2, 3, 4 ] }
{ "f1" : "field-1", "list" : [ 5, 1, 2, 3, 4 ] }
I get that I am modifying copies, but for the life of my I can't figure out how NOT to. How do I edit the original source data? (The data in the tree rooted at vData.)
Once you make the desired alterations, you need to go back through the tree and update your variables with the new data.
// Convert vData to QVariantMap
QVariantMap vMap = vData.value<QVariantMap>();
// Get the list of integers as a QVariantList
QVariantList vList = vMap["list"].value<QVariantList>();
// Change the 0 to a 5
vList[0] = 5;
// Change the map using insert, which replaces the value
vMap.insert("list", vList);
// Rebuild the QVariant from the QMap
vData = QVariant::fromValue(vMap);
// Dump
qWarning( VariantToString(vData).toLocal8Bit() );
You can convert the data back from the QVariant and update the source document from there.

How to pass Array of Objects using PHPCPP, loop through each object and return associative array

I have recently started learning PHPCPP - A C++ library for developing PHP extensions and trying to understand:
how to pass an Array of objects from php to C++ through PHPCPP
library as examples give only info about arrays and objects separately,
then how to loop through each object in C++
and how to return an associative array back to PHP
Can someone point me to the right direction?
I have come up with this example however need some help:
class Curve{
public :
double shape;
double peak;
double tpeak;
double start;
double lag;
double index;
};
Php::Value example1(Php::Parameters &params) {
vector<Curve> c = params[0];
//create an associative array and return to php
std::map< std::string, std::map<std::string, std::map<std::string, std::map<std::string, std::double>>> > data;
// loop through array of objects here or do something with an array
...
data[c.shape][c.peak][c.tpeak][c.start] = 1/12 * c.index;
return data;
}
extern "C" {
PHPCPP_EXPORT void *get_module() {
static Php::Extension myExtension("my_extension", "1.0");
myExtension.add<example1>("example1", {
Php::ByVal("curves", "Array", false);
});
eturn myExtension;
}
}
In contrary to your declaration
Php::ByVal("curves", "Array", false);
I am using
Php::ByVal("curves", "Array", true);
I did not test this piece of code, therefore it may be necessary to add some slight corrections :-)
Good luck!
public:
Php::Value GetAssociativeArray( Php::Parameters &params ) {
if ( params.size( ) < 1 ) {
error_function( "need at least 1 parameter" );
}
if ( ! params[ 0 ].isObject( ) ) {
error_function( "needs an object as first parameter" );
}
//
// what you have to do before you can use this example:
// define the data type data_t, which should be stored in php_array_result
// supply the PHP class the objects in php_array_objects belong to - can be coded in PHP or cpp
// supply the PHP class the objects in php_array_result belong to - can be coded in PHP or cpp
//
// the PHP object associated to the current cpp class
Php::Value self( this );
// the objects received
Php::Value php_array_objects = params[ 0 ];
// the PHP array to return
Php::Array php_array_result;
// the c++ - class
Curve * obj_curve;
// a single PHP object from the PHP array we received
Php::Value php_obj_in
// a single object from the PHP array we are delivering
Php::Object php_obj_out;
// the key of the associative PHP Array
std::string array_key;
// the data to collect in the associative PHP array
data_t data;
// some other data
int64_t data_returned;
for ( int i = 0; i < php_array_objects.size( ) ; i++ ) {
// retrieve the next object
php_obj_in = php_array_objects[ i ];
// cast PHP object to c++-class
obj_curve = ( Curve * ) php_obj_in.implementation( );
// do something with obj_curve
data = obj_curve->do_something( );
// calculate the key
key = "key_" + std::to_string( i );
// to create an object pass in the class name ( Curve ) to the constructor with optional constructor parameters (1,2,3)
php_obj_out = Php::Object( "Curve", 1, 2, 3 );
// set a class member named "class_member" of php_obj_out
php_obj_out[ "class_member" ] = data;
// call the method "class_method" of php_obj_out
data_returned = php_obj_out.call( "class_method", "parameter" );
// add the new created object to the PHP array
php_array_result[ key ] = php_obj_out;
}
return php_array_result;
} // GetAssociativeArray( )

yams-cpp nested sequence returning map instead of value

I am trying to parse the following config.yaml file.
config.yaml
foo:
bar:
baz: [1, 2, 3, 4]
bam: "some_string_value"
test.cpp
YAML::Node configObj = YAML::LoadFile("cfig.yaml"); // loads file just fine
YAML::Node fooObj = configObj["foo"]; // this Node object is a Map
// iterate over foo node to get bar node
for( auto it = fooObj.begin(); it != fooObj.end(); ++it){
YAML::Node barMap = it->second; // this Node object is a Map
// iterate over bar node to get bad node
for( auto i = barMap.begin(); i != barMap.end(); ++i){
YAML::Node bazMap = i->second; // this node is a sequence
for( std::size_t i=0; i<bazMap.size(); i++
auto index = bazMap[i].as<int>(); // <<< This is the problem
}
}
}
The problem as far as I can see is that I am expecting index to be an int but bazMap[i].as<int>() I am expecting to be 1 the first loop, 2 the second, etc. What I am getting instead is bazMap[i].as<int>() is of type map. What am I missing in my understanding here?
Thanks,
Bruce
Update The answer was that I stopped early in my nested for loops.
The answer that I found rests in that I was unclear on what I was getting.
In the config.yaml file I was expecting the following:
foo.Type() == Map
bar.Type() == Sequence
baz.Type() == Sequence
but what I was getting was:
foo.Type() == Map
bar.Type() == Map
baz.Type() == Sequence
all I had to do to resolve this issue was to change the config file.
foo:
bar:
- baz: [1, 2, 3, 4]
- bam: "some_string_value"
Then it parsed exactly as I had expected.
So the issue was the format of the config file and not in the parsing logic.

Creating JSON arrays in Boost using Property Trees

I'm trying to create a JSON array using boost property trees.
The documentation says: "JSON arrays are mapped to nodes. Each element is a child node with an empty name."
So I'd like to create a property tree with empty names, then call write_json(...) to get the array out. However, the documentation doesn't tell me how to create unnamed child nodes. I tried ptree.add_child("", value), but this yields:
Assertion `!p.empty() && "Empty path not allowed for put_child."' failed
The documentation doesn't seem to address this point, at least not in any way I can figure out. Can anyone help?
Simple Array:
#include <boost/property_tree/ptree.hpp>
using boost::property_tree::ptree;
ptree pt;
ptree children;
ptree child1, child2, child3;
child1.put("", 1);
child2.put("", 2);
child3.put("", 3);
children.push_back(std::make_pair("", child1));
children.push_back(std::make_pair("", child2));
children.push_back(std::make_pair("", child3));
pt.add_child("MyArray", children);
write_json("test1.json", pt);
results in:
{
"MyArray":
[
"1",
"2",
"3"
]
}
Array over Objects:
ptree pt;
ptree children;
ptree child1, child2, child3;
child1.put("childkeyA", 1);
child1.put("childkeyB", 2);
child2.put("childkeyA", 3);
child2.put("childkeyB", 4);
child3.put("childkeyA", 5);
child3.put("childkeyB", 6);
children.push_back(std::make_pair("", child1));
children.push_back(std::make_pair("", child2));
children.push_back(std::make_pair("", child3));
pt.put("testkey", "testvalue");
pt.add_child("MyArray", children);
write_json("test2.json", pt);
results in:
{
"testkey": "testvalue",
"MyArray":
[
{
"childkeyA": "1",
"childkeyB": "2"
},
{
"childkeyA": "3",
"childkeyB": "4"
},
{
"childkeyA": "5",
"childkeyB": "6"
}
]
}
What you need to do is this piece of fun. This is from memory, but something like this works for me.
boost::property_tree::ptree root;
boost::property_tree::ptree child1;
boost::property_tree::ptree child2;
// .. fill in children here with what you want
// ...
ptree.push_back( std::make_pair("", child1 ) );
ptree.push_back( std::make_pair("", child2 ) );
But watch out there's several bugs in the json parsing and writing. Several of which I've submitted bug reports for - with no response :(
EDIT: to address concern about it serializing incorrectly as {"":"","":""}
This only happens when the array is the root element. The boost ptree writer treats all root elements as objects - never arrays or values. This is caused by the following line in boost/propert_tree/detail/json_parser_writer.hpp
else if (indent > 0 && pt.count(Str()) == pt.size())
Getting rid of the "indent > 0 &&" will allow it to write arrays correctly.
If you don't like how much space is produced you can use the patch I've provided here
When starting to use Property Tree to represent a JSON structure I encountered similar problems which I did not resolve. Also note that from the documentation, the property tree does not fully support type information:
JSON values are mapped to nodes containing the value. However, all type information is lost; numbers, as well as the literals "null", "true" and "false" are simply mapped to their string form.
After learning this, I switched to the more complete JSON implementation JSON Spirit. This library uses Boost Spirit for the JSON grammar implementation and fully supports JSON including arrays.
I suggest you use an alternative C++ JSON implementation.
In my case I wanted to add an array to a more or less arbitrary location, so, like Michael's answer, create a child tree and populate it with array elements:
using boost::property_tree::ptree;
ptree targetTree;
ptree arrayChild;
ptree arrayElement;
//add array elements as desired, loop, whatever, for example
for(int i = 0; i < 3; i++)
{
arrayElement.put_value(i);
arrayChild.push_back(std::make_pair("",arrayElement))
}
When the child has been populated, use the put_child() or add_child() function to add the entire child tree to the target tree, like this...
targetTree.put_child(ptree::path_type("target.path.to.array"),arrayChild)
the put_child function takes a path and a tree for an argument and will "graft" arrayChild into targetTree
As of boost 1.60.0, problem persists.
Offering a Python 3 workaround (Gist), which can be syscalled just after boost::property_tree::write_json.
#!/usr/bin/env python3
def lex_leaf(lf: str):
if lf.isdecimal():
return int(lf)
elif lf in ['True', 'true']:
return True
elif lf in ['False', 'false']:
return False
else:
try:
return float(lf)
except ValueError:
return lf
def lex_tree(j):
tj = type(j)
if tj == dict:
for k, v in j.items():
j[k] = lex_tree(v)
elif tj == list:
j = [lex_tree(l) for l in j]
elif tj == str:
j = lex_leaf(j)
else:
j = lex_leaf(j)
return j
def lex_file(fn: str):
import json
with open(fn, "r") as fp:
ji = json.load(fp)
jo = lex_tree(ji)
with open(fn, 'w') as fp:
json.dump(jo, fp)
if __name__ == '__main__':
import sys
lex_file(sys.argv[1])
If you want JSON in C++, there's no need for Boost. With this library you can get JSON as a first class data type that behaves like an STL container.
// Create JSON on the fly.
json j2 = {
{"pi", 3.141},
{"happy", true},
{"name", "Niels"},
{"nothing", nullptr},
{"answer", {
{"everything", 42}
}},
{"list", {1, 0, 2}},
{"object", {
{"currency", "USD"},
{"value", 42.99}
}}
};
// Or treat is as an STL container; create an array using push_back
json j;
j.push_back("foo");
j.push_back(1);
j.push_back(true);
// also use emplace_back
j.emplace_back(1.78);
// iterate the array
for (json::iterator it = j.begin(); it != j.end(); ++it) {
std::cout << *it << '\n';
}
Confused with the official document and the above answers.
Below is what I understand.
Property Tree consists of nodes.
Each node is like below
struct ptree
{
map<key_name,value> data;
vector<pair<key_name,ptree>> children;
};
To put 'value' into data with 'put'
To put 'node' into children with 'push_back'\
// Write
bt::ptree root;
bt::ptree active;
bt::ptree requested;
bt::ptree n1, n2, n3;
n1.put("name", "Mark");
n1.put("age", 20);
n1.put("job", "aaa");
n2.put("name", "Rosie");
n2.put("age", "19");
n2.put("job", "bbb");
n3.put("name", "sunwoo");
n3.put("age", "10");
n3.put("job", "ccc");
active.push_back ({ "",l1 });
active.push_back ({ "",l2 });
requested.push_back({ "",l3 });
root.push_back ({"active", active});
root.push_back ({"requested", requested});
bt::write_json("E:\\1.json", root);
// READ
bt::ptree root2;
bt::ptree active2;
bt::ptree requested2;
bt::ptree r1, r2, r3;
bt::read_json("E:\\1.json", root2);
// loop children
for (auto& [k,n] : root.get_child("active"))
{
cout << n.get<string>("name", "unknown");
cout << n.get<int> ("age" , 11);
cout << n.get<string>("job" , "man");
cout << endl << flush;
}

How do I pass a list of objects from C++ to Lua?

I'm the lead dev for Bitfighter, and am adding user-scripted bots using Lua. I'm working with C++ and Lua using Lunar to glue them together.
I'm trying to do something that I think should be pretty simple: I have an C++ object in Lua (bot in the code below), and I call a method on it that (findItems) which causes C++ to search the area around the robot and return a list of objects it finds (TestItems and others not shown here). My question is simply how do I assemble and return the list of found items in C++, and then iterate over them in Lua?
Basically, I want to fill in the <<<< Create list of items, return it to lua >>>> block below, and make any corrections I may need in the Lua code itself, included below that.
I've tried to keep the code simple but complete. Hope there's not too much here! Thanks!
C++ Header file
class TestItem : public LuaObject
{
public:
TestItem(); // C++ constructor
///// Lua Interface
TestItem(lua_State *L) { } ; // Lua constructor
static const char className[];
static Lunar<TestItem>::RegType methods[];
S32 getClassID(lua_State *L) { return returnInt(L, TestItemType); }
};
class LuaRobot : public Robot
{
LuaRobot(); // C++ constructor
///// Lua Interface
LuaRobot(lua_State *L) { } ; // Lua constructor
static const char className[];
static Lunar<LuaRobot>::RegType methods[];
S32 findItems(lua_State *L);
}
C++ .cpp file
const char LuaRobot::className[] = "Robot"; // Class name in Lua
// Define the methods we will expose to Lua
Lunar<LuaRobot>::RegType LuaRobot::methods[] =
{
method(LuaRobot, findItems),
{0,0} // End method list
};
S32 LuaRobot::findItems(lua_State *L)
{
range = getIntFromStack(L, 1); // Pop range from the stack
thisRobot->findObjects(fillVector, range); // Put items in fillVector
<<<< Create list of items, return it to lua >>>>
for(int i=0; i < fillVector.size(); i++)
do something(fillVector[i]); // Do... what, exactly?
return something;
}
/////
const char TestItem::className[] = "TestItem"; // Class name in Lua
// Define the methods we will expose to Lua
Lunar<TestItem>::RegType TestItem::methods[] =
{
// Standard gameItem methods
method(TestItem, getClassID),
{0,0} // End method list
};
Lua Code
bot = LuaRobot( Robot ) -- This is a reference to our bot
range = 10
items = bot:findItems( range )
for i, v in ipairs( items ) do
print( "Item Type: " .. v:getClassID() )
end
So you need to fill a vector and push that to Lua.
Some example code follows. Applications is a std::list.
typedef std::list<std::string> Applications;
I create a table and fill it with the data in my list.
int ReturnArray(lua_State* L) {
lua_createtable(L, applications.size(), 0);
int newTable = lua_gettop(L);
int index = 1;
Applications::const_iterator iter = applications.begin();
while(iter != applications.end()) {
lua_pushstring(L, (*iter).c_str());
lua_rawseti(L, newTable, index);
++iter;
++index;
}
return 1;
}
This leaves me with an array in the stack. If it were returned to Lua, then I could write the following:
for k,v in ipairs( ReturnArray() ) do
print(v)
end
Of course so far, this just gets me a Lua array of strings. To get an array of Lua objects we just tweak your method a bit:
S32 LuaRobot::findItems(lua_State *L)
{
range = getIntFromStack(L, 1); // Pop range from the stack
thisRobot->findObjects(fillVector, range); // Put items in fillVector
// <<<< Create list of items, return it to lua >>>>
lua_createtable(L, fillVector.size(), 0);
int newTable = lua_gettop(L);
for(int i=0; i < fillVector.size(); i++) {
TestItem* item = fillVector[i];
item->push(L); // put an object, not a string, in Lua array
lua_rawseti(L, newTable, i + 1);
}
return 1;
}
This works perfectly. To clarify to others who are reading this, the method
item->push(L)
is
void push(lua_State *L) { Lunar<TestItem>::push(L, this); }
By encapsulating this in a method, it's possible to make the findItems agnostic to what it's finding.
Thank you for the help!