I'm currently trying to get the return value of a function that I call in JS. The following code can reproduce it (minus v8 includes)
#include "v8.h"
#include "libplatform/libplatform.h"
#include <string>
#include <cassert>
int64_t repro()
{
auto isolate = v8::Isolate::New(initializer.create_params_);
assert(isolate != nullptr);
v8::Isolate::Scope isolate_scope(isolate);
v8::HandleScope handle_scope(isolate);
auto context = v8::Context::New(isolate);
v8::Context::Scope context_scope(context);
assert(context.IsEmpty() == false);
auto global = context->Global();
std::string script = "function foo() {\n"
" return BigInt(1);\n"
"}";
v8::Local<v8::String> sourceScript =
v8::String::NewFromUtf8(isolate, script.c_str(),
v8::NewStringType::kNormal)
.ToLocalChecked();
v8::Local<v8::Script> s =
v8::Script::Compile(context, sourceScript).ToLocalChecked();
s->Run(context);
v8::Local<v8::String> name =
v8::String::NewFromUtf8(isolate, "foo",
v8::NewStringType::kInternalized)
.ToLocalChecked();
auto value = global->Get(context, name).ToLocalChecked();
assert(value->IsFunction());
auto func = v8::Handle<v8::Function>::Cast(value);
auto result = func->Call(context, context->Global(), 0, nullptr)
.ToLocalChecked();
assert(result->IsBigInt());
auto bigint = result->IntegerValue(context);
assert(bigint.IsNothing() == false);
return bigint.ToChecked();
}
when I now look at bigint - the type reports as a BigInt, but IsNothing() returns true. What am I doing wrong?
Thank you
Tobias
As the documentation says, v8::Value::IntegerValue() "Returns the equivalent of ToInteger()->Value()", which means it throws an exception (i.e. returns Nothing) when invoked on a BigInt, reflecting the fact that in JavaScript, calling the "abstract operation" ToInteger() on a BigInt throws a TypeError, or in other words: a BigInt doesn't just implicitly convert to a Number.
To extract a BigInt's value from C++, you can do this:
int64_t bigint = v8::Local<v8::BigInt>::cast(result)->Int64Value();
Of course that will give an incorrect result when the BigInt's value is bigger than an int64. It takes an optional bool* to indicate whether the conversion to int64 was lossless or truncating. If you need to get to bigger values, you can use the ToWordsArray(...) method.
how to get the return value of a JS function from V8?
Exactly like you did:
v8::MaybeLocal<v8::Value> result = func->Call(...);
Note that using .ToLocalChecked(); is risky: if the function throws an exception instead of returning a value, then .ToLocalChecked() will crash. If you don't control the function's code and hence can't guarantee that it won't throw, then it's better to test whether the result is empty, and handle exceptions gracefully. See V8's samples/ directory, or the documention on v8.dev/docs/, for lots of examples and additional explanations.
(Side note: I would recommend to use auto much less. It helps to see the types. The differences between, say, v8::Value, v8::Local<v8::Value>, v8::MaybeLocal<v8::Value>, and v8::Local<v8::BigInt> are meaningful, and it helps you write correct code when you don't just hide them behind auto.)
Related
In Xlib the structure XRRModeInfo contains, aside from nameLength field, the name itself. But in XCB the corresponding structure xcb_randr_mode_info_t only contains name_len, and there seems to be no function to get actual name string.
I do see all the mode names in the string returned by xcb_randr_get_screen_resources_names(), but they are all concatenated, and I don't know how to find the offset of a particular mode in this string.
So, how can I get the mode name using XCB?
I do see all the mode names in the string returned by xcb_randr_get_screen_resources_names(), but they are all concatenated, and I don't know how to find the offset of a particular mode in this string.
You have the length of the individual names and you know the length of each name, so you just have to count bytes:
#include <stdio.h>
#include <xcb/randr.h>
int main()
{
xcb_connection_t *c = xcb_connect(NULL, NULL);
xcb_screen_t *screen = xcb_setup_roots_iterator(xcb_get_setup(c)).data;
// TODO: Error handling
// TODO: Checking if the RandR extension is available
xcb_randr_get_screen_resources_reply_t *reply =
xcb_randr_get_screen_resources_reply(c,
xcb_randr_get_screen_resources(c, screen->root),
NULL);
xcb_randr_mode_info_iterator_t iter = xcb_randr_get_screen_resources_modes_iterator(reply);
uint8_t *names = xcb_randr_get_screen_resources_names(reply);
while (iter.rem) {
xcb_randr_mode_info_t *mode = iter.data;
printf("Mode %d has size %dx%d and name %.*s\n",
mode->id, mode->width, mode->height, mode->name_len, names);
names += mode->name_len;
xcb_randr_mode_info_next(&iter);
}
free(reply);
xcb_disconnect(c);
return 0;
}
I am using snprintf to format string using user-defined format (also given as string). The code looks like this:
void DataPoint::valueReceived( QVariant value ) {
// Get the formating QVariant, which is only considered valid if it's string
QVariant format = this->property("format");
if( format.isValid() && format.type()==QMetaType::QString && !format.isNull() ) {
// Convert QString to std string
const std::string formatStr = format.toString().toStdString();
LOGMTRTTIINFO(pointName<<"="<<value.toString().toUtf8().constData()<<"=>"<<formatStr<<"["<<formatStr.length()<<'\n');
// The attempt to catch exceptions caused by invalid formating string
try {
if( value.type() == QMetaType::QString ) {
// Treat value as string (values are allways ASCII)
const std::string array = value.toString().toStdString();
const char* data = (char*)array.c_str();
// Assume no more than 10 characters are added during formating.
char* result = (char*)calloc(array.length()+10, sizeof(char));
snprintf(result, array.length()+10, formatStr.c_str(), data);
value = result;
}
// If not string, then it's a number.
else {
double data = value.toDouble();
char* result = (char*)calloc(30, sizeof(char));
// Even 15 characters is already longer than largest number you can make any sense of
snprintf(result, 30, formatStr.c_str(), data);
LOGMTRTTIINFO(pointName<<"="<<data<<"=>"<<formatStr<<"["<<formatStr.length()<<"]=>"<<result<<'\n');
value = result;
}
} catch(...) {
LOGMTRTTIERR("Format error in "<<pointName<<'\n');
}
}
ui->value->setText(value.toString());
}
As you can see I assumed there will be some exception. But there's not, invalid formatting string results in gibberish. This is what I get if I try to format double using %s:
So is there a way to detect that invalid formatting option was selected, such as formatting number as string or vice-versa? And what if totally invalid formatting string is given?
You ask if it's possible to detect format/argument mismatch at run-time, right? Then the short and only answer is no.
To expand on that "no" it's because Variable-argument functions (functions using the ellipsis ...) have no kind of type-safety. The compiler will convert some types of arguments to others (e.g. char or short will be converted to int, float will be converted to double), and if you use a literal string for the format some compilers will be able to parse the string and check the arguments you pass.
However since you pass a variable string, that can change at run-time, the compiler have no possibility for any kind of compile-time checking, and the function must trust that the format string passed is using the correct formatting for the arguments passed. If it's not then you have undefined behavior.
It should be noted that snprintf might not actually fail when being passed mismatching format specifier and argument value.
For example if using the %d format to print an int value, but then passing a double value, the snprintf would happily extract sizeof(int) bytes from the double value, and interpret it as an int value. The value printed will be quite unexpected, but there won't be a "failure" as such. Only undefined behavior (as mentioned above).
Thus it's not really possible to detect such errors or problems at all. At least not through the code. This is something that needs proper testing and code-review to catch.
What happens when snprintf fails? When snprintf fails, POSIX requires that errno is set:
If an output error was encountered, these functions shall return a negative value and set errno to indicate the error.
Also you can find some relevant information regarding how to handle snprintf failures Here.
Okay, I believe in defensive programming. I assume that if I get a pointer it might be null (especially when using GSOAP). Therefore before I try to use the value of the pointer, I always check to make sure the pointer is not null.
In my current code, this is leading to a lot of nearly identical statements.
if (res->A) {
item.out_trace->a = *res->A;
}
if (res->B) {
item.out_trace->b = *res->B;
}
if (res->C) {
item.out_trace->b = *res->C;
}
I realize that I could always go and define a macro for this, but I am wondering if there is a neat C++11 trick to do that. I would love something like the C# ??
// Set y to the value of x if x is NOT null; otherwise,
// if x = null, set y to -1.
int y = x ?? -1;
Thanks.
Perhaps a template like this would meet your need:
template<typename T>
T safe_get( T const *ptr, T defval = T{} ) {
return ptr ? *ptr : std::move(defval);
}
It could be used like this:
item.out_trace->a = safe_get( rez->A );
Ideally it would be inlined and effectively zero-overhead (other than the inherent overhead of doing the safety check and having a branch, of course).
Google's v8 documentation describes how to add a global function to a JavaScript context. We can implement a printf-like function quite easily using the new lambda feature from C++11:
Handle<ObjectTemplate> global = ObjectTemplate::New();
global->Set(String::New("print"), FunctionTemplate::New(
[](const v8::Arguments &args) -> v8::Handle<v8::Value>
{
v8::String::AsciiValue ascii(args[0]);
std::cout << *ascii << "\n";
} ));
Persistent<Context> context = Context::New(NULL, global);
This works well for any global JavaScript function that is either stateless or references a global C++ variable (i.e. std::cout). But what if we want our global JavaScript function to reference a non-global C++ variable? For example, suppose we are creating several different JavaScript contexts each with its own global print function that uses a different C++ std::ostream? If v8 function templates used std::function objects instead of function pointers, the we would do something like this:
Persistent<Context> create_context(std::ostream &out)
{
Handle<ObjectTemplate> global = ObjectTemplate::New();
global->Set(String::New("print"), FunctionTemplate::New(
[&out](const v8::Arguments &args) -> v8::Handle<v8::Value>
{
v8::String::AsciiValue ascii(args[0]);
out << *ascii << "\n";
} ));
return Context::New(NULL, global);
}
Unfortunately, v8 does not seem to support this. I assume (hope?) that v8 has a way of doing something functionally equivalent, but I find myself mystified by the Doxygen for v8::FunctionTemplate. Would anyone who has attempted something similar be willing to distill the process down into something more understandable? I would also like to learn how to create a global instance of a JavaScript object that is bound to an existing, non-global instance of a C++ object.
In answer to my own question... the key is to realize that v8::Arguments is not simply an array of arguments. It also contains the exceedingly useful Callee() and Data() methods. If the function is a method of a JavaScript object then Callee() can, I think, be used to get ahold of whatever instance of that object the method was called on. Useful state information could then be stored in the object instance. You can also supply a data handle, which may point to any C++ object through void*, when adding a function template to an object. This function-specific data handle may then be accessed through the Data() method.
Below is a reasonably complete example of what I was trying to do in the question using v8::Arguments::Data(). Hopefully this will be useful to anyone who wants to do something similar. If you have an alternative strategy you like (and I am certain there is more than one way of doing this), please feel free to add it in another answer!
#include <iostream>
#include <ostream>
#include <v8.h>
// add print() function to an object template
void add_print(v8::Handle<v8::ObjectTemplate>& ot, std::ostream* out)
{
// add function template to ot
ot->Set(v8::String::New("print"), v8::FunctionTemplate::New(
// parameter 1 is the function callback (implemented here as a lambda)
[](const v8::Arguments& args)->v8::Handle<v8::Value>
{
// recover our pointer to an std::ostream from the
// function template's data handle
v8::Handle<v8::External> data = v8::Handle<v8::External>::Cast(args.Data());
std::ostream* out = static_cast<std::ostream*>(data->Value());
// verify that we have the correct number of function arguments
if ( args.Length() != 1 )
return v8::ThrowException(v8::String::New("Too many arguments to print()."));
// print the ascii representation of the argument to the output stream
v8::String::AsciiValue ascii(args[0]);
*out << *ascii << "\n";
// like 'return void;' only in JavaScript
return v8::Undefined();
},
// parameter 2 is the data handle with the pointer to an std::ostream
v8::External::New(out)
));
}
int main()
{
// create a stack-allocated handle scope
v8::HandleScope handle_scope;
// create a global template
v8::Local<v8::ObjectTemplate> global = v8::ObjectTemplate::New();
// add a print() function using std::cout to the global template
add_print(global, &std::cout);
// create a context
v8::Persistent<v8::Context> context = v8::Context::New(nullptr, global);
// enter the created context
v8::Context::Scope context_scope(context);
// create a string containing the JavaScript source code
v8::Local<v8::String> source = v8::String::New("print('1 + 1 = ' + (1 + 1));");
// compile the source code
v8::Local<v8::Script> script = v8::Script::Compile(source);
// run the script
script->Run();
// dispose of the persistent context
context.Dispose();
return 0;
}
Why does the following code throw an exception when getting to the second scanf_s after entering an number to put into the struct.
This by no means represents a complete linked list implementation.
Not sure how to get onto the next scanf_s when having entered the value? Any ideas?
EDIT: Updated code with suggested solution, but still get an AccessViolationException after first scanf_s
Code:
struct node
{
char name[20];
int age;
float height;
node *nxt;
};
int FillInLinkedList(node* temp)
{
int result;
temp = new node;
printf("Please enter name of the person");
result = scanf_s("%s", temp->name);
printf("Please enter persons age");
result = scanf_s("%d", &temp->age); // Exception here...
printf("Please enter persons height");
result = scanf_s("%f", &temp->height);
temp->nxt = NULL;
if (result >0)
return 1;
else return 0;
}
// calling code
int main(array<System::String ^> ^args)
{
node temp;
FillInLinkedList(&temp);
...
You are using scanf_s with incorrect parameters. Take a look at the examples in the MSDN documentation for the function. It requires that you pass in the size of the buffer after the buffer for all string or character parameters. So
result = scanf_s("%s", temp->name);
should be:
result = scanf_s("%s", temp->name, 20);
The first call to scanf_s is reading garbage off the stack because it is looking for another parameter and possibly corrupting memory.
There is no compiler error because scanf_s uses a variable argument list - the function doesn't have a fixed number of parameters so the compiler has no idea what scanf_s is expecting.
You need
result = scanf_s("%d", &temp->age);
and
result = scanf_s("%f", &temp->height);
Reason is that sscanf (and friends) requires a pointer to the output variable so it can store the result there.
BTW, you have a similar problem with the parameter temp of your function. Since you're changing the pointer (and not just the contents of what it points to), you need to pass a double pointer so that the changes will be visible outside your function:
int FillInLinkedList(node** temp)
And then of course you'll have to make the necessary changes inside the function.
scanf() stores data into variables, so you need to pass the address of the variable (or its pointer)Example:
char string[10];
int n;
scanf("%s", string); //string actually points to address of
//first element of string array
scanf("%d", &n); // &n is the address of the variable 'n'
%19c should be %s
temp->age should be &temp-age
temp->height should be &temp->height
Your compiler should be warning you
about these errors
I believe you need to pass parameters to scanf() functions by address. i.e. &temp->age
otherwise temp-age will be interpreted as a pointer, which will most likely crash your program.