I am fairly new to Qt, and I am trying to do some Android Development. I am working with Qt and using the QAndroidJNIEnvironment. In the code, I am implementing my native method using QMetaObject::invokeMethod to invoke a slot in the QMainWindow header. The problem is that the native method in the java file has a parameter that is a java integer array(equivalent type I believe in QAndroindJniObject is jintArray). I can't find the corresponding c++/Qt type to place in the Q_ARG(type, value ) macro to resolve the argument. Please help me understand what I am doing wrong, as i thought the equivalent type to jintArray was int [], but I receive error when I use that. Thanks in advance for the help.
onReceiveNativeMounted (JNIEnv * env, jobject obj,jint array_index,jintArray version)
{
QMetaObject::invokeMethod(&MainWindow::instance(), "onReceiveMounted"
, Qt::QueuedConnection, Q_ARG(int, array_index),Q_ARG(int[], version));
return array_index;
}
the error i receive is below:
error: no matching function for call to
'QArgument<int []>::QArgument(const char [6], _jarray*&)'
#define Q_ARG(type, data) QArgument<type >(#type, data)
^
As requested, the java function signature is below:
public static native int onReceiveNativeMounted(int array_index, int[] version);
You need to access the java arrays according to the JNI API. The easiest thing to do is to convert the data to a QVector. You need to copy the Java array since its lifetime is not under your control (unless you wish it to be, but that makes life much harder than it needs to be).
QVector toQVector(JNIEnv * env, jintArray arr) {
auto len = (*env)->GetArrayLength(env, arr);
QVector result(len);
auto data = (*env)->GetIntArrayElements(env, arr, 0);
for (int i = 0; i < len; ++i)
result[i] = data[i];
(*env)->ReleaseIntArrayElements(env, arr, data, 0);
return result;
}
It is a bit more performant to perform the call directly from a functor, rather than through invokeMethod. The functor can capture the vector:
int onReceiveNativeMounted (JNIEnv * env, jobject obj, jint array_index, jintArray version)
{
auto window = &MainWindow::instance();
auto vector = toQVector(env, version);
QObject sig;
sig.connect(&sig, &QObject::destroyed, window, [=]{
window->onReceiveMounted(array_index, vector.data());
}, Qt::QueuedConnection);
return array_index;
}
Related
While reading Writing R Extensions, Section 5.13 External pointers and weak references we see an example of a finalizer using R_ClearExternalPtr() albeit the comment next to it saying not really needed:
(...) The finalizer code is simply
static void chanFinalizer(SEXP ptr)
{
if(!R_ExternalPtrAddr(ptr)) return;
inRODBCClose(R_ExternalPtrAddr(ptr));
R_ClearExternalPtr(ptr); /* not really needed */
}
In the post Storing C objects in R by #Martin-Morgan he provides two finalizer examples, where he does include a call to R_ClearExternalPtr() in the first case but not in the second:
First example:
static void
_finalizer(SEXP ext)
{
if (NULL == R_ExternalPtrAddr(ext))
return;
Rprintf("finalizing\n");
char *ptr = (char *) R_ExternalPtrAddr(ext);
Free(ptr);
R_ClearExternalPtr(ext);
}
Second example:
static void
_finalizer(SEXP ext)
{
struct Foo *ptr = (struct Foo*) R_ExternalPtrAddr(ext);
Free(ptr);
}
Can someone help clarify the comment not really needed, and whether I should call R_ClearExternalPtr() at the end of the finalizer.
I have a C++ codebase that I'm exposing to R using Rcpp modules. Specifically, I use an interface pattern where the class(es) I expose is actually an abstraction layer on top of the underlying object, which is the implementation.
The class(es) I'm dealing with also interact with each other, and have methods that take as arguments shared pointers to objects. I'm having trouble figuring out the right way to expose these methods to R.
Eg here is some code. The TestClass::combine method takes a pointer to another TestClass object and does stuff with it. When I try to compile this code, I get compiler errors (see below) when I add the corresponding interface method ITestClass::combine to the module.
Implementation:
class TestClass
{
public:
TestClass(int const& n, double const& x)
: n(n), x(x)
{}
const double get_x() {
return x;
}
double combine(std::shared_ptr<TestClass> obj) {
return x + obj->get_x();
}
protected:
int n;
double x;
};
Interface:
//' #export ITestClass
class ITestClass
{
public:
ITestClass(int const& in_n, double const& in_x)
: impl(in_n, in_x)
{}
double get_x() {
return impl.get_x();
}
double combine(ITestClass obj) {
return impl.combine(obj.get_object_ptr());
}
std::shared_ptr<TestClass> get_object_ptr() {
std::shared_ptr<TestClass> ptr(&impl);
return ptr;
}
private:
TestClass impl;
};
RCPP_MODULE(RTestClassModule)
{
class_<ITestClass>("ITestClass")
.constructor<int, double>()
.method("get_x", &ITestClass::get_x, "get_x")
.method("combine", &ITestClass::combine, "combine"); // this line errors out
}
A sample of the errors I get:
In file included from C:/Rlib/Rcpp/include/Rcpp/as.h:25,
from C:/Rlib/Rcpp/include/RcppCommon.h:168,
from C:/Rlib/Rcpp/include/Rcpp.h:27,
from interface1.cpp:2:
C:/Rlib/Rcpp/include/Rcpp/internal/Exporter.h: In instantiation of 'Rcpp::traits::Exporter<T>::Exporter(SEXP) [with T = testpkg::ITestClass; SEXP = SEXPREC*]':
C:/Rlib/Rcpp/include/Rcpp/as.h:87:41: required from 'T Rcpp::internal::as(SEXP, Rcpp::traits::r_type_generic_tag) [with T = testpkg::ITestClass; SEXP = SEXPREC*]'
C:/Rlib/Rcpp/include/Rcpp/as.h:152:31: required from 'T Rcpp::as(SEXP) [with T = testpkg::ITestClass; SEXP = SEXPREC*]'
C:/Rlib/Rcpp/include/Rcpp/InputParameter.h:34:43: required from 'Rcpp::InputParameter<T>::operator T() [with T = testpkg::ITestClass]'
C:/Rlib/Rcpp/include/Rcpp/module/Module_generated_CppMethod.h:111:69: required from 'SEXPREC* Rcpp::CppMethod1<Class, RESULT_TYPE, U0>::operator()(Class*, SEXPREC**) [with Class = testpkg::ITestClass; RESULT_TYPE = double; U0 = testpkg::ITestClass; SEXP = SEXPREC*]'
C:/Rlib/Rcpp/include/Rcpp/module/Module_generated_CppMethod.h:109:10: required from here
C:/Rlib/Rcpp/include/Rcpp/internal/Exporter.h:31:31: error: no matching function for
call to 'testpkg::ITestClass::ITestClass(SEXPREC*&)'
Exporter( SEXP x ) : t(x){}
^
interface1.cpp:17:5: note: candidate: 'testpkg::ITestClass::ITestClass(SEXP, const int&, const double&)'
ITestClass(SEXP in_date, int const& in_n, double const& in_x)
^~~~~~~~~~
interface1.cpp:17:5: note: candidate expects 3 arguments, 1 provided
interface1.cpp:14:7: note: candidate: 'constexpr testpkg::ITestClass::ITestClass(const testpkg::ITestClass&)'
class ITestClass
^~~~~~~~~~
interface1.cpp:14:7: note: no known conversion for argument 1 from 'SEXP' {aka 'SEXPREC*'} to 'const testpkg::ITestClass&'
interface1.cpp:14:7: note: candidate: 'constexpr testpkg::ITestClass::ITestClass(testpkg::ITestClass&&)'
interface1.cpp:14:7: note: no known conversion for argument 1 from 'SEXP' {aka 'SEXPREC*'} to 'testpkg::ITestClass&&'
How do I define ITestClass::combine so that it can be called from R?
I found a better solution, one that has the preferred interface for combine and doesn't seem to run into problems with garbage collection.
A couple of points:
Because the underlying API works extensively with shared pointers, rather than storing a TestClass object in impl, I store a std::shared_ptr<TestClass>. This is referenced directly, rather than creating new shared ptrs from scratch (which crash R when they get destroyed).
I leverage the internal structure of the returned refclass object from an Rcpp module. In particular, it has a .pointer member that is a pointer to the underlying C++ object. So I can dereference that to get the impl member.
New interface:
//' #export ITestClass2
class ITestClass2
{
public:
ITestClass2(int const& in_n, double const& in_x)
: impl(in_n, in_x))
{}
double get_x()
{
return impl->get_x();
}
double combine(Environment obj)
{
SEXP objptr = obj[".pointer"];
ITestClass2* ptr = (ITestClass2*) R_ExternalPtrAddr(objptr);
return impl->combine(ptr->get_object_ptr());
}
// this doesn't need to be seen from R
protected:
std::shared_ptr<TestClass> get_object_ptr()
{
return impl;
}
private:
std::shared_ptr<TestClass> impl;
};
RCPP_MODULE(RTestClassModule2)
{
class_<ITestClass2>("ITestClass2")
.constructor<int, double>()
.method("get_x", &ITestClass2::get_x, "get_x")
.method("combine", &ITestClass2::combine, "combine")
;
}
Call this in R as follows:
obj <- new(ITestClass2, 1, pi)
obj2 <- new(ITestClass2, 2, exp(1))
obj$combine(obj2)
Edit: see better answer here
I found a rather kludgy solution that involves calling the R foreign language API directly:
class ITestClass {
...
double combine(SEXP obj) {
TestClass* ptr = (TestClass*) R_ExternalPtrAddr(obj);
std::shared_ptr<TestClass> sptr(ptr);
return impl.combine(sptr);
}
Rcpp::XPtr<TestClass> get_object() {
return Rcpp::XPtr<TestClass>(&impl);
}
}
obj1 <- new(ITestClass, 1, pi)
obj2 <- new(ITestClass, 2, -0.1)
obj1$combine(obj2$get_object())
# [1] 3.041593
This isn't great for a couple of reasons:
On the R side, I have to pass obj$get_object() as the argument to combine, which is highly non-intuitive
I've seen answers noting that Rcpp::XPtr and std::shared_ptr don't work well together, since they both try to manage the memory pointed to
Hopefully there's a better solution out there.
I want to receive a custom struct of QObject through gpointer on a GstPadProbe callback. I have connected the callback function like this:
struct customData d;
thread = new QThread();
d.worker = new Worker();
d.worker->moveToThread(thread);
d.message = "message\n";
src_pad = gst_element_get_static_pad (osd, "src");
if (!src_pad)
g_print ("Unable to get src pad\n");
else
gst_pad_add_probe (src_pad, GST_PAD_PROBE_TYPE_BUFFER,
src_pad_buffer_probe, NULL, NULL);
g_signal_connect(G_OBJECT(src_pad), "src",
G_CALLBACK(src_pad_buffer_probe), &d);
My callback function is a probe:
static GstPadProbeReturn src_pad_buffer_probe (GstPad * pad, GstPadProbeInfo * info, gpointer u_data)
{
struct customData *custom = (struct customData *) u_data;
g_print("Hello World!\n%s\n", custom->message);
return GST_PAD_PROBE_OK;
}
I have defined the struct as :
struct customData{
char *message;
Worker *worker;
};
I am trying to access the custom->message on src_pad_buffer_probe() thorough custom struct. But the program crashes unexpectedly. I am experimenting this for GTK and QT multi-threaded communication.
Connecting to a signal is just saying "call me later when a certain condition is met". You're passing it a pointer to your structure d you've defined on the stack.
struct customData d;
...
g_signal_connect(G_OBJECT(src_pad), "src", G_CALLBACK(src_pad_buffer_probe), &d);
This means that later on, when the callback is called, d must still exist. This won't be the case if it has been allocated on the stack and you returned from that function. So either declare d as static, or allocate it on the heap through malloc or similar.
I have a c++11 function that returns a:
std::vector<const T*> f();
with T being a c++ class that I exposed to python with class_. All the T instances reside in static storage that will live throught the live of the python process.
I am trying to expose f as a python function
getAllTs()
that would return python objects wrappers around T. I chose T* to be the held type for class_.
I am converting std::vector to a python tuple, with this bad semi-generic function:
template <typename Cont>
struct stdcont_to_python_tuple
{
static PyObject* convert(const Cont& container)
{
boost::python::list lst;
for (const auto& elt: container)
lst.append(elt);
return boost::python::incref( boost::python::tuple(lst).ptr() );
}
static PyTypeObject const* get_pytype()
{
return &PyTuple_Type;
}
};
I couldn't construct the tuple directory from the container. Is that possible?
I need to display these Ts in a UI table, perform sorting, filtering.
The max number of T instances is 30000 odd. In c++11:
sizeof(T) = 24 bytes
In python3:
sys.getsizeof(t) = 72 bytes
What is the return policy I can use where def'ing getAllTs to minimize duplication, ie to have the least extras added by python?
Thanks
Exposing std::vector<const T*> to Python
The easiest way to expose std::vector<...> is to expose it as a boost::python::class_, and use the vector_indexing_suite to provide a Python sequence like interface.
std::vector<const spam*> get_spams() { ... }
BOOST_PYTHON_MODULE(example)
{
namespace python = boost::python;
// Expose `spam`
python::class_<spam, spam*>("Spam");
// Expose `std::vector<const spam*>`
python::class_<std::vector<const spam*>>("Spams")
.def(python::vector_indexing_suite<std::vector<const spam*>>())
;
python::def("get_spams", &get_spams);
}
However, using the type const spam* may not be as fruitful as one would hope. First, Python does not have the concept of const. Furthermore, when exposing the spam class as being held by spam*, the automatic to-python and from-python converter are for spam*, not const spam*. This may not be immediately apparent due to the indexing suite returning proxies during indexing, but it will become apparent when iterating, as the iterator's value will attempt to be converted to Python.
spams = example.get_spams()
# Access by index returns a proxy. It does not perform a
# spam-to-python conversion.
spams[0].perform()
# The iterator's value will be returned, requiring a
# spam-to-python conversion.
for spam in spams:
pass
To resolve this, one can register an explicit to-python conversion for const spam*. I would strongly consider re-examining if const spam* is necessary, or if exposing spam as being held by a different type would be easier (e.g. boost::shared_ptr with a null deleter). Regardless, here is a complete example demonstrating this functionality:
#include <iostream>
#include <boost/python.hpp>
#include <boost/python/suite/indexing/vector_indexing_suite.hpp>
/// Mocks...
struct spam
{
spam() { std::cout << "spam() " << this << std::endl; }
~spam() { std::cout << "~spam() " << this << std::endl; }
void perform() { std::cout << "spam::perform() " << this << std::endl; }
};
namespace {
std::array<spam, 3> spams;
} // namespace
std::vector<const spam*> get_spams()
{
std::vector<const spam*> result;
for (auto& spam: spams)
{
result.push_back(&spam);
}
return result;
}
/// #brief Convert for converting `const spam*` to `Spam`.
struct const_spam_ptr_to_python
{
static PyObject* convert(const spam* ptr)
{
namespace python = boost::python;
python::object object(python::ptr(ptr));
return python::incref(object.ptr());
}
};
BOOST_PYTHON_MODULE(example)
{
namespace python = boost::python;
// Enable `const spam*` to `Spam` converter.
python::to_python_converter<const spam*, const_spam_ptr_to_python>();
// Expose `spam`.
python::class_<spam, spam*>("Spam", python::no_init)
.def("perform", &spam::perform)
;
// Expose `std::vector<const spam*>`.
python::class_<std::vector<const spam*>>("Spams")
.def(python::vector_indexing_suite<std::vector<const spam*>>())
;
python::def("get_spams", &get_spams);
}
Interactive usage:
>>> import example
spam() 0x7ffbec612218
spam() 0x7ffbec612219
spam() 0x7ffbec61221a
>>> spams = example.get_spams()
>>> for index, spam in enumerate(spams):
... spams[index].perform()
... spam.perform()
...
spam::perform() 0x7ffbec612218
spam::perform() 0x7ffbec612218
spam::perform() 0x7ffbec612219
spam::perform() 0x7ffbec612219
spam::perform() 0x7ffbec61221a
spam::perform() 0x7ffbec61221a
~spam() 0x7ffbec61221a
~spam() 0x7ffbec612219
~spam() 0x7ffbec612218
Construct Python tuple from C++ container
A boost::python::tuple can be constructed from a sequence. If a C++ object is provided, then it needs to be convertible to a Python type that implements the Python iterator protocol. For instance, in the above example, as std::vector<const spam*> is exposed and provides a sequence like interface via the vector_indexing_suite, one could write get_spams() as:
boost::python::tuple get_spams()
{
std::vector<const spam*> result;
for (auto& spam: spams)
{
result.push_back(&spam);
}
return boost::python::tuple(result);
}
Alternatively, one can use types and functions provided by the boost/python/iterator.hpp file to create Python iterators from C++ Containers or Iterators. The examples demonstrate exposing a std::pair of iterators, begin and end, to Python. As the std::pair will have a to-python conversion available, one could construct a boost::python::tuple from the std::pair.
Here is a compete example demonstrating this approach:
#include <boost/python.hpp>
/// #brief Returns a tuple, constructing it form a range.
template <typename Container>
boost::python::tuple container_to_tuple(Container& container)
{
namespace python = boost::python;
return python::tuple(std::make_pair(
container.begin(), container.end()));
}
BOOST_PYTHON_MODULE(example)
{
namespace python = boost::python;
// Expose an int range.
typedef std::vector<int>::iterator vector_int_iterator;
typedef std::pair<vector_int_iterator, vector_int_iterator> vector_int_range;
python::class_<vector_int_range>("IntRange")
.def("__iter__", python::range(
&vector_int_range::first, &vector_int_range::second))
;
// Return a tuple of ints.
python::def("get_ints", +[] {
std::vector<int> result;
result.push_back(21);
result.push_back(42);
return container_to_tuple(result);
});
}
Interactive usage:
>>> import example
>>> ints = example.get_ints()
>>> assert(isinstance(ints, tuple))
>>> assert(ints == (21, 42))
Memory Footprint
If the C++ object already exists, one can have the python::object reference it via a pointer, which will reduce duplicating some overall memory usage. However, there are no options to reduce the base footprint of instances for Boost.Python classes, which comes from new-style class, size for variable-length data, C++ object, vtable pointer, pointer to instance holder, and padding for instance holder alignment. If you need a smaller footprint, then consider using the Python/C API directly for type creation, and using Boost.Python for interacting with the objects.
I'm trying to hack with Qt's signals and slots, and I ran into an issue where QMetaType::invokeMethod won't properly pass pointer arguments to the slot being called.
call(QObject *receiver, const char *slot, const QList<QGenericArgument> &args)
{
const QMetaObject *meta = receiver->metaObject();
bool success = meta->invokeMethod(receiver, slot,
args.value(0, QGenericArgument()),
args.value(1, QGenericArgument()),
args.value(2, QGenericArgument()),
...
args.value(9, QGenericArgument()));
}
Then I call it the following way:
MyReceiver *receiver;
MyObject *myObject;
call(receiver, "mySlot", QList<QGenericArgument>() << Q_ARG(MyObject *, myObject));
Where class MyObject : public QObject { ... }. I also do Q_DECLARE_METATYPE(MyObject *) and qRegisterMetaType<MyObject *>("MyObject *")
What happens is that the slot on the receiver is being invoked, but with the value of the argument is always 0 no matter what I pass to the call(...) as Q_ARG
Out of curiosity I looked into the auto-generated MOC file of the receiver, and found that the slots are invoked with the following code:
void MyReceiver::qt_static_metacall(QObject *_o, QMetaObject::Call _c, int _id, void **_a)
{
if (_c == QMetaObject::InvokeMetaMethod) {
Q_ASSERT(staticMetaObject.cast(_o));
MyReceiver *_t = static_cast<MyReceiver *>(_o);
switch (_id) {
case 0: _t->mySlot((*reinterpret_cast< MyObject*(*)>(_a[1]))); break;
default: ;
}
}
}
Turns out that the value of _a[1] bears proper address of MyObject *. But the reinterpret_cast turns it into 0.
Now I have the following questions:
1) How to programmatically invoke a slot and make sure that the pointer arguments are properly passed to the slot?
2) What does this *reinterpret_cast< MyObject*(*)>(_a[1]) mean? What the extra parentheses (*) mean, and how to interpret this piece of code?
Ok, I think I figured why it's not working... Q_ARG only will create a pointer to my pointer and store the former. I didn't mention that the call function was part of the Task call meant to invoke a slot later on - when the values wrapped into Q_ARG are already out of scope. Basically Q_ARG only maintains a weak reference to the argument object.