Passing unmanaged pointer to unmanaged object in managed class in C++/CLI - pointers

Let's assume the following situation:
class A
{
public:
void MyMethod()
{
a->AnotherMethod(b);
}
private:
MyType* a;
MyAnotherType* b;
};
and
typedef std::vector< int >MyAnotherType;
I want to pass a pointer to std::vector allocated (and filled in) in C++/CLI code to unmanaged C++. If I simply write a->AnotherMethod(b) then the vector is empty in unmanaged code (e.g. 4 elements in C++/CLI and 0 elements after passing to a.
What is the proper way to do that?

Try pragma managed and unmanaged.
Make sure the definition of the class you are passing into the DLL is defined in unmanaged section.

Related

Why won't C++/WinRT IDL file allow me to define a method with a pointer as a parameter?

I am using code in a C# UWP project to generate data in a short array, pass this data to code in a C++/WinRT Component project (using a pointer and the array's length), modify the data, and then pass back a new array (using a pointer and the array's length) to the C# code. Using the code below, I do not immediately get errors. However, building the C++/WinRT project fails, as my idl file gets the error: MIDL2025 [msg]syntax error [context]: expecting an identifier near "*".
Is it possible to pass pointers from C# to C++/WinRT? (If you see any additional issues with my code or its efficiency, feel free to let me know.)
I have scoured the internet for all information regarding C++/WinRT (not C++/CX) and .idl files. I do not think anything more than a few years old will be accurate for C++/WinRT. C++/WinRT was released on June 23, 2015 and anything before then regarding WinRT will really just be C++/CX. I made my code by following the example here: https://github.com/microsoft/Windows-appsample-photo-editor/blob/master/PhotoEditor/Photo.idl. Also, I believe that the idl files in C++/WinRT are actually MIDL 3.0. Looking at this link https://learn.microsoft.com/en-us/uwp/midl-3/intro#types, I changed my idl file to accept Int32 rather than int and Int16* rather than short* (please let me know if that is unnecessary). I have researched a bit about the out keyword, but it did not seem to work with MIDL 3.0. To be clear I want to get back short* as in the pointer, not short as in the value.
Here is my idl file:
//.idl file
namespace MyNamespace
{
[default_interface]
runtimeclass MyClass
{
MyClass();
void DoStuff(Int32 length, Int16* inbuf, Int16* outbuf);
}
}
Here is my header file:
//.h file
#pragma once
#include "MyClass.g.h"
namespace winrt::MyProject::implementation
{
struct MyClass: MyClassT<MyClass>
{
MyClass() = default;
void DoStuff(int length, short* inbuf, short* outbuf);
};
}
namespace winrt::MyProject::factory_implementation
{
struct MyClass: MyClassT<MyClass, implementation::MyClass>
{
};
}
Here is my C++ file:
//.cpp file
#include "pch.h"
#include "MyClass.h"
#include "MyClass.g.cpp"
namespace winrt::MyProject::implementation
{
void MyClass::DoStuff(int length, short* inbuf, short* outbuf)
{
//Process data here by editing outbuf
}
}
Thank you in advance!

How to expose global strings and ints to C++, QML, and JS

Our team is developing a Qt application which makes use of C++, QML, and JS. What is the best way to expose non-language specific strings, representing filenames and ints representing error codes, so that all languages can easily use them?
The most efficient and clean solution would be to implement those strings and ints as properties of a QObject, then create an instance of that object in main.cpp and register it as a singleton.
// before main()
YourType data;
static QObject * getData(QQmlEngine * e, QJSEngine *) {
e->setObjectOwnership(&data, QQmlEngine::CppOwnership); // just in case
return &data;
}
// in main()
qmlRegisterSingletonType<YourType>("Core", 1, 0, "Data", getData);
QQmlApplicationEngine engine;
//...
And now you have a global data for C++, and a global Data for QML. In the first case you will need to declare data as an extern in sources which need to access it, in the second case you will need to import Core 1.0 in order to access Data and its properties.

Passing native shared_ptr across managed assembly

We are in the process of converting C# code to C++, but we need to do so in phases. I am at a point now where I need to instantiate several native objects from within managed code. These native objects I cannot change, and their declaration looks like this:
public class NativeA();
public class NativeB(std::shared_ptr<NativeA> obj);
Both NativeA and NativeB need to be instantiated from managed code as:
void main() {
ManagedA ObjectA = gcnew ManagedA();
ManagedB ObjectB = gcnew ManagedB(ObjectA);
}
The problem comes in with getting the shared_ptr of NativeA in the constructor of NativeB. Niether NativeA nor NativeB will be manipulated in managed code, they just need to be instantiated. Ideally, something like this:
public ref class ManagedA {
public:
ManagedA() { _object = new NativeA(); }
~ManagedA() { delete _object; }
NativeA * Get() { return _object; }
private:
NativeA *_object;
};
public ref class ManagedB {
public:
ManagedB(ManagedA^ objectA ) {
_object = new NativeB(std::make_shared<NativeA>(*objectA->Get());
}
~ManagedB() { delete _object; }
private:
NativeB *_object;
};
But, this is not allowed in c++/cli because native types are declared as private. Defining #pragma make_public(NativeA) does not solve this either.
My intent is not to work with the native objects in managed code, they just need to be instantiated, so I really don't care about trying to marshal the native pointers and deal with .NET GC if I don't have to, and I don't want to perform a copy. I just want to wrap the classes in order to pass them around.
Is there a clean and simple way to do this?
It appears that the answer was not due to a syntax or usage problem. The two managed objects were in different DLLs and could not be passed across them via .NET. Once the code was compiled in the same project, the issue was resolved.
Although the error message indicated the problem was an accessibility issue in VS 2015, and because it reported it during the link phase, I suspect the cause was because the linker would not have known about the implementation of the NativeA in NativeB without declaring an extern. Being wrapped in CLR, it surfaced as a different issue.

Do Qt properties assume objects are pointers and not members?

Let's say I have a C++ object with a member object that I expose to QML:
class X : public QObject
{
Q_OBJECT
};
class Y : public QObject
{
Q_OBJECT
Q_PROPERTY(X* x READ getX CONSTANT)
public:
X* getX(void) { return &x; }
X x;
};
Most of the time this works, but sometimes it will cause crashes. The call stack is quite lengthy but goes like this:
QScopedPointer<QObjectData, QScopedPointerDeleter<QObjectData> >::data
qGetPtrHelper<QScopedPointer<QObjectData>>
QObject::d_func
QObjectPrivate::get
QQmlNotifierEndpoint::disconnect
QQmlNotifierEndpoint::~QQmlNotifierEndpoint
QQmlJavaScriptExpressionGuard::~QQmlJavaScriptExpressionGuard
QRecyclePool<QQmlJavaScriptExpressionGuard, 1024>::Delete
QQmlJavaScriptExpressionGuard::Delete
QQmlJavaScriptExpression::GuardCapture::captureProperty
QQmlEnginePrivate::captureProperty
QV4::QObjectWrapper::getProperty
etc.
If instead I set X to be a pointer:
class Y : public QObject
{
Q_OBJECT
Q_PROPERTY(X* x READ getX CONSTANT)
public:
Y()
{ x = new X; }
X* getX(void) { return x; }
X* x;
};
The crashes go away.
Is this a known restriction of Q_PROPERTY, that if you return a pointer to a QObject, that object is assumed to be a pointer (not a member) and things like deleteLater() might be called?
This crash most likely happens because QML takes ownership of the object c your function returns, and want to delete it at some point late.
In your first example, since x is not allocated from the free store, trying to delete it will crash.
In the second example, the QML engine still tries to delete it, but it does so without problem.
The question to ask is why does the QML engine takes ownership of the object ?
The documentation states in Data Type Conversion Between QML and C++ | Data Ownership :
When data is transferred from C++ to QML, the ownership of the data always remains with C++. The exception to this rule is when a QObject is returned from an explicit C++ method call: in this case, the QML engine assumes ownership of the object, unless the ownership of the object has explicitly been set to remain with C++ by invoking QQmlEngine::setObjectOwnership() with QQmlEngine::CppOwnership specified.
Additionally, the QML engine respects the normal QObject parent ownership semantics of Qt C++ objects, and will not ever take ownership of a QObject instance which already has a parent.
So, in order to remedy to your problem, you could either affect a QObject parent to your x object, or explicitely declaring it having QmlEngine::CppOwnership.

Qt: Q_PROPERTY with pointer and forward declaration for QtScript access

Problem
I am making a project using Q_OBJECT and Q_PROPERTY to access some objects from scripts. I have two problems:
making classes that use forward declarations scriptable
returning a property as pointer
Explanations
1. Why forward declaration?
The class B gets the forward declaration to A, because A needs the complete B type in the header due to the templates. B needs only an incomplete type (A*) in the header, thus the forward declaration is valid.
2. Why returning a pointer?
We cannot return a copy, as we need access to the actual object in the script. We cannot return a reference, as Qt does not allow slots to return references - their type would be ignored, they would only return void*.
Code
Complete code download on pastebin or as ZIP archive or as ZIP archive of minimal example is available, for testing / playing: I needed to split up the files for the forward declaration and for MOC. I added a Makefile to test it. Make deps: g++, moc, Qt.
Important parts
class A; // forward declaration necessary, see explanation above
class B : public QObject {
Q_OBJECT
Q_PROPERTY(A a READ GetA) // <-- ERROR HERE
// ...
public slots:
A* GetA() {
return mA;
}
private:
A* mA;
// ...
}
The error line in the script:
print(bObj.GetA().GetName());
Compile error
This error disappears when I comment out the Q_PROPERTY above.
tmp/B.moc.hpp:95:51: error: invalid use of incomplete type ‘struct A’
tmp/../B.hpp:10:7: error: forward declaration of ‘struct A’
Script exception
When leaving out the Q_PROPERTY and calling the GetA() method as a slot from the script, I get the following exception:
Line 7: "TypeError: cannot call GetA(): unknown return type `A*'
(register the type with qScriptRegisterMetaType())"
When registering A* with qRegisterMetaType<A*>("A*"); this changes to:
Line 7: "TypeError: Result of expression 'bObj.GetA().GetName'
[undefined] is not a function."
That shows that GetA() does not return the A object, or somehow it returns a pointer, but the script cannot dereference it. GetA() then actually returns a QVariant(A*), can this be used somehow?
Questions:
Can I somehow make a Q_PROPERTY from an incomplete type, or how could I avoid the forward declaration?
Can I return a reference in a slot (maybe some tricks, e.g. a class that wraps the pointer and "overrides" the script operator., if something similar exists) or
Can I somehow dereference a QVariant(A*) to A in QtScript?
Your property type is A, not A*, that's why you get very reasonable error.
You should use QScriptValue. Look this code. It works Ok:
class A; // forward declaration necessary, see explanation above
class B : public QObject
{
Q_OBJECT
// Using QScriptValue, made from A instead of A to allow script work correctly with an object
Q_PROPERTY(QScriptValue a READ GetA)
public slots:
QScriptValue GetA() {
//making QScriptValue from A. Type conversion in C style only due to limitation of incomplete type
//In real app it's beter to put defenition of this slot after A's defenition
return static_cast<QScriptEngine*>(parent())->newQObject((QObject*)mA);
}
private:
A* mA;
// ...
public:
//I decided my object will be a child of scriptEngine, but you can take a pointer to it in some other way
B(QScriptEngine * parent);
};
class A: public QObject
{
Q_OBJECT
public slots:
QString GetName() const {return "a name";}
public:
A(QScriptEngine*parent):QObject(parent){}
};
B::B(QScriptEngine *parent):QObject(parent), mA(new A(parent)){}

Resources