Archived

This forum has been archived. Please start a new discussion on GitHub.

Redirecting parts of output stream to other function

Nice day to you,

I have slice declaration like this:
interface BoxModule {
	nonmutating PropertyValue getProperty(string propertyName)
		throws NameNotExistError;
	...
};

interface SettingModule {
	PropertyValue run(
		PropertyValue valueBefore,
		BoxModule* boxModuleParam,
		StringSeq localePrefs,
		Ferda::ModulesManager::ManagersEngine* manager,
		out string about)
			throws BoxRuntimeError;
	...
};


BoxModule is implemented by other application than SettingModule and there is
third application with name Manager which has to call getProperty from
Boxmodule and than execute run of SettingModule with PropertyValue returned
from SettingModule. PropertyValue is some class, but getProperty returns some
child of that class which is not know for Manager at his compilation time.

So I thougth that Manager have to use dynamic invocation to redirect whole
class information from BoxModule to SettingModule. My problem is, that I don't
know how to get bytes of class PropertyValue from bytes returned by ice_invoke
on getProperty and get that bytes to stream for invoking run.

I use Ice for C#.

Can you help me please,
thanks,
Michal

Comments

  • matthew
    matthew NL, Canada
    I want to ensure that I understand what you are trying to do.

    You have this manager process that needs to pass on instances of PropertyValue of which it has no knowledge. In addition to the instance of the class you need to add further information (ie: new parameters to SettingModule::run method).

    In order to call SettingModule::run directly you need to extract the class instances which you cannot do. In order to use ice_invoke() you need to build a new parameter list, which you don't know how to do.

    Is this correct?

    Regards, Matthew
  • Yes.

    Exactly - for ice_invoke() I know how to add "further information" - but I don't know how to get bytes reprezenting instance of PropertyValue and pass them by ice_invoke().

    Can you help me with that?

    Thank you
  • If your manager has no knowledge of the actual derived type that is returned by getProperty, then it cannot pass on that type. The dynamic invocation facilities don't help you here: there is no way to extract a class instance of unknown type from a stream without also slicing it to the most-derived type that is known to the receiver.

    You must either make the derived type known to your manager, or accept that the instance will be sliced to PropertyValue.

    Cheers,

    Michi.
  • thank you for reply,

    but I don't understand, how it is possible, that it is not possible.

    For example if you have function
    PropertyValue test(out int a);
    

    how you know, where beggins bytes with return value of a?

    if I understand, how it works, I need the same: I know, where beggins bytes with PropertyValue instance and I need to know how many bytes represents this PropertyValue and that bytes will I pass to other stream.

    Where am I wrong?

    Thanks,
    Michal
  • kovacm wrote:
    but I don't understand, how it is possible, that it is not possible.

    For example if you have function
    PropertyValue test(out int a);
    

    how you know, where beggins bytes with return value of a?

    if I understand, how it works, I need the same: I know, where beggins bytes with PropertyValue instance and I need to know how many bytes represents this PropertyValue and that bytes will I pass to other stream.

    It does not work that way, at least not in general. A class is marshaled as a series of slices, each corresponding to one level of the derivation hierarchy. The receiving end knows of course where the bytes of a class instance start and end, but that byte stream does not stand alone. In particular, if a class contains pointers to other instances, the byte stream that describes the class instance can contain indexes that point at other parts of the same request, but outside the bytes that make up the instance.

    In other words, the marshaled representation of a class instance can depend on other parts of the byte stream for the enclosing request or reply. If you were to "lift out" just the section of the byte stream that represents the instance and insert it into a different byte stream, it no longer makes sense if the instance contains class pointers (see section 32.2.11 of the manual).

    Your operation returns a class by value. By definition, that means that any unknown part of the instance will be sliced off. If you want to not lose the derived part of the instance, you cannot pass it by value but must pass it by reference instead. To do that, your manager would have to return a proxy to the instance (instead of the instance itself) and pass that proxy to the eventual receiver. That receiver can then use the proxy to retrieve the class instance in total (assuming that the receiver has knowledge of the derived type of the class).

    Alternatively, you can make the most-derived type of the class known to the manager, in which case the instance will not be sliced.

    Cheers,

    Michi.
  • Just another follow-up on this issue... Basically, extracting a class instance without full type knowledge is not possible. You can only extract those parts of a class for which you have type knowledge, and the derived parts are sliced. This is simply a consequence of the pointer semantics of classes and the way they are marshaled.

    So, here are the options as I see them:
    1. Use classes to model unions, with the base class holding a discriminator and each derived class implementing one member of the union. If you choose this option, you must have full type knowledge of all the possible derived types in the forwarding process (your manager application). This would be my preferred approach. After all, if you use classes to model unions, you are exchanging data by value, meaning that the type of the value must be known in all processes that receive or send the value.
    2. Forward the entire request using ice_invoke(). If the receiver has knowledge of the derived types, it can then extract the parameters it is intersted in using the streaming interfaces. While this would work, I don't like it all that much--it's certainly not very elegant.
    3. Use a struct to model your union. The downside of this is that you no longer have a union (that is, all members will be marshaled, not just the active member), so this is wasteful in bandwidth (and would obscure the intent at the type level). Moreover, this approach also requires the intermediate process to have full type knowledge, meaning that you might as well go with option 1.
    4. Pass proxies to the class instances through the intermediate and have the ultimate receiver retrieve the class data via the proxy. The downside here is that you must be able to make a direct invocation from the ultimate receiver to the intial provider, which may be undesirable.

    I'm still a little puzzled by the design though. In particular, with your design, the manager who acts as an intermediary wants to receive and send a value without having knowledge of the type of that value. This strikes me as unusual--normally, intermediaries who forward values have full type knowledge or, if they don't have full type knowledge, they forward proxies (that is, use reference semantics instead of value semantics).

    I don't know enough about your particular application to know how you could best change it, so I can give only general advice. But the best way to solve your problem may well be to either give the manager full type knowledge, or to change the design such that the manager forwards proxies instead of values, in which case it won't need full type knowledge.

    Hope this helps,

    Michi.
  • Thank you very much for big replies.

    In my application it is a little bit more complicated. I have not only to resend in Manager PropertyValue from BoxModule to SettingModule but also to serialize this PropertyValue.

    For better understanding our application:

    In our application user is connecting boxes (box modules) together by nice graphical frontend (FrontEnd use Manager for that). Every box has properties and actions (and other things...). User sets properties in property grid. If it is basic property (string, int...) it normally use property grid, if it has complicated structure, it starts dialog for setting this property (Setting module). Manager also need to save all properties and connections between boxes for next user session.

    Some (I hope many) boxes and setting modules will be done by other programmers possibly in other programming language than our Manager. I would like to make it for them not so much complicated to make new boxes and setting modules.

    So I see in this time these possibilities:
    1. Add to BoxModule function for serializing and deserializing of properties of "not basic type" and pass PropertyValue by reference to setting module or pass reference of whole boxModule with name of property for setting
    2. For properties of "not basic type" have only one type of propertyValue, which have inside ByteSeq - serialized value. Bad on this approach is that SettingModule have to have knowledge of how it was serialized
    3. My preffered way was to make serialization by Manager but now I think it is not possible - am I true?

    Do you see better way how to manage this?

    Thank you!
    Michal
  • matthew
    matthew NL, Canada
    If you really want to keep your architecture intact you can do more or less what, as Michi pointed out. However, it IS very ugly.

    What you have to do is send the full serialized byte-stream to the receiver that understands the full object heirarchy and deserialize the object there. This means something like this:
    interface SettingModule {
    	PropertyValue run(
    		PropertyValue valueBefore,
    		BoxModule* boxModuleParam,
    		StringSeq localePrefs,
    		Ferda::ModulesManager::ManagersEngine* manager,
    		out string about)
    			throws BoxRuntimeError;
    	PropertyValue runRaw(
    		Ice::ByteSeq valueBefore,
    		BoxModule* boxModuleParam,
    		StringSeq localePrefs,
    		Ferda::ModulesManager::ManagersEngine* manager,
    		out string about)
    			throws BoxRuntimeError;
    
    	...
    };
    

    The valueBefore byte-sequence is the parameter data passed to ice_invoke in the Manager. You can extract the object from the byte-sequence using the InputStream class.

    You could make it somewhat more palatible by having some other class within the SettingModule server that the manager calls upon, and then this forwards the data to the SettingModule::run method.

    I hope this makes sense.

    Regards, Matthew