www.digitalmars.com         C & C++   DMDScript  

digitalmars.D.learn - Dispatching from receive to a function

reply Bob Cowdery <bob bobcowdery.plus.com> writes:
 I've been looking for a way to hook up messages to a thread with a
handler function. As far as I can tell receive pattern matching only
allows the pattern to be distinguished by the parameter signature which
does not help when all the handlers only have a few different signatures.

I like the pattern matching concept as I have worked with Erlang which
will pattern match to a term (basically an arbitrary structure). Erlang
defines an Atom type which is really a global constant that can be used
as a message type in the match. Does anyone know of a way to simulate this?

Failing that I think a dispatcher structure using an associative array
would be my next choice. However I'm getting stuck on how to define and
use an array which maps a string key to a delegate. Can someone help me
out there please. I will then effectively have my 'Atom' as a string in
the message and dispatch on that.

I don't want to hard code in a switch statement.

Any other suggestions are very welcome.

Thanks
Bob
Sep 27 2010
parent reply Johannes Pfau <spam example.com> writes:
On 27.09.2010 15:07, Bob Cowdery wrote:
 
 Failing that I think a dispatcher structure using an associative array
 would be my next choice. However I'm getting stuck on how to define and
 use an array which maps a string key to a delegate. Can someone help me
 out there please. I will then effectively have my 'Atom' as a string in
 the message and dispatch on that.
 
Not sure about the other questions, but here's an example for a string/delegate associative array: ------------------------------------------- import std.stdio; alias bool delegate(int, char) TestDelegate; TestDelegate[string] dict; class C { bool test(int i, char c) {return true;} } void main() { C c = new C(); dict["test"] = &c.test; writeln(dict["test"](0, 'c')); } ----------------------------------------------- -- Johannes Pfau
Sep 27 2010
parent reply Bob Cowdery <bob bobcowdery.plus.com> writes:
 Thanks. Do you know if the signature is a mandatory part. If I left the
signature out would it then only work with a delegate with no
parameters? If so I think I'm in the same state as my delegates will not
all have the same signature.

Bob

On 27/09/2010 14:21, Johannes Pfau wrote:
 On 27.09.2010 15:07, Bob Cowdery wrote:
 Failing that I think a dispatcher structure using an associative array
 would be my next choice. However I'm getting stuck on how to define and
 use an array which maps a string key to a delegate. Can someone help me
 out there please. I will then effectively have my 'Atom' as a string in
 the message and dispatch on that.
Not sure about the other questions, but here's an example for a string/delegate associative array: ------------------------------------------- import std.stdio; alias bool delegate(int, char) TestDelegate; TestDelegate[string] dict; class C { bool test(int i, char c) {return true;} } void main() { C c = new C(); dict["test"] = &c.test; writeln(dict["test"](0, 'c')); } -----------------------------------------------
Sep 27 2010
parent reply Johannes Pfau <spam example.com> writes:
On 27.09.2010 15:36, Bob Cowdery wrote:
  Thanks. Do you know if the signature is a mandatory part. If I left the
 signature out would it then only work with a delegate with no
 parameters? If so I think I'm in the same state as my delegates will not
 all have the same signature.
 
Yep, the signature is mandatory. There is another, ugly, unsafe (it's safe, but you really have to be careful and write correct code) way: Basically you cast away the delegates type information. You then store the delegate in an array. Later, you have to cast the delegate back to it's original type. The problem here is, that D doesn't provide enough runtime type information to know which type the delegate originally had, so _we_ have to store some type info _manually_. This can for example be done by storing both the delegate and our type info in a struct. Here's an example how to do exactly that. !!!Note: It's very important to always cast the delegate back to the _correct_ type before calling it, everything else leads to undefined behavior!!! So just setting CDelegate.Type to a wrong type can cause a disaster! (Sorry about the bad variable names in the example. For better encapsulation most of the casts could be moved into the CDelegate struct by adding functions and constructors) --------------------------------------------------------------------------- import std.stdio; enum DelegateType { A, B, C, D } struct CDelegate { void delegate() del; //Exact type doesn't matter, all delegates have the same size DelegateType Type; } CDelegate[string] dict; class C { bool test(int i, char c) {return true;} } class D { int test(string s) {return 99;} } void main() { C c = new C(); D d = new D(); CDelegate custom; custom.del = cast(void delegate())&c.test; custom.Type = DelegateType.C; dict["c"] = custom; custom.del = cast(void delegate())&d.test; custom.Type = DelegateType.D; dict["d"] = custom; foreach(cdeleg; dict) { switch(cdeleg.Type) { case DelegateType.C: bool delegate(int i, char c) realdel = cast (bool delegate(int i, char c)) cdeleg.del; writeln(realdel(0, 'c')); break; case DelegateType.D: int delegate(string) realdel = cast (int delegate(string)) cdeleg.del; writeln(realdel("test")); break; } } } --------------------------------------------------------------------------- -- Johannes Pfau
Sep 27 2010
parent reply Juanjo Alvarez <juanjux thatwebmailofgoogleproperty.com> writes:
I'm new to the language so I don't know if this is horribly wrong on some 
levels, but it works:

-----------------------------

import std.variant;
import std.stdio;

class C
{
    bool test(int i, char c) { writeln("Hello from test1"); return true; }
    void test2(string v) { writeln("Hello from test2, ", v); }
}


void main()
{
    auto c = new C;
    Variant[string] holder = ["test": Variant(&c.test), "test2": Variant
(&c.test2)];
    receiver(holder);
}

void receiver(Variant[string] message) 
{
    // If you get the Variant template instantiation delegate 
    // signature wrong, it will
    // be detected at compile time!
    auto t = message["test"].get!(bool delegate(int, char));
    auto t2 = message["test2"].get!(void delegate(string));
    t(1, 'c');
    t2("foo");
}
--------------------------

Curiously if you create holder like this, it will give an arrayoutofbound 
error at runtime, I don't know if that is a bug:

void main()
{
   auto c = new C;
   Variant[string] holder;
   holder["test"] = Variant(&c.test);
   holder["test2"] = Variant(&c.test2);
}

On Mon, 27 Sep 2010 18:03:32 +0200, Johannes Pfau wrote:

 On 27.09.2010 15:36, Bob Cowdery wrote:
  Thanks. Do you know if the signature is a mandatory part. If I left
  the
 signature out would it then only work with a delegate with no
 parameters? If so I think I'm in the same state as my delegates will
 not all have the same signature.
 
 
Yep, the signature is mandatory. There is another, ugly, unsafe (it's safe, but you really have to be careful and write correct code) way: Basically you cast away the delegates type information. You then store the delegate in an array. Later, you have to cast the delegate back to it's original type. The problem here is, that D doesn't provide enough runtime type information to know which type the delegate originally had, so _we_ have to store some type info _manually_. This can for example be done by storing both the delegate and our type info in a struct. Here's an example how to do exactly that. !!!Note: It's very important to always cast the delegate back to the _correct_ type before calling it, everything else leads to undefined behavior!!! So just setting CDelegate.Type to a wrong type can cause a disaster! (Sorry about the bad variable names in the example. For better encapsulation most of the casts could be moved into the CDelegate struct by adding functions and constructors)
---------------------------------------------------------------------------
 import std.stdio;
 
 enum DelegateType
 {
     A,
     B,
     C,
     D
 }
 
 struct CDelegate
 {
     void delegate() del; //Exact type doesn't matter, all delegates have
 the same size
     DelegateType Type;
 }
 
 CDelegate[string] dict;
 
 class C
 {
     bool test(int i, char c) {return true;}
 }
 
 class D
 {
     int test(string s) {return 99;}
 }
 
 void main()
 {
     C c = new C();
     D d = new D();
     CDelegate custom;
 
     custom.del = cast(void delegate())&c.test; custom.Type =
     DelegateType.C;
     dict["c"] = custom;
 
     custom.del = cast(void delegate())&d.test; custom.Type =
     DelegateType.D;
     dict["d"] = custom;
 
     foreach(cdeleg; dict)
     {
         switch(cdeleg.Type)
         {
             case DelegateType.C:
                 bool delegate(int i, char c) realdel = cast (bool
 delegate(int i, char c)) cdeleg.del;
                 writeln(realdel(0, 'c'));
                 break;
 
             case DelegateType.D:
                 int delegate(string) realdel = cast (int
 delegate(string)) cdeleg.del;
                 writeln(realdel("test"));
                 break;
         }
     }
 }
 
---------------------------------------------------------------------------
Sep 27 2010
parent reply Johannes Pfau <spam example.com> writes:
On 27.09.2010 20:46, Juanjo Alvarez wrote:
 I'm new to the language so I don't know if this is horribly wrong on some 
 levels, but it works:
 
 -----------------------------
 
 import std.variant;
 import std.stdio;
 
 class C
 {
     bool test(int i, char c) { writeln("Hello from test1"); return true; }
     void test2(string v) { writeln("Hello from test2, ", v); }
 }
 
 
 void main()
 {
     auto c = new C;
     Variant[string] holder = ["test": Variant(&c.test), "test2": Variant
 (&c.test2)];
     receiver(holder);
 }
 
 void receiver(Variant[string] message) 
 {
     // If you get the Variant template instantiation delegate 
     // signature wrong, it will
     // be detected at compile time!
     auto t = message["test"].get!(bool delegate(int, char));
     auto t2 = message["test2"].get!(void delegate(string));
     t(1, 'c');
     t2("foo");
 }
 --------------------------
 
 Curiously if you create holder like this, it will give an arrayoutofbound 
 error at runtime, I don't know if that is a bug:
 
 void main()
 {
    auto c = new C;
    Variant[string] holder;
    holder["test"] = Variant(&c.test);
    holder["test2"] = Variant(&c.test2);
 }
 
I totally forgot about Variant! As far as I know that's exactly what variant is for and it's way better than my proposed hack. The example you posted is perfectly fine as well. I'm not sure what causes the arrayoutofbound error, the second example is correct as well. -- Johannes Pfau
Sep 27 2010
parent reply Juanjo Alvarez <juanjux thatwebmailofgoogleproperty.com> writes:
On Mon, 27 Sep 2010 21:00:48 +0200, Johannes Pfau wrote:

 On 27.09.2010 20:46, Juanjo Alvarez wrote:
 I'm new to the language so I don't know if this is horribly wrong on
 some levels, but it works:
 
 -----------------------------
 
 import std.variant;
 import std.stdio;
 
 class C
 {
     bool test(int i, char c) { writeln("Hello from test1"); return
     true; } void test2(string v) { writeln("Hello from test2, ", v); }
 }
 
 
 void main()
 {
     auto c = new C;
     Variant[string] holder = ["test": Variant(&c.test), "test2":
     Variant
 (&c.test2)];
     receiver(holder);
 }
 
 void receiver(Variant[string] message) {
     // If you get the Variant template instantiation delegate //
     signature wrong, it will
     // be detected at compile time!
     auto t = message["test"].get!(bool delegate(int, char)); auto t2 =
     message["test2"].get!(void delegate(string)); t(1, 'c');
     t2("foo");
 }
 --------------------------
 
 Curiously if you create holder like this, it will give an
 arrayoutofbound error at runtime, I don't know if that is a bug:
 
 void main()
 {
    auto c = new C;
    Variant[string] holder;
    holder["test"] = Variant(&c.test);
    holder["test2"] = Variant(&c.test2);
 }
 
 
I totally forgot about Variant! As far as I know that's exactly what variant is for and it's way better than my proposed hack. The example you posted is perfectly fine as well. I'm not sure what causes the arrayoutofbound error, the second example is correct as well.
Yep, variants are cool. You could even use an struct as value type of the associative array and store the variant in one member and the signature of the delegate, as string, in another member. The you could use a mixin in the receiver to extract the delegate without knowing the original signature. All this is wonderfully disturbing to me :)
Sep 27 2010
next sibling parent Bob Cowdery <bob bobcowdery.plus.com> writes:
 Thanks for the suggestions. I'm still hankering after an elegant
solution to the receive rather than try to patch it up after the event.
The best I can come up with is pretty ugly (odd) at the front but looks
better at the back-end. Maybe someone can refine it a bit.


struct RATE{};
struct SRC_1{};
struct SRC_2{};

class OutputFrame{
void set_rate(RATE desc, int speed) {..}
void set_clock_src1(SRC_1 desc, string lo_ref){...}
void set_clock_src2(SRC_2 desc, string hi_ref) {...}
}

output_buffer = new OutputFrame();
while (true) {
            receive(
                (immutable (float) [] smpls) {
                    add_to_buffer(smpls);
                    output_data();
                },
                &output_buffer.set_rate,
                &output_buffer.set_clock_src1,
                &output_buffer.set_clock_src2,
            );
 }


On 27/09/2010 20:49, Juanjo Alvarez wrote:
 On Mon, 27 Sep 2010 21:00:48 +0200, Johannes Pfau wrote:

 On 27.09.2010 20:46, Juanjo Alvarez wrote:
 I'm new to the language so I don't know if this is horribly wrong on
 some levels, but it works:

 -----------------------------

 import std.variant;
 import std.stdio;

 class C
 {
     bool test(int i, char c) { writeln("Hello from test1"); return
     true; } void test2(string v) { writeln("Hello from test2, ", v); }
 }


 void main()
 {
     auto c = new C;
     Variant[string] holder = ["test": Variant(&c.test), "test2":
     Variant
 (&c.test2)];
     receiver(holder);
 }

 void receiver(Variant[string] message) {
     // If you get the Variant template instantiation delegate //
     signature wrong, it will
     // be detected at compile time!
     auto t = message["test"].get!(bool delegate(int, char)); auto t2 =
     message["test2"].get!(void delegate(string)); t(1, 'c');
     t2("foo");
 }
 --------------------------

 Curiously if you create holder like this, it will give an
 arrayoutofbound error at runtime, I don't know if that is a bug:

 void main()
 {
    auto c = new C;
    Variant[string] holder;
    holder["test"] = Variant(&c.test);
    holder["test2"] = Variant(&c.test2);
 }
I totally forgot about Variant! As far as I know that's exactly what variant is for and it's way better than my proposed hack. The example you posted is perfectly fine as well. I'm not sure what causes the arrayoutofbound error, the second example is correct as well.
Yep, variants are cool. You could even use an struct as value type of the associative array and store the variant in one member and the signature of the delegate, as string, in another member. The you could use a mixin in the receiver to extract the delegate without knowing the original signature. All this is wonderfully disturbing to me :)
Sep 27 2010
prev sibling parent Philippe Sigaud <philippe.sigaud gmail.com> writes:
On Mon, Sep 27, 2010 at 21:49, Juanjo Alvarez <
juanjux thatwebmailofgoogleproperty.com> wrote:

 Curiously if you create holder like this, it will give an
 arrayoutofbound error at runtime, I don't know if that is a bug:
I think I got this one too. IIRC, it's a bug in the holder["test"] part than the Variant(&c.test) part.
 Yep, variants are cool. You could even use an struct as value type of the
 associative array and store the variant in one member and the signature
 of the delegate, as string, in another member. The you could use a mixin
 in the receiver to extract the delegate without knowing the original
 signature.

 All this is  wonderfully disturbing to me :)
I'm not sure you can. You can only mixin strings known at compile-time. Your string member will only have a value at runtime. Except if the struct is an enum, maybe. Philippe
Sep 27 2010