www.digitalmars.com         C & C++   DMDScript  

digitalmars.D.learn - How to export a deduced template type to the enclosing scope?

reply =?UTF-8?B?QWxpIMOHZWhyZWxp?= <acehreli yahoo.com> writes:
I think similar questions were asked by others in different contexts before.

I played with core.thread.Fibre a little bit. As others have done a 
number of times before, I tried to make the following syntax possible 
inside fiber code:

     yield(42);

I wonder whether there is a clever trick to pull out a deduced type from 
a template. I don't think it is possible, because there may be many 
instantiations of the template and it would not be clear which one to 
pull out. (I don't think there is any way of getting all of the 
instantiations of a template because the compiler has only a partial 
view of the program at a time.)

However, what if there is exactly one instantiation allowed?

struct S(alias Func)
{
     /* This template mixin will instantiate the yield(T) template. */
     mixin Func!();

     void yield(T)(T)
     {
         /* As expected and demonstrated by the following pragma, in
          * this case T happens to be 'double'. Assuming that Func is
          * not allowed to call yield() with more than one type, can we
          * pull the actual type of T out into S's scope? */

         alias YieldedT = T;
         pragma(msg, YieldedT);    /* Prints 'double'. */
     }

     /* QUESTION: Can we know the type for the single instantiation of
      *           yield(T) here? */

     YieldedT data;    /* What is YieldedT? */
}

mixin template MyFunc()
{
     void foo()
     {
         double d;
         yield(d);    /* <-- The single instantiation */
     }
}

void main()
{
     auto s = S!MyFunc();
     s.foo();
}

So near and yet so far... :)

Ali
Sep 23 2014
next sibling parent reply "monarch_dodra" <monarchdodra gmail.com> writes:
On Tuesday, 23 September 2014 at 22:09:11 UTC, Ali Çehreli wrote:
 So near and yet so far... :)

 Ali
You explain "how" you want to achieve your goal, but I'm not sure I understand "what" the goal is. Perhaps if you explain that in more detail, I'd have a better understanding of the problem. I've never used core.thread.Fibre, so I don't know what "yield(42);" would do, so apologies if the question is stupid.
Sep 23 2014
parent reply =?UTF-8?B?QWxpIMOHZWhyZWxp?= <acehreli yahoo.com> writes:
On 09/23/2014 03:28 PM, monarch_dodra wrote:
 On Tuesday, 23 September 2014 at 22:09:11 UTC, Ali Çehreli wrote:
 So near and yet so far... :)

 Ali
You explain "how" you want to achieve your goal, but I'm not sure I understand "what" the goal is. Perhaps if you explain that in more detail, I'd have a better understanding of the problem. I've never used core.thread.Fibre, so I don't know what "yield(42);" would do, so apologies if the question is stupid.
Sorry. You are right. :) I almost deleted all of the specifics to avoid any concrete example as I think the question is still valid. Very briefly, fibers provide faster concurrency by executing both the owner and the fiber on the same thread. They are D's coroutines, without the much-missed 'yield' keyword. There is some language (runtime?) support for fibers though as the 'yield()' and 'call()' calls do involve context switching behind the scenes (much faster than thread context switching). I am not experienced with them either but from the examples on the core.thread documentation it looks like a fiber cannot yield a value in D; it must cause side-effects first, and then yield(), so that the owner can observe the side-effect. Fibers give the convenience of iteration a.la. opCall(), and InputRange interface comes naturally. (Example below.) I played with the idea of enabling the yield(42) syntax from the fiber code just because it does not involve side-effects and is more intuitive. There are blog posts, thread discussions, and enhancement reports about the same. So, my goal was the above: The client would make a yield(42) call, the data would be saved in a local variable automatically (the type of which is the question at hand), and then the result would be provided on an InputRange interface. Additionally, although I am not aware of other examples of it, again as an experiment, I tried to see whether injecting user code by a template mixin would make any sense. Since template mixins are expanded where they are mixed-in, then I thought perhaps I could deduce a type from inside that code. I failed, you will succeed. :o) Here is a working example with the minor inconvenience of having to specify ResultT from the outside, which seems redundant. The goal is to remove the need for that parameter. Note that I am aware of the inheritance-based kind of fibers and the following may not make sense at all. I was just experimenting. import std.stdio; import core.thread; import std.algorithm; /* This is what the user provides. We will mix this in. */ mixin template Foo() { void run() { /* Note how naturally the elements are produced in imperative * style: */ foreach (i; 0 .. 10) { yield(i); /* The element type is obviously 'int'. */ } } } /* This is an InputRange that exposes the yielded values as * elements. PROBLEM: Admittedly minor, ResultT seems redundant. */ class FiberRange(alias Func, ResultT) { mixin Func!(); // User code mixed-in ResultT result; // The front element Fiber fiber; // The fiber that does the work /* Supports the more natural yield(42) syntax. */ final void yield(T : ResultT)(T result) { this.result = result; Fiber.yield; } public: this() { /* He fiber is started by the user's 'run' function. */ this.fiber = new Fiber(&run); this.fiber.call(); } /* Trivial InputRange interface: */ final property bool empty() const { return fiber.state == Fiber.State.TERM; } final property ResultT front() const { return result; } final void popFront() { this.fiber.call; } } /* Convenience function: */ auto fiberRange(alias Func, ResultT)() { return new FiberRange!(Func, ResultT); } void main() { /* Works like a charm! :p */ auto result = fiberRange!(Foo, int) .filter!(a => a % 2); assert(result.equal([1, 3, 5, 7, 9])); } Can we get rid of the ResultT template parameter? Tricks with variadic templates, the with statement, etc. come to mind but I could not manage it. Ali
Sep 23 2014
parent reply Steven Schveighoffer <schveiguy yahoo.com> writes:
On 9/23/14 7:12 PM, Ali Çehreli wrote:

 Can we get rid of the ResultT template parameter? Tricks with variadic
 templates, the with statement, etc. come to mind but I could not manage it.
I don't think so. yield just returns control back to another fiber. It's not an entry point. What you *could* do is match an "expectation" function with the yield result. In other words, at the place where you expect the fiber to yield you a result, pass in a pointer to a value for the yield to fill in: expectYield(&someint); -> fiber runs, yields an int, and then when this yields the result, someint is filled in. I'm not super familiar with how D fibers work, or fibers in general. But perhaps it's the 'call' function that could be made to take parameters. -Steve
Sep 25 2014
parent =?UTF-8?B?QWxpIMOHZWhyZWxp?= <acehreli yahoo.com> writes:
On 09/25/2014 05:40 AM, Steven Schveighoffer wrote:

 On 9/23/14 7:12 PM, Ali Çehreli wrote:

 Can we get rid of the ResultT template parameter? Tricks with variadic
 templates, the with statement, etc. come to mind but I could not
 manage it.
I don't think so.
Me neither. :) This question made me aware of a distinction in template use cases. - Templates are mostly for generic algorithms as in "This code will work with any type, value, etc." - The yield() template in my example was not about genericity: I wanted to instantiate that template with only one type (which currently has to be provided by the user). So, I wanted to take advantage of the template type deduction to detect a type from inside the user's code. Not a big deal at all. Ali
Sep 25 2014
prev sibling parent "kiran kumari" <kiranfabzen gmail.com> writes:
On Tuesday, 23 September 2014 at 22:09:11 UTC, Ali Çehreli wrote:
 I think similar questions were asked by others in different 
 contexts before.

 I played with core.thread.Fibre a little bit. As others have 
 done a number of times before, I tried to make the following 
 syntax possible inside fiber code:

     yield(42);

 I wonder whether there is a clever trick to pull out a deduced 
 type from a template. I don't think it is possible, because 
 there may be many instantiations of the template and it would 
 not be clear which one to pull out. (I don't think there is any 
 way of getting all of the instantiations of a template because 
 the compiler has only a partial view of the program at a time.)

 However, what if there is exactly one instantiation allowed?

 struct S(alias Func)
 {
     /* This template mixin will instantiate the yield(T) 
 template. */
     mixin Func!();
 see more example
http://techgurulab.com/course/java-quiz-online/
     void yield(T)(T)
     {
         /* As expected and demonstrated by the following 
 pragma, in
          * this case T happens to be 'double'. Assuming that 
 Func is
          * not allowed to call yield() with more than one type, 
 can we
          * pull the actual type of T out into S's scope? */

         alias YieldedT = T;
         pragma(msg, YieldedT);    /* Prints 'double'. */
     }

     /* QUESTION: Can we know the type for the single 
 instantiation of
      *           yield(T) here? */

     YieldedT data;    /* What is YieldedT? */
 }

 mixin template MyFunc()
 {
     void foo()
     {
         double d;
         yield(d);    /* <-- The single instantiation */
     }
 }

 void main()
 {
     auto s = S!MyFunc();
     s.foo();
 }

 So near and yet so far... :)

 Ali
Sep 23 2014