Thread overview | |||||||||||
---|---|---|---|---|---|---|---|---|---|---|---|
|
September 27, 2010 Dispatching from receive to a function | ||||
---|---|---|---|---|
| ||||
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 |
September 27, 2010 Re: Dispatching from receive to a function | ||||
---|---|---|---|---|
| ||||
Posted in reply to Bob Cowdery | 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 |
September 27, 2010 Re: Dispatching from receive to a function | ||||
---|---|---|---|---|
| ||||
Posted in reply to Johannes Pfau | 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'));
> }
> -----------------------------------------------
>
|
September 27, 2010 Re: Dispatching from receive to a function | ||||
---|---|---|---|---|
| ||||
Posted in reply to Bob Cowdery | 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 |
September 27, 2010 Re: Dispatching from receive to a function | ||||
---|---|---|---|---|
| ||||
Posted in reply to Johannes Pfau | 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; > } > } > } > --------------------------------------------------------------------------- |
September 27, 2010 Re: Dispatching from receive to a function | ||||
---|---|---|---|---|
| ||||
Posted in reply to Juanjo Alvarez | 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 |
September 27, 2010 Re: Dispatching from receive to a function | ||||
---|---|---|---|---|
| ||||
Posted in reply to Johannes Pfau | 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 :)
|
September 27, 2010 Re: Dispatching from receive to a function | ||||
---|---|---|---|---|
| ||||
Posted in reply to Juanjo Alvarez | 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 :)
|
September 27, 2010 Re: Dispatching from receive to a function | ||||
---|---|---|---|---|
| ||||
Posted in reply to Juanjo Alvarez Attachments:
| 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 |
Copyright © 1999-2021 by the D Language Foundation