I would like to be able to create C++ classes and expose them into the V8 JavaScript engine. I'm using the v8pp library to do this, and by following their examples as well as the Google V8 Hello World Script, I have come to this code
main.cpp file
TestClass1.h - whose class I'd like to expose to JavaScript
CMakeList.txt file - so you can see how I've linked V8 if this is important):
// main.cpp
#define V8_COMPRESS_POINTERS
#include <v8.h>
#include <libplatform.h>
#include <v8pp/module.hpp>
#include <v8pp/class.hpp>
#include "src/TestClass1.h"
int main(int argc, char* argv[]) {
v8::V8::InitializeICUDefaultLocation(argv[0]);
v8::V8::InitializeExternalStartupData(argv[0]);
std::unique_ptr<v8::Platform> platform = v8::platform::NewDefaultPlatform();
v8::V8::InitializePlatform(platform.get());
v8::V8::Initialize();
v8::Isolate::CreateParams create_params;
create_params.array_buffer_allocator = v8::ArrayBuffer::Allocator::NewDefaultAllocator();
v8::Isolate *isolate = v8::Isolate::New(create_params);
{
v8::Isolate::Scope isolate_scope(isolate);
v8::HandleScope handle_scope(isolate);
v8::Local<v8::Context> context = v8::Context::New(isolate);
v8pp::module window(isolate);
v8pp::class_<TestClass1> TestClass1_V8(isolate);
TestClass1_V8
.ctor<int, int>()
.set("a", v8pp::property(&TestClass1::getA, &TestClass1::setA))
.set("b", v8pp::property(&TestClass1::getB, &TestClass1::setB))
//.set_static("c", 5, true)
.set("methodA", &TestClass1::testMethod);
window.set("TestClass1", TestClass1_V8);
isolate->GetCurrentContext()->Global()->Set(isolate->GetCurrentContext(), v8pp::to_v8(isolate, "window"), window.new_instance());
v8::Context::Scope context_scope(context);
{
v8::Local<v8::String> source = v8::String::NewFromUtf8Literal(isolate, "(function() {let t = new window.TestClass1(); t.a = 5; return t.a})()");
v8::Local<v8::Script> script = v8::Script::Compile(context, source).ToLocalChecked();
v8::Local<v8::Value> result = script->Run(context).ToLocalChecked();
v8::String::Utf8Value utf8(isolate, result);
printf("%s\n", *utf8);
}
}
return 0;
}
// src/TestClass1.h
#ifndef V8PP_TESTCLASS1_H
#define V8PP_TESTCLASS1_H
class TestClass1 {
friend int main(int argc, char* argv[]);
public:
static int m_c;
TestClass1(int a, int b) {
m_a = a;
m_b = b;
}
int testMethod(int c) {
return m_a + m_b + c;
}
private:
int m_a;
int m_b;
int getA() {
return m_a;
}
void setA(int a) {
m_a = 2 * a;
}
int getB() {
return m_b;
}
void setB(int b) {
m_b = 3 + b;
}
};
#endif //V8PP_TESTCLASS1_H
# CMakeList.txt
cmake_minimum_required(VERSION 3.19)
project(V8PP)
set(CMAKE_CXX_STANDARD 20)
### V8 ### [MANUAL]
set(CMAKE_CXX_FLAGS "/MT")
set(CMAKE_C_FLAGS "/MT")
if(MSVC)
add_compile_options(
$<$<CONFIG:>:/MT> #---------|
$<$<CONFIG:Debug>:/MTd> #---|-- Statically link the runtime libraries
$<$<CONFIG:Release>:/MT> #--|
)
endif()
include_directories(E:/V8/depot_tools/v8/include)
include_directories(E:/V8/depot_tools/v8/include/libplatform)
include_directories(${CMAKE_SOURCE_DIR}/v8pp-master/v8pp-master)
link_directories(E:/V8/depot_tools/v8/out.gn/x64.release/obj/)
link_directories(E:/V8/depot_tools/v8/out.gn/x64.release/obj/third_party)
link_directories(E:/V8/depot_tools/v8/out.gn/x64.release/obj/third_party/icu)
link_libraries(
v8_libbase
v8_libplatform
v8_monolith
icuuc
icui18n
)
link_libraries(winmm.lib)
link_libraries(dbghelp.lib)
link_libraries(shlwapi.lib)
### V8 ###
add_executable(V8PP main.cpp)
I've isolated the error down to the line
isolate->GetCurrentContext()->Global()->Set(isolate->GetCurrentContext(), v8pp::to_v8(isolate, "window"), window.new_instance());
Specifically window.new_instance(). Going into the v8pp source code (file module.hpp), the only line in the method is
return obj_->NewInstance(isolate_->GetCurrentContext()).ToLocalChecked();
I separated out the different statements onto separate lines, and the error is coming from the obj_->NewInstance(), where obj_ is a v8::Local<v8::ObjectTemplate>, created in the initializer list of the constructor of the module object. This function call is part of v8 itself, but I only have access to the header files of v8, so I don't know what has caused the error.
The code builds fine, but when it's run, there isn't a traceback, just:
Process finished with exit code -1073741819 (0xC0000005)
implying a memory access error (maybe to do with pointers?)
Does anyone know how to add a new instance of a v8pp module into the v8 engine without this crash occurring?
Edit
Using:
Windows 10
C++ 20
CMake (on CLion)
MSVC 2019 64-bit
I found the issue: firstly, I had to move the line of code
v8::Context::Scope context_scope(context);
to directly under the line
v8::Local<v8::Context> context = v8::Context::New(isolate);
This did create another error
#
# Fatal error in v8::ToLocalChecked
# Empty MaybeLocal.
#
<unknown>:21: Uncaught argument count does not match function definition
which was because when I called the constructor in JavaScript, I forgot to add the arguments, so changing the JavaScript code to
(function() {let t = new window.TestClass1(); t.a = 5; return t.a;})()
and everything works.
Related
I am trying to install Catch2 on ubuntu 20.04.
Used instruction from here.
This is what i do:
$ git clone https://github.com/catchorg/Catch2.git
$ cd Catch2
$ cmake -Bbuild -H. -DBUILD_TESTING=OFF
$ sudo cmake --build build/ --target install
Than it saing me that all ok: link for output.
BUT:
When I try to compile the example: // from here
main.cpp
#define CATCH_CONFIG_MAIN // This tells Catch to provide a main() - only do this in one cpp file
#define CATCH_CONFIG_ENABLE_BENCHMARKING
#include <catch2/catch.hpp>
std::uint64_t Fibonacci(std::uint64_t number) {
return number < 2 ? 1 : Fibonacci(number - 1) + Fibonacci(number - 2);
}
TEST_CASE("Fibonacci") {
CHECK(Fibonacci(0) == 1);
// some more asserts..
CHECK(Fibonacci(5) == 8);
// some more asserts..
// now let's benchmark:
BENCHMARK("Fibonacci 20") {
return Fibonacci(20);
};
BENCHMARK("Fibonacci 25") {
return Fibonacci(25);
};
BENCHMARK("Fibonacci 30") {
return Fibonacci(30);
};
BENCHMARK("Fibonacci 35") {
return Fibonacci(35);
};
}
CMakeLists.txt
cmake_minimum_required(VERSION 3.5 FATAL_ERROR)
project(Persistent-world LANGUAGES CXX)
add_executable(${PROJECT_NAME} main.cpp )
find_package(Catch2 REQUIRED)
target_link_libraries(${PROJECT_NAME} Catch2::Catch2)
It output such ERROR: catch2/catch.hpp: No such file or directory
Thanks in advance
The problem is quite simple: clonning catchorg/Catch2 now gets you a v3 branch by default, which works differently. The most important change is that it is no longer single header, and that the catch2/catch.hpp header no longer exists.
You can either switch to the v2 branch before configuring and installing the build, or adapt your code to the changes in v3, starting with this documentation on v2 -> v3 migration.
To get the default main, link against Catch2::Catch2WithMain target.
Admin helped me.
On catch v3. I need:
cmake_minimum_required(VERSION 3.5)
project(Catch2 LANGUAGES CXX)
add_executable(${PROJECT_NAME} main.cpp )
find_package(Catch2)
target_link_libraries(${PROJECT_NAME} Catch2::Catch2WithMain)
If you just link against Catch2::Catch2, you won't get the default main and have to write your own, and your own main needs to invoke the tests. See e.g. that
Than i understand that with main it should looks like:
#define CATCH_CONFIG_MAIN // This tells Catch to provide a main() - only do this in one cpp file
#define CATCH_CONFIG_ENABLE_BENCHMARKING
#include <catch2/catch_all.hpp>
std::uint64_t Fibonacci(std::uint64_t number) {
return number < 2 ? 1 : Fibonacci(number - 1) + Fibonacci(number - 2);
}
TEST_CASE("Fibonacci") {
CHECK(Fibonacci(0) == 1);
// some more asserts..
CHECK(Fibonacci(5) == 8);
// some more asserts..
// now let's benchmark:
BENCHMARK("Fibonacci 20") {
return Fibonacci(20);
};
BENCHMARK("Fibonacci 25") {
return Fibonacci(25);
};
BENCHMARK("Fibonacci 30") {
return Fibonacci(30);
};
BENCHMARK("Fibonacci 35") {
return Fibonacci(35);
};
}
int main( int argc, char* argv[] )
{
Catch::Session session; // There must be exactly one instance
// writing to session.configData() here sets defaults
// this is the preferred way to set them
int returnCode = session.applyCommandLine( argc, argv );
if( returnCode != 0 ) // Indicates a command line error
return returnCode;
// writing to session.configData() or session.Config() here
// overrides command line args
// only do this if you know you need to
int numFailed = session.run();
// numFailed is clamped to 255 as some unices only use the lower 8 bits.
// This clamping has already been applied, so just return it here
// You can also do any post run clean-up here
return numFailed;
}
Is V8 Broken or am I?
I want to add a JS Date to a Object available in the global object. This works with a older version (4.9.385.28) but fails with (5.0.71.33)... see output
g++ -I/usr/local core.c -o testCore -ldl -pthread -std=c++0x -lv8 -lv8_libplatform -lv8_libbase
core.c:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "include/libplatform/libplatform.h"
#include "include/v8.h"
#include "time.h"
using namespace v8;
class ArrayBufferAllocator : public v8::ArrayBuffer::Allocator
{
public:
virtual void* Allocate(size_t length) { void* data = AllocateUninitialized(length); return data == NULL ? data : memset(data, 0, length); }
virtual void* AllocateUninitialized(size_t length) { return malloc(length); }
virtual void Free(void* data, size_t) { free(data); }
};
int main(int argc, char* argv[])
{
// Initialize V8.
V8::InitializeICU();
V8::InitializeExternalStartupData(argv[0]);
Platform* platform = platform::CreateDefaultPlatform();
V8::InitializePlatform(platform);
V8::Initialize();
// Create a new Isolate and make it the current one.
ArrayBufferAllocator allocator;
Isolate::CreateParams create_params;
create_params.array_buffer_allocator = &allocator;
Isolate* isolate = Isolate::New(create_params);
Isolate::Scope isolate_scope(isolate);
// Create a stack-allocated handle scope.
HandleScope handle_scope(isolate);
Local<Context> context = Context::New(isolate,NULL,ObjectTemplate::New(isolate));
Context::Scope context_scope(context);
Local<ObjectTemplate> activity = ObjectTemplate::New(isolate);
// ################################################################################################################################
// ************************ This line breaks the NewInstance() call (5.0.71.33) works in (4.9.385.28) *****************************
// ################################################################################################################################
activity->Set (String::NewFromUtf8(isolate, "createddate", NewStringType::kNormal).ToLocalChecked(), Date::New(isolate,time(NULL)*1000.0));
Local<Object> activityInst = activity->NewInstance();
context->Global()->Set(String::NewFromUtf8(isolate, "activity", NewStringType::kNormal).ToLocalChecked(), activityInst);
return 0;
}
output:
#
# Fatal error in ../src/heap/heap.cc, line 3564
# Check failed: map->instance_type() == JS_REGEXP_TYPE || map->instance_type() == JS_OBJECT_TYPE || map->instance_type() == JS_ARRAY_TYPE.
#
==== C stack trace ===============================
1: V8_Fatal
2: v8::internal::Heap::CopyJSObject(v8::internal::JSObject*, v8::internal::AllocationSite*)
3: v8::internal::Factory::CopyJSObjectWithAllocationSite(v8::internal::Handle<v8::internal::JSObject>, v8::internal::Handle<v8::internal::AllocationSite>)
4: v8::internal::JSObjectWalkVisitor<v8::internal::DummyContextObject, (v8::internal::BoilerplateKind)1>::StructureWalk(v8::internal::Handle<v8::internal::JSObject>)
5: v8::internal::JSObjectWalkVisitor<v8::internal::DummyContextObject, (v8::internal::BoilerplateKind)1>::StructureWalk(v8::internal::Handle<v8::internal::JSObject>)
6: v8::internal::JSObject::DeepCopyApiBoilerplate(v8::internal::Handle<v8::internal::JSObject>)
7: v8::internal::(anonymous namespace)::InstantiateObject(v8::internal::Isolate*, v8::internal::Handle<v8::internal::ObjectTemplateInfo>, bool)
8: v8::internal::ApiNatives::InstantiateObject(v8::internal::Handle<v8::internal::ObjectTemplateInfo>)
9: v8::ObjectTemplate::NewInstance(v8::Local<v8::Context>)
10: main
11: start
12: 0x1
Illegal instruction
You cannot add javascript objects to ObjectTemplates. You can only add templates. This changed between the versions you mention.
The idea, I think, is that a template will be instantiated many times and associating the same object with each of them doesn't make sense. Whereas associated templates will also be re-instantiated giving different sub-objects for each parent object.
You're free to associate anything with the resulting object after NewInstance() is called, though.
I am currently working on ubuntu 12.04 . I have simple hello world program in c++ with v8 binding as follows :
#include "include/v8.h"
#include "include/libplatform/libplatform.h"
using namespace v8;
int main(int argc, char* argv[]) {
// Initialize V8.
V8::InitializeICU();
Platform* platform = platform::CreateDefaultPlatform();
V8::InitializePlatform(platform);
V8::Initialize();
// Create a new Isolate and make it the current one.
//Isolate* isolate = Isolate::GetCurrent();
Isolate* isolate = Isolate::New();
{
Isolate::Scope isolate_scope(isolate);
// Create a stack-allocated handle scope.
HandleScope handle_scope(isolate);
// Create a new context.
Local<Context> context = Context::New(isolate);
// Enter the context for compiling and running the hello world script.
Context::Scope context_scope(context);
// Create a string containing the JavaScript source code.
Local<String> source = String::NewFromUtf8(isolate, "'Hello' + ', World!'");
// Compile the source code.
Local<Script> script = Script::Compile(source);
// Run the script to get the result.
Local<Value> result = script->Run();
// Convert the result to an UTF8 string and print it.
String::Utf8Value utf8(result);
printf("%s\n", *utf8);
}
// Dispose the isolate and tear down V8.
isolate->Dispose();
V8::Dispose();
V8::ShutdownPlatform();
delete platform;
return 0;
}
I just want
compilation( Local<Script> script = Script::Compile(source);)
and
execution(Local<Value> result = script->Run();)
in different functions. Is it possible? if yes , how? Can anybody help
I am developing a ROS Qt GUI application and I face a problem on ROS Hydro (I had the same problem while working on ROS Fuerte). My project does not recognize my library like image_transport.h. I added it to the beginning of the qnode.hpp file but it did not solve the problem.
My major problem:
/home/attila/catkin_ws/src/arayuz/src/qnode.cpp:-1: error: undefined reference to `image_transport::ImageTransport::ImageTransport(ros::NodeHandle const&)'
This is the code that generates the error:
#include "ros/ros.h"
#include "ros/network.h"
#include "string"
#include "std_msgs/String.h"
#include "sstream"
#include "../include/arayuz/qnode.hpp"
namespace enc=sensor_msgs::image_encodings;
static const char WINDOW[ ]="Kinect";
namespace arayuz {
QNode::QNode(int argc, char** argv ) :
init_argc(argc),
init_argv(argv)
{}
QNode::~QNode() {
if(ros::isStarted()) {
ros::shutdown(); // explicitly needed since we use ros::start();
ros::waitForShutdown();
}
cv::destroyWindow(WINDOW);
wait();
}
bool QNode::init() {
ros::init(init_argc,init_argv,"arayuz");
if ( ! ros::master::check() ) {
return false;
}
ros::start(); // explicitly needed since our nodehandle is going out of scope.
ros::NodeHandle n;
// Add your ros communications here.
image_transport::ImageTransport it(n);
imagesub = it.subscribe("/kinectCamera", 1,&QNode::chatterimage,this);
start();
return true;
}
bool QNode::init(const std::string &master_url, const std::string &host_url) {
std::map<std::string,std::string> remappings;
remappings["__master"] = master_url;
remappings["__hostname"] = host_url;
ros::init(remappings,"arayuz");
if ( ! ros::master::check() ) {
return false;
}
ros::start(); // explicitly needed since our nodehandle is going out of scope.
ros::NodeHandle n;
// Add your ros communications here.
image_transport::ImageTransport it(n);
imagesub = it.subscribe("/kinectCamera",1,&QNode::chatterimage,this);
start();
return true;
}
void QNode::chatterimage(const sensor_msgs::ImageConstPtr& msg)
{
rgbimage=cv_bridge::toCvCopy(msg,enc::BGR8);
Q_EMIT chatterimageupdate();
}
void QNode::run() {
while ( ros::ok() ) {
ros::spin();
}
std::cout << "Ros shutdown, proceeding to close the gui." << std::endl;
Q_EMIT rosShutdown(); // used to signal the gui for a shutdown (useful to roslaunch)
}
}
In order to link against ROS libraries you need to add the dependency to your package.xml file:
<build_depend>image_transport</build_depend>
<run_depend>image_transport</run_depend>
and to your CMakeLists.txt:
find_package(catkin REQUIRED COMPONENTS
...
image_transport
...
)
Just adding the header to your compilation isn't sufficient: the error message states that you successfully compiled the code but it failed to link the executable. You will also need to find the library implementing the image_transport code and link it to your executable. I have no idea of ros but here is a link which seems to describe how to build code using this library.
I want to start Boost test case from dll under Windows RT. I built test case as dll via the Visual Studio command prompt using the following comandline:
cl.exe /EHsc /D_USRDLL /D_WINDLL /LDd ~location\testcase.cpp ~library location\libboost_unit_test_framework-vc110-mt-sgd-1_53.lib /link /DLL /OUT:~output directory\testcase.dll
placed it into my application’s folder and set property "Content" to "true". After launching of my application I have the following error:
Unhadled exception at the 0x00B9AF16 in TestApp.exe: 0xC0000005: Access violation reading location 0x00000000
Top of the call stack is below:
> TestApp.exe!boost::unit_test::framework::get(unsigned long id, boost::unit_test::test_unit_type t) Line 388 C++
TestApp.exe!boost::unit_test::framework::get(unsigned long id) Line 73 C++
TestApp.exe!boost::unit_test::traverse_test_tree(unsigned long id, boost::unit_test::test_tree_visitor & V) Line 232 C++
TestApp.exe!boost::unit_test::traverse_test_tree(const boost::unit_test::test_suite & suite, boost::unit_test::test_tree_visitor & V) Line 207 C++
TestApp.exe!boost::unit_test::traverse_test_tree(unsigned long id, boost::unit_test::test_tree_visitor & V) Line 234 C++
TestApp.exe!boost::unit_test::framework::run(unsigned long id, bool continue_test) Line 403 C++
TestApp.exe!boost::unit_test::unit_test_main(boost::unit_test::test_suite * (int, char * *) * init_func, int argc, char * * argv) Line 185 C++
Here is the dll code (NOTE: If I place the same code directly into my source, it works fine):
void test_stat()
{
//some code there
}
extern "C" {
__declspec (dllexport) test_suite* init_unit_test_suite( int argc, char* argv[] )
{
test_suite *test = BOOST_TEST_SUITE("test name");
test->add(BOOST_TEST_CASE(&test_stat));
return test;
}
}
Code of the application for launching of the test case:
boost::unit_test::test_suite* main_global_test_suite;
test_suite* init_unit_test_suite( int argc, char* argv[] ) {
return NULL; }
test_suite* run_global_test_suite (int, char* []) {
return main_global_test_suite;
}
HINSTANCE hMyDll;
typedef test_suite* (*PFN_MyFunction)(int,const char*);
PFN_MyFunction pfnMyFunction;
test_suite* rPtr;
if((hMyDll=::LoadPackagedLibrary(L"testcase", 0))==NULL)
{
return;
}
pfnMyFunction=(PFN_MyFunction)GetProcAddress(hMyDll,"init_unit_test_suite");
if (pfnMyFunction != NULL)
{
//just create fake arguments for the boost::unit_test::unit_test_main function call
char* argv[1024];
argv[0] = "Text";
rPtr = pfnMyFunction(1, NULL);
main_global_test_suite = rPtr;
const int error =
boost::unit_test::unit_test_main(&run_global_test_suite, 1, argv );
}
else
{
//handling code
}
FreeLibrary(hMyDll);
Is there any ideas how to solve the problem?
Check what console_test_runner is doing. This is command line application (part of Boost.Test), which intended to do just that - load and execute test units implemented in shared library. Also please make sure you tell UTF that you want to build dll: define BOOST_TEST_DYN_LINK.