I'm trying to setup a remote object for a given class.
The class return pointer to internal objects.
class A
{
...
func(){ };
}
class B
{
std::vector<A> AObjects;
...
A *addObject(){}
}
I created the rep file like this
class A
{
SLOT(func())
}
class B
{
SLOT(A* addObject())
}
This causes errors because the generated code tries to serialize A to transfer it to the cient/replica side.
What I want is a RemoteObject of A to get transferred to call func remotely from the client side.
The number of A Object depends on the remote calls of addObject.
Is there a way to get the such a system with qt remote objects?
One way I could imagine is to set the returntype in B to the generated Replica class.
class A
{
SLOT(func())
}
class B
{
SLOT(AReplica addObject())
}
But then the generated code must be available also at server side and the headers need to be in the right inclusion order.
Addition: Replica objects are not serializable either.
Edit:
To get the desired behavior I changed the rep-file
class Object
{
SLOT(print(QString& i_oString));
};
class ObjectGenerator
{
SLOT(QString genObj())
};
The implementation of the server looks like this:
QString Server::genObj()
{
auto obj = new Obj();
auto objRep = QString::number(reinterpret_cast<size_t>(obj)); //pinter as unique string reference
m_oHost.enableRemoting(obj, objRep);
qDebug() << "generated object: " << objRep << endl;
return objRep;
}
The client can now trigger object generations and retrieve the replica
auto obj = reptr->genObj();
obj.waitForFinished();
auto rep = m_node.acquire<ObjectReplica>(obj.returnValue());
QObject::connect(this, SIGNAL(printSignal(QString&)), rep, SLOT(print(QString &)));
In this implementation I need to wrap the client side to get a usable interface.
Is there a more qt-like way of doing this?
Related
I am trying to solve an issue whereby I need to combine third party events with the eventBus send and reply approach that Vertx provides for Standard and Worker Verticle setups. I am not sure if what I have laid out below is necessarily the correct approach.
Problem Statement:
I want to have standard verticle that sends a message to a worker verticle.
The worker verticle does some preprocessing and then uses a client method provided by a third party state management lib to publish an even (in an async manner). The result of which is only whether or not the event was successfully received or not (but does not contain any further info around processing etc).
Further processing takes place when the third party state management lib receives the event(this all happens on a separate thread) and a success or failure can occur at which point another event will be published to the cluster management tools output channel.
From the output channel listener I then want to be able to use the event to somehow use the message.reply() on the worker verticle to send back a response to the standard verticle that made the original request, thereby closing the loop of the entire request lifecycle but also using the async approach that vertx is built to use.
Now I conceptually know how to do 90% of what is described here but the missing piece for me is how to coordinate the event on the output channel listener and connect this to the worker verticle so that I can trigger the message.reply.
I have looked at possibly using SharedData and Clustering that Vertx has but was wondering if there is possibly another approach.
I have put a possible example implementation but would really appreciate if anyone has any insights/thoughts into how this can be accomplished and if I am on the right track.
class Order(val id: String)
class OrderCommand(val order: Order) : Serializable {
companion object {
const val name = "CreateOrderCommand"
}
}
class SuccessOrderEvent(val id: String) : Serializable {
companion object {
const val name = "OrderSuccessfulEvent"
}
}
interface StateManagementLib {
fun <T> send(
value: T,
success: Handler<AsyncResult<Int>>,
failure: Handler<AsyncResult<Exception>>
) {
val output = publish(value)
if (output == 1) {
success.handle(Future.succeededFuture())
} else {
failure.handle(Future.failedFuture("Failed"))
}
}
// non-blocking
fun <T> publish(value: T): Int // returns success/failure only
}
class WorkVerticle constructor(private val lib: StateManagementLib) : AbstractVerticle() {
override fun start(startPromise: Promise<Void>) {
workerHandler()
startPromise.complete()
}
private fun workerHandler() {
val consumer = vertx.eventBus().consumer<OrderCommand>(OrderCommand.name)
consumer.handler { message: Message<OrderCommand> ->
try {
vertx.sharedData().getClusterWideMap<String, Message<OrderCommand>>("OrderRequest") { mapIt ->
if (mapIt.succeeded()) {
lib.send(message.body(), {
// The StateManagementLib successfully propagated the event so, we try and store in this map (id -> Message)
mapIt.result().put(message.body().order.id, message)
}, {
message.fail(400, it.result().message)
})
}
}
} catch (e: Exception) {
message.fail(
HttpResponseStatus.INTERNAL_SERVER_ERROR.code(), "Failed to encode data."
)
}
}
// A consumer that will pick up an event that is received from the clusterTool output channel
vertx.eventBus().addInboundInterceptor { context: DeliveryContext<SuccessOrderEvent> ->
// Retrieve cluster map to get the previously stored message and try to respond with Message.reply
// This should go back to the Standard Verticle that sent the message
vertx.sharedData().getClusterWideMap<String, Message<OrderCommand>>("OrderRequest") {
if (it.succeeded()) {
val id = context.message().body().id
val mapResult = it.result().get(id)
it.result().remove(id)
// Try and reply so the original eventloop thread can pickup and respond to calling client
mapResult.result().reply(id)
}
}
}
}
}
I can set a breakpoint at my server method and it is being called on hub start. And if i put a breakpoint at hub.start(), I do see that the connection already has the client side version of the method bound. But somehow the method is not being called from server. Here is my code:
Server method
[HubName("MovementHub")]
public class MovementHub : Hub
{
public void UpdatePlayerPosServer(PlayerPosition playerPosition)
{
playerPosition.LastUpdatedBy = Context.ConnectionId;
Clients.AllExcept(playerPosition.LastUpdatedBy).updatePlayerPosClient(playerPosition); //debugging here shows the playerposition all filled out nicely. this hub method is HIT.
}
}
Client Method
$(() => {
var connection = (<any>$.connection).MovementHub;
//this method is never called
connection.client.updatePlayerPosClient = (playerPosModel) => {
alert("updatingremotePlayers: " + playerPosModel);
}
});
Hub Start (typescript class. method is called from another class)
public updateServerPos = (x: number, y: number) => {
var connection = (<any>$.connection).MovementHub;
this.LoginID = $("#displayname").val();
$.connection.hub.start().done(() => {
var playerposModel = { Id: this.LoginID, X: x, Y: y };
connection.server.updatePlayerPosServer(playerposModel); //debugging here shows me that "connection" has the client method bound at this point
}).fail(function(error) {
console.log(error);
});
}
I've read a few posts on this that specify you have to have the client method bound before hub start, but IT IS. And the server method is getting called correctly. So not sure what's goin on here.
Edit: I realized, I'm an idiot and might be falling victim to being skipped by the "AllExcept" call on clients. I was the exception! lol
The only remaining problem onw is why I have to have the client Method "instantiated" in an IFFE? I'd like to put it in the same Typescript class where the server method is being called from.
Turns out MIXING javascript IIFE calls with typescript calls can be hazardous. I have a totally unrelated (i thought) hub start happening BEFORE this client method was bound. I realized, even tho, I have two Hubs, there's really only one hub.start(); silly me.
I'm implementing network controller that sends requests to the server with integer command type id and binary serialized block of other command data. Prototype of all commands looks like:
class NetCommand {
public static var typeId; // type must be set in successors!
function new() {
}
public function typeGet():Int {
return Reflect.field(Type.getClass(this), "typeId");
}
}
All this mess in typeGet() function done just for access to the static variables with type ids of all successors. I can't simply write
return typeId;
because statics are not inheritable and this method will return 0 as a value of prototype's variable. Is there any neat solution? Is my solution cross-platform?
Update:
All command classes must be registered in controller class like this:
public function bindResponse(aClass:Class<NetCommand>) {
var typeId = Reflect.field(aClass, "typeId");
mBindResponse.set(typeId, aClass);
}
and then when new command arrives its data passes to the method that find necessary class by command id, creates instance of desired class and passes other data to it:
function onResponse(aTypeId:Int, aData:Dynamic) {
var cmdClass:Class<NetCommand> = mBindResponse.get(aTypeId);
var command:NetCommand = Type.createInstance(cmdClass, []);
command.response(aData); // this must be overriden in successor classes
}
Method typeGet() is used only for targeting outgoing instances and error handling with default behaviour of error command class without creating a heap of classes that differs only by typeId constant. So this method suppreses implementation of the real command id and may be overriden for example.
Why don't you simply make typeId an instance member (not static) ?
Generally, a lot of code does nothing but get/set class members. For that I implemented a simple container class to have getters and setters associated
to a "field". At a first sight this looks pretty ok and results in far less code. This is how the container class looks like:
Member.h
#include <functional>
template <class T>
class Member
{
public:
T data;
using Getter_t = std::function<T(void)>;
using Setter_t = std::function<void(T)>;
using Notify_t = std::function<void(void)>;
Setter_t m_setterFunc;
Getter_t m_getterFunc;
Notify_t m_notifyFunc;
Member()
{
this->m_getterFunc = [=] (void) -> T { return this->data; };
this->m_setterFunc = [=] (T data) -> void { this->data = data; };
this->m_notifyFunc = [] (void) -> void { };
}
auto get() -> T { return this->m_getterFunc(); }
auto set(T data) -> void { this->m_setterFunc(data); this->m_notifyFunc(); }
auto getter(Getter_t func) -> Member& { this->m_getterFunc = func; return *this; }
auto setter(Setter_t func) -> Member& { this->m_setterFunc = func; return *this; }
auto notify(Notify_t func) -> Member& { this->m_notifyFunc = func; return *this; }
~Member() { }
};
I know some things are not perfect yet but that's okay for now. The next few lines show how Member instances are defined and the simple and convenient way to access underlying data. get, set and notify functions can be replaced by lambdas or function pointers to override custom behavior.
main.cpp
#include <iostream>
#include "Member.h"
class MyClass
{
public:
Member<int> foo;
Member<std::string> bar;
void barChanged() { std::cout << "bar changed\n"; }
};
auto main(int argc, const char * argv[]) -> int
{
MyClass instance;
instance.foo.notify([] () -> void { std::cout << "foo changed\n"; });
instance.bar.notify(std::bind(&MyClass::barChanged, instance));
instance.foo.set(10);
instance.bar.set("some string");
std::cout << instance.foo.get() << " " << instance.bar.get() << std::endl;
return 0;
}
The problem now is that the Q_PROPERTY macro expects function names for the READ and WRITE accessors and I'm back at where I started: I have to write get and set functions for each property explicitly. Exactly what I wanted to avoid.
class MyOtherClass : public QObject
{
Q_OBJECT
Q_PROPERTY(bool flag READ getFlag WRITE setFlag NOTIFY flagChanged);
public:
Member<bool> m_flag;
auto getFlag() -> bool { return m_flag.get(); }
auto setFlag(bool flag) -> void { this->m_flag.set(flag); }
};
Is it possible to directly use the already existing m_flag.get and m_flag.set functions? I tried the obvious things but they were either rejected by the moc or resulted in too much code.
Edit
As mentioned below, the MEMBER keyword makes it possible to have properties without specifying get and set functions. However, private members then only can be accessed by their names (this->property("myPropertyName")) and also there's no way to achieve more than "plain" get and set.
To make it more clear: The motivation is not to just avoid writing get and set functions but trying to implement a flexible member system which
by default performs get/set as expected
supports custom logic (for example forward newly set values to some other instance)
can be used for C++ class members and is compatible with Qt properties
And the only missing piece is the bridge between the Q_PROPERTY READ/WRITEaccessors and the get/set methods of the Member class.
Thanks for any help!
I don't think that it's possible to redirect READ or WRITE property methods to some other internal or external object without writing wrappers, but if your getters and setters do not do anything except return or set data: there is MEMBER variable association at least in latest Qt versions.
From Qt Doc:
Q_PROPERTY(type name
(READ getFunction [WRITE setFunction] |
MEMBER memberName [(READ getFunction | WRITE setFunction)])
[RESET resetFunction]
[NOTIFY notifySignal]
[REVISION int]
[DESIGNABLE bool]
[SCRIPTABLE bool]
[STORED bool]
[USER bool]
[CONSTANT]
[FINAL])
A READ accessor function is required if no MEMBER variable was
specified. It is for reading the property value. Ideally, a const
function is used for this purpose, and it must return either the
property's type or a const reference to that type. e.g.,
QWidget::focus is a read-only property with READ function,
QWidget::hasFocus().
A WRITE accessor function is optional. It is for setting the property
value. It must return void and must take exactly one argument, either
of the property's type or a pointer or reference to that type. e.g.,
QWidget::enabled has the WRITE function QWidget::setEnabled().
Read-only properties do not need WRITE functions. e.g., QWidget::focus
has no WRITE function.
A MEMBER variable association is required if no READ accessor function
is specified. This makes the given member variable readable and
writable without the need of creating READ and WRITE accessor
functions. It's still possible to use READ or WRITE accessor functions
in addition to MEMBER variable association (but not both), if you need
to control the variable access.
Using MEMBER you do not need to write getters and setters.
Is there a way to check in the code in a form if the form is being used as a caller for another form that is currently open?
As far as I know, out of the box there is no way to check this. If you have a small set of specific forms (say one caller form and 2 or 3 called forms), it should be pretty easy to manage a list of called forms in the caller form by calling a method in the caller form from the called forms.
If you want a generic solution that works for all forms, things get interesting. You probably want to take a look at the SysSetupForm[Run] and SysForm[Run] classes and try to extend these with a similar called forms management logic. It may be necessary to store the data of the called forms management in the global cache.
That said I suggest you rethink your form design. Usually such a requirement is a result of bad design.
This is just an idea, which I haven't tried but I don't see why it wouldn't work. I didn't optimise the code, so use this idea just as an inspiration.
First, in each form being called from the main form modify the init method as follows:
public void init()
{
super();
blahblah::addHandleToCache(element.hWnd());
}
This is the code of blahblah::addHandleToCache:
public static client void addHandleToCache(int _handle)
{
#define.CACHE_OWNER('MyCallerForm')
container con;
int hWnd;
int i;
//get a container with open window handles from the cache
if (infolog.globalCache().isSet(#CACHE_OWNER, curUserId()))
{
con = infolog.globalCache().get(#CACHE_OWNER, curUserId());
}
//remove handles of closed windows from the container
for (i = conLen(con); i >= 1; i--)
{
hWnd = conPeek(con, i);
if (!WinApi::isWindow(hWnd))
{
conDel(con, i, 1);
}
}
//add the current window handle to the container
con += _handle;
//save the container in the cache
infolog.globalCache().set(#CACHE_OWNER, curUserId(), con);
}
Now when you want to check whether your main form is being used as a caller for other open forms you can use the following check:
public boolean hasOpenChildForms()
{
#define.CACHE_OWNER('MyCallerForm')
container con;
int hWnd;
boolean ret;
//get a container with open window handles from the cache
if (infolog.globalCache().isSet(#CACHE_OWNER, curUserId()))
{
con = infolog.globalCache().get(#CACHE_OWNER, curUserId());
}
for (i = conLen(con); i >= 1; i--)
{
hWnd = conPeek(con, i);
if (WinApi::isWindow(hWnd))
{
//the form is open
ret = true
}
else
{
//remove handles of closed windows from the container
conDel(con, i, 1);
}
}
return ret;
}
Yes, there is.
Your code should look something like this for AX 2009:
Object caller = element.args().caller();
if (SysDictClass::isEqualOrSuperclass(classIdGet(caller), classNum(FormRun)))
{
// ...
}
And something like this for AX 2012:
Object caller = element.args().caller();
if (caller is FormRun)
{
// ...
}
But please note that the caller() will only be set at this point if the form was called through a menu item. If you invoke a method directly, you'll have to manually set the caller() in the Args object.
If you want to call a method on the caller form, I've blogged about this in the past:
http://devexpp.blogspot.com.br/2013/09/calling-methods-on-caller-dynamics-ax.html