I have a C++ module for nodejs. I need to accept a key/value pair as argument for a method.
var my_map = {'key1': 'value1','key2': 'value2'};
Not sure what to do after this:
void MyClient::AcceptData(const FunctionCallbackInfo<Value>& args)
{
Isolate* isolate = args.GetIsolate();
if (args.Length() != 1)
{
isolate->ThrowException(v8::Exception::TypeError(String::NewFromUtf8(isolate,
"Usage: Key/Value Map")));
return;
}
// It returns true for args[0]->isObject(), but not for args[0]->IsMap()
// Now what? How do I get a C++ map out of args[0] ?
// What do I cast it into?
}
If you are sure it is a Map object, you can retrieve it through this code:
Handle<Map> map = Handle<Map>::cast(args[0]);
And then use the properties and attributes of the map.
Hope this helps.
Ok, I found the answer...
v8::Local<v8::Object> obj = args[0]->ToObject();
v8::Local<v8::Array> props = obj->GetPropertyNames();
std::map<std::string, std::string> theMap;
for (unsigned int i = 0; i < props->Length(); i++)
{
char key[1000], value[1000];
props->Get(i)->ToString()->WriteUtf8(key, 1000);
obj->Get(props->Get(i))->ToString()->WriteUtf8(value, 1000);
theMap.insert(std::make_pair(key, value));
}
Related
I am storing js callbacks in vector:
std::vector<std::unique_ptr<Persistent<Function>>> callbacks;
Everything is working fine. The problem is that I do not want to store duplicated callbacks because I do not want to notify the same callback twice. I have to compare them somehow. Here is my full function but that does not work:
void ProcessCallback(const FunctionCallbackInfo<Value>& args)
{
std::string returnInfo;
Isolate* isolate = args.GetIsolate();
Local<Function> notifyFunction = Local<Function>::Cast(args[0]);
auto predicate = [&](const std::unique_ptr<Persistent<Function>>& c)
{
return c->Get(isolate)->StrictEquals(notifyFunction);
};
auto it = std::find_if(callbacks.begin(), callbacks.end(), predicate);
if (it == callbacks.end())
{
returnInfo = "Did not find callback. Adding..." + std::to_string(callbacks.size());
auto persistentCallback = std::make_unique<Persistent<Function>>(isolate, notifyFunction);
callbacks.emplace_back(std::move(persistentCallback));
}
else
{
returnInfo = "Callback already exist in a list.\n";
}
args.GetReturnValue().Set(String::NewFromUtf8(isolate, returnInfo.c_str()).ToLocalChecked());
}
In js:
function callback(msg) {
console.log(msg);
}
let addon = require('./build/Release/addon');
console.log(addon.on(callback));
console.log(addon.on(callback));
Is there something that I can rely on to uniquely identify function that is passed from js? Thanks.
I'm trying to use dht to keep mutable data with libtorrent. As far as I can understand, the right way is to use the method dht_put_item from session. The problem is that I need to pass a callback function and I don't know what I'm doing wrong... my code looks like the following
namespace lt = libtorrent;
//The callback function
void cb(lt::entry& cdentry, boost::array<char,64>& cbarray, boost::uint64_t& cbint, std::string const& cbstring){
//My stuff here
}
void main(){
//The session
lt::session ses;
//The data I want to insert into DHT
std::string cadenaStr = "519d818411de49652b4aaf34850321de28bb2dce";
//Now I create the keys
unsigned char seed[32];
unsigned char public_key[32];
unsigned char private_key[64];
unsigned char signature[32];
ed25519_create_seed(seed);
ed25519_create_keypair(public_key, private_key, seed);
ed25519_sign(signature, cadenaStr.c_str(), sizeof(cadenaStr.c_str()), public_key, private_key);
//How can I use this?, where is the data supposed to go? :|
ses.dht_put_item(public_key, cb, false);
}
On libtorrent/session_handler.hpp this method is defined as
void dht_put_item(boost::array<char, 32> key
, boost::function<void(entry&, boost::array<char,64>&
, boost::uint64_t&, std::string const&)> cb
, std::string salt = std::string());
Can someone please tell me what I'm doing wrong.
Thanks!
There is an example in the libtorrent repository that I use for testing. It can generate keys, put and get both mutable and immutable items.
https://github.com/arvidn/libtorrent/blob/master/tools/dht_put.cpp
How can I use this?, where is the data supposed to go? :|
You provide the data in the callback that's called. The reason for this kind of API is that there are use cases where you want to mutate the data, and then you need to first know whether something is already stored under this key, and what it is.
You are missing the settings pack for your session.
lt::settings_pack settings;
settings.set_bool(settings_pack::enable_dht, false);
settings.set_int(settings_pack::alert_mask, 0xffffffff);
ses.apply_settings(settings);
settings.set_bool(settings_pack::enable_dht, true);
ses.apply_settings(settings);
Then you need to wait until you receive a boostrap message by waiting for an alert.
wait_for_alert(ses, dht_bootstrap_alert::alert_type);
Last, your dht_put_item call:
char const* cadenaStr = "519d818411de49652b4aaf34850321de28bb2dce";
dht_put_item(public_key, std::bind(&put_string, std::placeholders::_1, std::placeholders::_2, std::placeholders::_3, std::placeholders::_4, public_key, private_key, cadenaStr));
You will need these functions:
static alert* wait_for_alert(session* ses, int alert_type)
{
alert* ret = nullptr;
bool found = false;
while (!found)
{
ses->wait_for_alert(seconds(5));
std::vector<alert*> alerts;
ses->pop_alerts(&alerts);
for (std::vector<alert*>::iterator i = alerts.begin()
, end(alerts.end()); i != end; ++i)
{
if ((*i)->type() != alert_type)
{
continue;
}
ret = *i;
found = true;
}
}
return ret;
}
static void put_string(
entry& e
,boost::array<char, 64>& sig
,boost::int64_t& seq
,std::string const& salt
,boost::array<char, 32> const& pk
,boost::array<char, 64> const& sk
,char const* str)
{
using dht::sign_mutable_item;
if (str != NULL) {
e = std::string(str);
std::vector<char> buf;
bencode(std::back_inserter(buf), e);
dht::signature sign;
seq++;
sign = sign_mutable_item(buf, salt, dht::sequence_number(seq)
, dht::public_key(pk.data())
, dht::secret_key(sk.data()));
sig = sign.bytes;
}
}
For my assignment, I'm storing user login infos. I'm taking in a string which is the command. The command can be create, login, remove, etc. There are 10 total options, i.e 10 different strings possible. Can anyone explain a more efficient way to write this instead of 10 if and else if statements? Basically how should I format/structure things besides using a bunch of if (string == "one"), else if (string == "two"). Thank you
I expect that your lecturer would like you to extract function to another re-usable function:
string action;
command = CreateAction(action);
command.Do(...);
Ofcourse, inside you CreateAction class you still need to have the conditionals that determine which commands need to be created.
AbstractCommand CreateAction(action)
{
if (action == "login")
return LoginCommand();
else if (action == "remove")
return RemoveCommand();
..... etc etc
}
And if you really want to get rid of all the conditionals than you can create some self-registering commands but that involves a lot more code and classes......
You should look up things like Command Pattern and Factory Pattern
You can use function pointers and a lookup table.
typedef void (*Function_Pointer)(void);
void Create(void);
void Login(void);
void Remove(void);
struct Function_Option_Entry
{
const char * option_text;
Function_Pointer p_function;
};
Function_Option_Entry option_table[] =
{
{"one", Create},
{"two", Login},
{"three", Remove},
};
const unsigned int option_table_size =
sizeof(option_table) / sizeof(option_table[0]);
//...
std::string option_text;
//...
for (i = 0; i < option_table_size; ++i)
{
if (option_text == option_table[i].option_text)
{
option_table[i].p_function();
break;
}
}
Use a switch, and a simple hash-function.
You need to use a hash-function, because C and C++ only allow switching on integral values.
template<size_t N> constexpr char myhash(const char &x[N]) { return x[0] ^ (x[1]+63); }
char myhash(const string& x) { return x.size() ? x[0] ^ (x[1]+63) : 0; }
switch(myhash(s)) {
case myhash("one"):
if(s != "one") goto nomatch;
// do things
break;
case myhash("two"):
if(s != "two") goto nomatch;
// do things
break;
default:
nomatch:
// No match
}
Slight adjustments are needed if you are not using std::string.
I would recommend you to create a function for every specific string. For example, if you receive a string "create" you will call function doCreate(), if you receive a string "login" then you call function doLogin()
The only restriction on these function is that all of them must have the same signature. In an example above it was smh like this:
typedef void (*func_t) ();
The idea is to create a std::map from strings to these functions. So you wouldn't have to write 10 if's or so because you will be able to simple choose the right function from the map by the name of a specific string name. Let me explain it by the means of a small example:
typedef void (*func_t) ();
void doCreate()
{
std::cout << "Create function called!\n";
}
void doLogin()
{
std::cout << "Login function called!\n";
}
std::map<std::string, func_t> functionMap;
void initMap()
{
functionMap["create"] = doCreate;
functionMap["login"] = doLogin;
}
int main()
{
initMap();
std::string str = "login";
functionMap[str](); // will call doLogin()
str = "create";
functionMap[str](); // will call doCreate()
std::string userStr;
// let's now assume that we also can receive a string not from our set of functions
std::cin >> userStr;
if (functionMap.count(userStr))
{
functionMap[str](); // now we call doCreate() or doLogin()
}
else
{
std::cout << "Unknown command\n";
}
return 0;
}
I hope it will help you in someway=)
You can use a map which does the comparison for you.
Something like this:
Initialise map:
std::map<std::string, std::function<void(std::string&)>> map;
map["login"] = std::bind(&Class::DoLogin, this, std::placeholders::_1);
map["create"] = std::bind(&Class::DoCreate, this, std::placeholders::_1);
Receive message:
map.at(rx.msg_type)(rx.msg_data);
Handler:
void Class::DoLogin(const std::string& data)
{
// do login
}
Maybe you can create a std::map<std::string, int> and use map lookups to get the code of the command that was passed - you can later switch on that number. Or create an enum Command and have a std::map<std::string, Command> and use the switch.
Example:
enum Command
{
CREATE,
LOGIN,
...
};
std::map<std::string, Command> commandNameToCode;
// fill the map with appropriate values
commandNameToCode["create"] = Command::CREATE;
// somehow get command name from user and store in the below variable (not shown)
std::string input;
// check if the command is in the map and if so, act accordingly
if(commandNameToCode.find(input) != commandNameToCode.end())
{
switch(commandNameToCode[input])
{
case CREATE:
// handle create
break;
...
}
}
I am using the following code to read data from plist for my game:
int levelNum = SOME_VALUE_FROM_OUTSIDE;
ValueMap mapFile = FileUtils::getInstance()->getValueMapFromFile("LevelDetails.plist");
std::string strLevel = std::to_string(levelNum);
ValueMap mapLevel = mapFile.at(strLevel).asValueMap();
LevelDetails.plist is a plist with dictionary as root. The problem is there can be occasions where there is no key named levelNum / strLevel. So I have to check if the key exists before I run this line:
ValueMap mapLevel = mapFile.at(strLevel).asValueMap(); //Throws exception occasionally
So what is the proper way to check if a key named levelNum / strLevel exists or not?
Because ValueMap is a std::unordered_map, you can use the methods from that class:
if (mapFile.count(strLevel).count() > 0) {
ValueMap mapLevel = mapFile.at(strLevel).asValueMap();
}
Declaration of ValueMap in cocos2d-x is:
typedef std::unordered_map<std::string, Value> ValueMap;
You could also use the find method which will return an iterator to the key-element pair or a past-the-end iterator if the key isn't found.
auto it = mapFile.find(strLevel);
if (it != mapFile.end()) {
it->first; //key
it->second; //element
}
else {
//key not found
}
I came across this question for a similar reason and think I have found an appropriate solution, using cocos2d-x-3.11.1 (should also be applicable for older versions).
if( mapFile.at(strLevel).getType() != Value::Type::NONE ){
//OR if( mapFile[strLevel].getType() != Value::Type::NONE ) {
//if reached here then the 'key exists', thus perform desired line.
ValueMap mapLevel = mapFile.at(strLevel).asValueMap();
}
You could also check against a specific type defined in "CCValue.h" such as:
Value::Type::MAP
What we use is this:
string fullPath = cocos2d::FileUtils::getInstance()->fullPathForFilename("file.plist");
auto dataFromPlist = cocos2d::FileUtils::getInstance()->getValueMapFromFile(fullPath);
if (!dataFromPlist["key1"].isNull())
{
auto map = dataFromPlist["key1"].asValueMap();
//Do something else
}
I'm transforming a parser for v8 in NodeJS. Currently I have the following structure
struct Node {
short tag;
std::string data;
Node(std::string input, short tagId) {
tag = tagId;
data = input;
}
};
std::vector<Node> elems;
And I'm populating the vector from loop like this:
elems.push_back(Node(STRING, 3));
My goal is return a javascript object like this:
[
{ tag: 2, data: "asdsad" },
{ tag: 2, data: "asdsad" },
{ tag: 2, data: "asdsad" }
]
But since the V8 documentation is crappy, I couldn't figure out how to do it. My best shot was to make
Local<Value> data[2] = {
Local<Value>::New(Integer::New(2)),
String::New("test")
};
but I can't figure out how to make it an array and return it.
I'm using this example as template.
Here's what you might try (node v0.10.x):
// in globals
static Persistent<String> data_symbol;
static Persistent<String> tag_symbol;
// in addon initialization function
data_symbol = NODE_PSYMBOL("data");
tag_symbol = NODE_PSYMBOL("tag");
// in some function somewhere
HandleScope scope;
Local<Array> nodes = Array::New();
for (unsigned int i = 0; i < elems.length; ++i) {
HandleScope scope;
Local<Object> node_obj = Object::New();
node_obj->Set(data_symbol, String::New(elems[i].data.c_str()));
node_obj->Set(tag_symbol, Integer::New(elems[i].tag));
nodes->Set(i, node_obj);
}
return scope.Close(nodes);
I was looking for a solution for Node 5, and it seems mscdex's answer is outdated now. Hope this helps someone.
HandleScope scope(isolate);
Local<Array> nodes = Array::New(isolate);
for (unsigned int i = 0; i < elems.length; ++i) {
HandleScope scope(isolate);
Local<Object> node = Object::New(isolate);
node->Set(String::NewFromUtf8(isolate, "data"), String::New(isolate, elems[i].data.c_str()));
node->Set(String::NewFromUtf8(isolate, "tag"), Integer::New(isolate, elems[i].tag));
nodes->Set(i, node);
}
args.GetReturnValue().Set(nodes);