Re: [boost] Re: [Threads] Simple active object wrapper, take 2

At 01:02 28/02/2004, you wrote:
scott <scottw <at> qbik.com> writes:
ps: the sample code from mark blewett looks very promising?
Yes. Although from my point of view, it has the same drawback with switching on messages that you have. I wonder if the queue of messages in the servant class could be a queue of fully-bound function calls,
I'm halfway there, in that the queue holds callbacks, eg in Servant::dispatch the "do something with m" simply executes the callback. To give a usage example class MyServant : public Servant { public: MyServant(Scheduler* scheduler) : Servant(scheduler) {} void do_fn(int x, int y) { std::cout << x << " " << y << std::endl; } }; int main() { Scheduler scheduler; scheduler.start(1,1); MyServant my_servant(&scheduler); my_servant.post_call(new CallbackVAA<MyServant,int,int>(&my_servant, &MyServant::do_fn, 1, 2)); scheduler.stop(); return 0; } will result in the output "1 2" via one of the schedulers worker threads
generated by proxy objects which describe interfaces? (Yes, I am quite enamoured of that idea :))
Interesting idea... assuming I'm understanding correctly.. in that the callback is created by an object in the servant class, the above becomes; class MyServant : public Servant { public: MyServant(Scheduler* scheduler) : Servant(scheduler), fn(this, &MyServant::do_fn) {} MethodProxy<MyServant, int, int> fn; private: void do_fn(int x, int y) { std::cout << x << " " << y << std::endl; } }; int main() { Scheduler scheduler; scheduler.start(1,1); MyServant my_servant(&scheduler); my_servant.fn(1, 2); // certainly a lot easier to understand than previous example! scheduler.stop(); return 0; } Regards Mark --- Outgoing mail is certified Virus Free. Checked by AVG anti-virus system (http://www.grisoft.com). Version: 6.0.573 / Virus Database: 363 - Release Date: 28/01/2004

Hi Mark, You have introduced a third perspective - looks promising. Hope I can keep track of all the differences and still contribute in some useful manner.
[mailto:boost-bounces@lists.boost.org]On Behalf Of Mark Blewett
<snip>
class MyServant : public Servant { public: MyServant(Scheduler* scheduler) : Servant(scheduler) {} void do_fn(int x, int y) { std::cout << x << " " << y << std::endl; } };
class MyServant : public Servant { public: MyServant(Scheduler* scheduler) : Servant(scheduler), fn(this, &MyServant::do_fn) {} MethodProxy<MyServant, int, int> fn; private: void do_fn(int x, int y) { std::cout << x << " " << y << std::endl; } };
<snip> I understand the direction that both Matthew and yourself are heading. The resulting interface for clients of your class, is concise. Fully bound functions are a nice packaging and there are plenty of precendents. There is one design constraint that this direction would impose on clients, that rules it out as a solution for me. This doesnt necessarily interest you but just in case, I will try to paint the picture as briefly as I can. If active objects (i.e. classes deriving from Servant) communicate by calling methods defined in each other, then they have compile-time knowledge of each other. Reasonable assumption I suppose. However, it follows that every client of a particular object, i.e. an active object that calls a member of another active object, is itself required to provide a specific interface. That being the interface that the second object uses to respond. Returning to the db server example; every client must derive from a common class that defines all the members that the server might call. Perhaps like this; class DbServant : public Servant { public: DbServant(Scheduler* scheduler) : Servant(scheduler), fn(this, &DbServant::do_fn) {} MethodProxy<DbServant, int, int> fn; private: void do_fn(int x, int y) { std::cout << x << " " << y << std::endl; } }; class MyReadOnlyViewer : public DbServant { // Run a status display repeated around // the factory }; class MyViewerAndController : public DbServant { // Run a window with controls // for modifying the data model }; This (hopefully) seems like a reasonable circumstance. But as coded the db_server would be calling methods defined in DbServant when what you really want is different implementations for MyReadOnlyViewer and MyViewerAndController. I assume that with some twiddling your technique could accomodate these kind of requirements (given that you are still reading and still interested). Would the db_server also have a separate "db_interface" class? Also, if this kind of interface class technique has any legs is it also (potentially) dragging lots of implementation details along with it? Can that be avoided? Should it be? Cheers, Scott

Mark Blewett <boost <at> blewett.nildram.co.uk> writes:
generated by proxy objects which describe interfaces? (Yes, I am quite enamoured of that idea :))
Interesting idea... assuming I'm understanding correctly.. in that the callback is created by an object in the servant class, the above becomes;
class MyServant : public Servant { public: MyServant(Scheduler* scheduler) : Servant(scheduler), fn(this, &MyServant::do_fn) {} MethodProxy<MyServant, int, int> fn; private: void do_fn(int x, int y) { std::cout << x << " " << y << std::endl; } };
int main() { Scheduler scheduler; scheduler.start(1,1);
MyServant my_servant(&scheduler); my_servant.fn(1, 2); // certainly a lot easier to understand than previous example!
scheduler.stop(); return 0; }
Yes, that's what I was referring to. Also, I think it should be possible to do the same for values returned from invoking the methods of another object: class SomeClass { public: std::string value(int id) { return "foo"; } } class SomeServant : public Servant, private SomeClass { public: SomeServant() : getValue(&SomeServant::value, this) {} MethodProxy<std::string (int)> getValue; }; class OtherServant : public Servant { public: void findAndStoreValue(SomeServant& other, int id) { ReturnProxy<void (std::string)>(&storeValue) = other.getValue(int); } private: void storeValue(std::string value) { ... } } (Although that would require some nasty overloading of the meaning of operator= in the proxy...) So, the return address of the cross-thread method call can also be encoded, and the result result delivered by the same mechanism (which is one of the properties scott was keen on). Can the template parameter of ReturnProxy be discovered by the compiler? Anyway, I think some reasonably clean syntax can be found to bind both the arguments and the return address into the function call, provided that the return address is a method in the invoking object. Matt

Matthew Vogt <mvogt <at> juptech.com> writes:
Anyway, I think some reasonably clean syntax can be found to bind both the arguments and the return address into the function call, provided that the return address is a method in the invoking object.
Well, after some fiddling, the best I can manage is: class SomeClass { public: std::string value(int id) { return "foo"; } } class ThatServant : public reactive<ThatServant>, private SomeClass { public: ThatServant() : getValue(&ThatServant::value, this) {} method<std::string (int)> getValue; }; class ThisServant : public reactive<ThisServant> { public: void findAndStoreValue(ThisServant& other, int id) { (return_to(&ThisServant::storeValue, this)) = other.getValue(int); } private: void storeValue(std::string value) { ... } } Matt
participants (3)
-
Mark Blewett
-
Matthew Vogt
-
scott