Cannot put QAbstractListModel object as one class's property - qt

My code looks like so:
class Wrapper: public QObject {
Q_OBJECT
Q_PROPERTY(PeopleListModel & list READ list)
public:
PeopleListModel & list() {
return list_;
}
private:
PeopleListModel list_;
};
And PeopleListModel is a subclass of QAbstractListModel.
But I got compilation error:
^
In file included from plugin.cpp:131:0:
plugin.moc: In member function ‘virtual int Wrapper::qt_metacall(QMetaObject::Call, int, void**)’:
plugin.moc:184:52: error: cannot declare pointer to ‘class PeopleListModel&’
case 0: *reinterpret_cast< PeopleListModel&*>(_v) = list(); break;
^
In file included from ../../../../Qt5.2.0/5.2.0/gcc_64/include/QtCore/qnamespace.h:45:0,
from ../../../../Qt5.2.0/5.2.0/gcc_64/include/QtCore/qobjectdefs.h:45,
from ../../../../Qt5.2.0/5.2.0/gcc_64/include/QtCore/qobject.h:48,
from ../../../../Qt5.2.0/5.2.0/gcc_64/include/QtCore/qplugin.h:45,
from ../../../../Qt5.2.0/5.2.0/gcc_64/include/QtQml/qqmlextensionplugin.h:45,
from ../../../../Qt5.2.0/5.2.0/gcc_64/include/QtQml/QQmlExtensionPlugin:1,
from plugin.cpp:1:
../../../../Qt5.2.0/5.2.0/gcc_64/include/QtCore/qabstractitemmodel.h: In member function ‘PeopleListModel& PeopleListModel::operator=(const PeopleListModel&)’:
../../../../Qt5.2.0/5.2.0/gcc_64/include/QtCore/qglobal.h:967:12: error: ‘QAbstractListModel& QAbstractListModel::operator=(const QAbstractListModel&)’ is private
Class &operator=(const Class &) Q_DECL_EQ_DELETE;
^
../../../../Qt5.2.0/5.2.0/gcc_64/include/QtCore/qabstractitemmodel.h:479:5: note: in expansion of macro ‘Q_DISABLE_COPY’
Q_DISABLE_COPY(QAbstractListModel)
^
plugin.cpp:32:7: error: within this context
class PeopleListModel : public QAbstractListModel {
^
In file included from plugin.cpp:131:0:
plugin.moc: In member function ‘virtual int Wrapper::qt_metacall(QMetaObject::Call, int, void**)’:
plugin.moc:184:59: note: synthesized method ‘PeopleListModel& PeopleListModel::operator=(const PeopleListModel&)’ first required here
case 0: *reinterpret_cast< PeopleListModel&*>(_v) = list(); break;
^
make: *** [plugin.o] Error 1

Related

What is the proper usage of Q_DECLARE_SMART_POINTER_METATYPE?

I tried to define a function that returns std::shared_ptr<OrderModel> as follows:
Q_DECLARE_SMART_POINTER_METATYPE(std::shared_ptr)
namespace tradeclient {
class OrderModel : public QObject
{
Q_OBJECT
public:
Q_PROPERTY(QString marketId READ marketId CONSTANT)
Q_PROPERTY(quint64 id READ id CONSTANT)
...
};
using OrderPtr = std::shared_ptr<OrderModel>;
class MarketModel : public QObject
{
Q_OBJECT
public:
Q_INVOKABLE tradeclient::OrderPtr createLimitOrder()
{
return m_orders.front();
}
private:
//In my app I initialize OrderModel-s and add them so some container.
std::vector<OrderPtr> m_orders;
};
} //namespace tradeclient
Q_DECLARE_METATYPE(tradeclient::OrderPtr)
qRegisterMetaType<tradeclient::OrderPtr>();
and then call createLimitOrder() from QML:
var order = market.createLimitOrder()
console.log("Limit order type: %1, value: %2, id: %3".arg(typeof(order)).arg(JSON.stringify(order)).arg(order.id))
but with no success, because I can't access order.id and the console output is:
Limit order type: object, value: "", id: undefined
I also tried to define functions like this:
Q_INVOKABLE QVariant createLimitOrder()
{
return QVariant::fromValue(m_orders.front());
}
Q_INVOKABLE QSharedPointer<tradeclient::OrderModel> createLimitOrder()
{
return QSharedPointer<tradeclient::OrderModel>(new OrderModel());
}
but nothing changed.
But at least, QML treats order as a non-empty value and the following QML code:
if (order)
console.log("The order is defined.");
else
console.log("The order is not defined.");
prints:
"The order is defined."
So order is defined, but its properties are not. What did I miss?
Buy the way, the above QML code works correctly if I return naked pointer:
Q_INVOKABLE tradeclient::OrderModel* createLimitOrder()
{
return m_orders.front().get();
}
it prints
Limit order type: object, value: {"objectName":"","marketId":"BTCUSDT","id":0,"side":1,"type":0,"status":0,"price":{"precision":2},"stopPrice":{".....
Does QVariant that wraps std::shared_ptr expose object properties to QML?
EDIT1
As far as I understand, Q_DECLARE_SMART_POINTER_METATYPE is a relatively simple thing, it requires the class template to have operator -> (see ./qtbase/tests/auto/corelib/kernel/qvariant/tst_qvariant.cpp):
namespace MyNS {
template<typename T>
class SmartPointer
{
T* pointer;
public:
typedef T element_type;
explicit SmartPointer(T* t = nullptr)
: pointer(t)
{
}
T* operator->() const { return pointer; }
};
}
Q_DECLARE_SMART_POINTER_METATYPE(MyNS::SmartPointer)
so it is reasonable to expect that it behaves like a naked pointer in QML. However, I have concerns that this is one of those cases where they simply did not implement this simple thing well enough.

How to pass a class object from an Rcpp module back into C++?

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.

What is the alternative to std::atexit in C++

I wanted to remove file when the program is closed, but not ended. I tried to do it with function std::atexit, but its parameter can't be pointer to a function if that's class member function. So I was wondering is there any simple alternative?
class User
{
std::experimental::filesystem::path file_path;
std::experimental::filesystem::path & get_file_path();
void clean_file_path();
void (User::*x)();
}
int main()
{
std::experimental::filesystem::path p = user.get_file_path();
user.x = & User::clean_file_path;
std::ofstream output(p, std::ios::binary | std::ios::trunc);
std::atexit(user.x);
}
You could have a static function inside your class and you could call "atexit" with a pointer to this static function:
class User
{
public:
void clean();
static void cleanStatic();
};
int main()
{
void (User:: * pClean)() = &User::clean;
void (*pCleanStatic)() = &User::cleanStatic;
std::atexit(pCleanStatic);
}
You can register multiple functions with "atexit" but there is a limit to that number so maybe registering only one function that will handle the work for all the objects is a better solution.
One more thing: the type of pCleanStatic and the type of pClean are not the same:
pCleanStatic is void(*)()
pClean is void(User::*)()
"atexit" expects a parameter of type void(*)(). So only pCleanStatic will be accepted as a parameter. In Visual Studio, if you try to compile the code
std::atexit(pClean);
you'll get the following error:
error C2664: 'int atexit(void (__cdecl *)(void))': cannot convert argument 1 from 'void (__thiscall User::* )(void)' to 'void (__cdecl *)(void)'
mentioning the two different types.

QList Inheritance leads to class not recognized as a type by foreach

With the following code
class Section : public QList<Property>
{
public:
explicit Section();
explicit Section(QString name);
Property getPropertyNamed(QString name);
QString getName();
private:
QString *m_name;
};
Property Section::getPropertyNamed(QString name)
{
Property toReturn;
foreach (Property loopOn, this)
{
if(loopOn.getName() == name)
toReturn = loopOn;
}
return toReturn;
}
The compiler throws the following error. I did dig in the source generating the error, but it's frankly way above my level of understanding but it DID seem to be a template of the foreach instruction.
error: 'Section* const' is not a class, struct, or union type
typename T::const_iterator i, e;
So basically,
- What is wrong exactly in there?
- How can I fix it?!
[Title is only a wild guess of what is actually happening here, will edit if necessary]
The problem is that this is a pointer to QList<Property>, but you have to use the class instance itself, i.e write:
foreach (Property loopOn, *this) {
[..]
}

c++ managed class constructor can not have parameters?

please help me out , why my code cannot compile,
the compiler complains that:
error C2629: 意外的“StringToAnsi (”
error C2334: “{”的前面有意外标记;跳过明显的函数体
error C2629: 意外的“StringToAnsi (”
...
Here is my code:
#using <System.dll>
#using <mscorlib.dll>
class StringToAnsi
{
private:
void * m_ptr;
public:
StringToAnsi( System::Object ^ str)
{
m_ptr = System::Runtime::InteropServices::Marshal::StringToHGlobalAnsi(safe_cast<System::String^>(str)).ToPointer();
}
StringToAnsi(System::String ^ str)
{
m_ptr = System::Runtime::InteropServices::Marshal::StringToHGlobalAnsi(str).ToPointer();
}
~StringToAnsi()
{
System::Runtime::InteropServices::Marshal::FreeHGlobal(System::IntPtr(m_ptr));
}
operator const ACHAR*()
{
return (const ACHAR*)m_ptr;
}
Because you have two constructors with the same number of parameters. There is an Object and a String, but both are an Object. So this seems very ambiguous.
When you create two methods (or constructors), you can't let them have the same number of parameters, because the compiler doesn't know which one to call.
When you put in a string into the construction like so: new StringToAnsi("bla"). The compiler doesn't know which constructor to use.

Resources