www.digitalmars.com         C & C++   DMDScript  

digitalmars.D - How would you do this in D?

reply Jose Armando Garcia <jsancio gmail.com> writes:
Hey guys,

I am trying to create a function that return true or executes
"something" the n-th time it is called from a specific call site. How
would you do that in D? In C we can do that with the help of
pre-processor macros. E.g.:

---
#include <assert.h>

#define EVERY(N, ACTION) { static int counter = 0; if(++counter > (N))
counter -= (N); if(counter == 1) (ACTION); }

int main()
{
   int i = 0;
   int j = 0;
   while(i < 10)
   {
      EVERY(10, ++j);
      EVERY(2, ++i);
   }
   assert(j == 2);
   assert(i == 10);
}
---

In D, I hacked the following implementation. I don't like this
implementation for a log of reason... Am I missing some D feature? Can
someone do better?

---
int[string] map;

bool every(string file = __FILE__, int line = __LINE__)(int time)
{
   // assume file ~ "+" ~ to!string(line) is unique
   // assumption could be removed by using a struct
   string key = file ~ "+" ~ to!string(line);

   map[key] += 1;
   if(map[key] > time) map[key] -= time;

   return map[key] == 1;
}

unittest
{
   auto i = 0;
   auto j = 0;
   while(i < 10)
   {
      if(every(10)) ++j;
      if(every(2)) ++i;
   }
   assert(j == 2);
   assert(i == 10);
}

void main() {}
---

Thanks!
-Jose
May 10 2011
next sibling parent reply Andrei Alexandrescu <SeeWebsiteForEmail erdani.org> writes:
On 5/10/11 7:32 PM, Jose Armando Garcia wrote:
 Hey guys,

 I am trying to create a function that return true or executes
 "something" the n-th time it is called from a specific call site. How
 would you do that in D? In C we can do that with the help of
 pre-processor macros. E.g.:

 ---
 #include<assert.h>

 #define EVERY(N, ACTION) { static int counter = 0; if(++counter>  (N))
 counter -= (N); if(counter == 1) (ACTION); }

 int main()
 {
     int i = 0;
     int j = 0;
     while(i<  10)
     {
        EVERY(10, ++j);
        EVERY(2, ++i);
     }
     assert(j == 2);
     assert(i == 10);
 }
 ---

 In D, I hacked the following implementation. I don't like this
 implementation for a log of reason... Am I missing some D feature? Can
 someone do better?
[snip] Just use a static variable inside a function parameterized by __FILE__ and __LINE__. Indeed the drawback is that two every() calls in one line will share that static. I consider it a small matter. If it becomes a bear we may as well add __COLUMN__ or something similar. Note that the macro version has other, arguably larger, drawbacks - e.g. it can't be used as an expression, evaluates the limit multiple times. pollutes the global namespace etc. Andrei
May 10 2011
next sibling parent Jose Armando Garcia <jsancio gmail.com> writes:
Thanks. I should have read
http://www.digitalmars.com/d/2.0/template.html more carefully.

"Multiple instantiations of a TemplateDeclaration with the same
TemplateArgumentList, before implicit conversions, all will refer to
the same instantiation."

The default values which are evaluated at the call site are part of
the TemplateArgumentList.

On Tue, May 10, 2011 at 9:52 PM, Andrei Alexandrescu
<SeeWebsiteForEmail erdani.org> wrote:
 On 5/10/11 7:32 PM, Jose Armando Garcia wrote:
 Hey guys,

 I am trying to create a function that return true or executes
 "something" the n-th time it is called from a specific call site. How
 would you do that in D? In C we can do that with the help of
 pre-processor macros. E.g.:

 ---
 #include<assert.h>

 #define EVERY(N, ACTION) { static int counter =3D 0; if(++counter> =A0(N=
))
 counter -=3D (N); if(counter =3D=3D 1) (ACTION); }

 int main()
 {
 =A0 =A0int i =3D 0;
 =A0 =A0int j =3D 0;
 =A0 =A0while(i< =A010)
 =A0 =A0{
 =A0 =A0 =A0 EVERY(10, ++j);
 =A0 =A0 =A0 EVERY(2, ++i);
 =A0 =A0}
 =A0 =A0assert(j =3D=3D 2);
 =A0 =A0assert(i =3D=3D 10);
 }
 ---

 In D, I hacked the following implementation. I don't like this
 implementation for a log of reason... Am I missing some D feature? Can
 someone do better?
[snip] Just use a static variable inside a function parameterized by __FILE__ an=
d
 __LINE__. Indeed the drawback is that two every() calls in one line will
 share that static. I consider it a small matter. If it becomes a bear we =
may
 as well add __COLUMN__ or something similar.

 Note that the macro version has other, arguably larger, drawbacks - e.g. =
it
 can't be used as an expression, evaluates the limit multiple times. pollu=
tes
 the global namespace etc.


 Andrei
May 10 2011
prev sibling next sibling parent Jonathan M Davis <jmdavisProg gmx.com> writes:
On 2011-05-10 18:06, Jose Armando Garcia wrote:
 Thanks. I should have read
 http://www.digitalmars.com/d/2.0/template.html more carefully.
 
 "Multiple instantiations of a TemplateDeclaration with the same
 TemplateArgumentList, before implicit conversions, all will refer to
 the same instantiation."
 
 The default values which are evaluated at the call site are part of
 the TemplateArgumentList.
Yes, default arguments are evaluated at the call site, not where the code is declared. I believe that C++ has the opposite behavior, so it's not uncommon that people miss that. It's _very_ useful though - particularly when dealing with __FILE__ and __LINE__. - Jonathan M Davis
May 10 2011
prev sibling next sibling parent reply Jens Mueller <jens.k.mueller gmx.de> writes:
Content-Disposition: inline

Jonathan M Davis wrote:
 On 2011-05-10 18:06, Jose Armando Garcia wrote:
 Thanks. I should have read
 http://www.digitalmars.com/d/2.0/template.html more carefully.
 
 "Multiple instantiations of a TemplateDeclaration with the same
 TemplateArgumentList, before implicit conversions, all will refer to
 the same instantiation."
 
 The default values which are evaluated at the call site are part of
 the TemplateArgumentList.
Yes, default arguments are evaluated at the call site, not where the code is declared. I believe that C++ has the opposite behavior, so it's not uncommon that people miss that. It's _very_ useful though - particularly when dealing with __FILE__ and __LINE__.
I did some little testing (see attached file). The bar() example shows that default arguments are evaluated at call site (even in C++). If it was only evaluated when declared it would always return 1. But __LINE__ does not work as in D. I believe that is due to __LINE__ being a macro in C whereas in D it is evaluated at instantiation time (i.e. it is not text replacement). So it seems that instantiation of default arguments is the same in D and C++. The difference is that __LINE__ and __FILE__ are different things in C++ and D. In C++ there is no such thing as evaluating __LINE__. Or let's say they are constant expressions (due to textual replacement) where in D they are evaluated differently depending on the context. Does this make sense? Jens
May 11 2011
parent reply Don <nospam nospam.com> writes:
Jens Mueller wrote:
 Jonathan M Davis wrote:
 On 2011-05-10 18:06, Jose Armando Garcia wrote:
 Thanks. I should have read
 http://www.digitalmars.com/d/2.0/template.html more carefully.

 "Multiple instantiations of a TemplateDeclaration with the same
 TemplateArgumentList, before implicit conversions, all will refer to
 the same instantiation."

 The default values which are evaluated at the call site are part of
 the TemplateArgumentList.
Yes, default arguments are evaluated at the call site, not where the code is declared. I believe that C++ has the opposite behavior, so it's not uncommon that people miss that. It's _very_ useful though - particularly when dealing with __FILE__ and __LINE__.
I did some little testing (see attached file). The bar() example shows that default arguments are evaluated at call site (even in C++). If it was only evaluated when declared it would always return 1. But __LINE__ does not work as in D. I believe that is due to __LINE__ being a macro in C whereas in D it is evaluated at instantiation time (i.e. it is not text replacement). So it seems that instantiation of default arguments is the same in D and C++. The difference is that __LINE__ and __FILE__ are different things in C++ and D. In C++ there is no such thing as evaluating __LINE__. Or let's say they are constant expressions (due to textual replacement) where in D they are evaluated differently depending on the context. Does this make sense?
__LINE__ and __FILE__ are magical when used as default arguments in D, for exactly this use case, since it is *extremely* useful. In every other case they behave as in C++. (IIRC the magic is not even implemented for default arguments of __LINE__ + 0, only for __LINE__).
May 11 2011
parent Jonathan M Davis <jmdavisProg gmx.com> writes:
On 2011-05-11 03:59, Don wrote:
 Jens Mueller wrote:
 Jonathan M Davis wrote:
 On 2011-05-10 18:06, Jose Armando Garcia wrote:
 Thanks. I should have read
 http://www.digitalmars.com/d/2.0/template.html more carefully.
 
 "Multiple instantiations of a TemplateDeclaration with the same
 TemplateArgumentList, before implicit conversions, all will refer to
 the same instantiation."
 
 The default values which are evaluated at the call site are part of
 the TemplateArgumentList.
Yes, default arguments are evaluated at the call site, not where the code is declared. I believe that C++ has the opposite behavior, so it's not uncommon that people miss that. It's _very_ useful though - particularly when dealing with __FILE__ and __LINE__.
I did some little testing (see attached file). The bar() example shows that default arguments are evaluated at call site (even in C++). If it was only evaluated when declared it would always return 1. But __LINE__ does not work as in D. I believe that is due to __LINE__ being a macro in C whereas in D it is evaluated at instantiation time (i.e. it is not text replacement). So it seems that instantiation of default arguments is the same in D and C++. The difference is that __LINE__ and __FILE__ are different things in C++ and D. In C++ there is no such thing as evaluating __LINE__. Or let's say they are constant expressions (due to textual replacement) where in D they are evaluated differently depending on the context. Does this make sense?
__LINE__ and __FILE__ are magical when used as default arguments in D, for exactly this use case, since it is *extremely* useful. In every other case they behave as in C++. (IIRC the magic is not even implemented for default arguments of __LINE__ + 0, only for __LINE__).
Hmm. Good to know. Well regardless of why and how it works with __FILE__ and __LINE__, it's definitely fantastic that they do. It is indeed _extremely_ useful. - Jonathan M Davis
May 11 2011
prev sibling next sibling parent so <so so.so> writes:
 Just use a static variable inside a function parameterized by __FILE__  
 and __LINE__. Indeed the drawback is that two every() calls in one line  
 will share that static. I consider it a small matter. If it becomes a  
 bear we may as well add __COLUMN__ or something similar.
__COLUMN__ would be a very nice addition. Not an everyday usage but there was this unorthodox approach on UI coding, immediate mode, which i quite like. The problem was the id generation. There were solutions, but either not enough or too verbose. With default template args and __F,L,C__ we would have a pretty good solution for this particular problem and similar issues. I don't know, maybe there are already some neat solutions to this in D.
May 11 2011
prev sibling parent reply Alexander <aldem+dmars nk7.net> writes:
On 11.05.2011 02:52, Andrei Alexandrescu wrote:

 Note that the macro version has other, arguably larger, drawbacks - e.g. it
can't be used as an expression, evaluates the limit multiple times. pollutes
the global namespace etc.
BTW, I don't get it - why *scoped* variable is going to *global* namespace when declared "static"? This, IMHO, defeats the value and purpose of scoping... /Alexander
May 11 2011
parent reply Andrei Alexandrescu <SeeWebsiteForEmail erdani.org> writes:
On 05/11/2011 12:07 PM, Alexander wrote:
 On 11.05.2011 02:52, Andrei Alexandrescu wrote:

 Note that the macro version has other, arguably larger, drawbacks - e.g. it
can't be used as an expression, evaluates the limit multiple times. pollutes
the global namespace etc.
BTW, I don't get it - why *scoped* variable is going to *global* namespace when declared "static"? This, IMHO, defeats the value and purpose of scoping... /Alexander
The macro name itself is polluting the global namespace. Andrei
May 11 2011
parent Alexander <aldem+dmars nk7.net> writes:
On 11.05.2011 20:07, Andrei Alexandrescu wrote:

 The macro name itself is polluting the global namespace.
OK, this I understand. But I cannot have something like: { static int i = 0; } { static int i = 1; } This produces linker warning: io.o:(.tdata.+0x0): multiple definition of `void io.main(immutable(char)[][]).int i' Sure, this defined in function scope, not globally, but anyway... /Alexander
May 11 2011
prev sibling parent reply Daniel Gibson <metalcaedes gmail.com> writes:
Am 11.05.2011 02:32, schrieb Jose Armando Garcia:
 Hey guys,
 
 I am trying to create a function that return true or executes
 "something" the n-th time it is called from a specific call site. How
 would you do that in D? In C we can do that with the help of
 pre-processor macros. E.g.:
 
 ---
 #include <assert.h>
 
 #define EVERY(N, ACTION) { static int counter = 0; if(++counter > (N))
 counter -= (N); if(counter == 1) (ACTION); }
 
 int main()
 {
    int i = 0;
    int j = 0;
    while(i < 10)
    {
       EVERY(10, ++j);
       EVERY(2, ++i);
    }
    assert(j == 2);
    assert(i == 10);
 }
 ---
 
 In D, I hacked the following implementation. I don't like this
 implementation for a log of reason... Am I missing some D feature? Can
 someone do better?
 
 ---
 int[string] map;
 
 bool every(string file = __FILE__, int line = __LINE__)(int time)
 {
    // assume file ~ "+" ~ to!string(line) is unique
    // assumption could be removed by using a struct
    string key = file ~ "+" ~ to!string(line);
 
    map[key] += 1;
    if(map[key] > time) map[key] -= time;
 
    return map[key] == 1;
 }
 
 unittest
 {
    auto i = 0;
    auto j = 0;
    while(i < 10)
    {
       if(every(10)) ++j;
       if(every(2)) ++i;
    }
    assert(j == 2);
    assert(i == 10);
 }
 
 void main() {}
 ---
 
 Thanks!
 -Jose
1. For ACTION you could use a delegate/lambda 2. Using a map is an ugly hack IMHO. IMHO it's not necessary, because the code generated by the template will be unique if __FILE__ and __LINE__ are unique (as you assume), so you could use a static int like in C. However, it's probably smarter and more clean to have struct (or maybe class) containing the counter, the delegate and the threshold (10 for every 10 etc) and a method incrementing the counter and invoking the delegate. The code isn't as short as the C version, but the implementation is more clean IMHO: http://pastebin.com/XVSnALbq There may be a more clever template-trick, however. Cheers, - Daniel
May 10 2011
parent Daniel Gibson <metalcaedes gmail.com> writes:
Am 11.05.2011 02:56, schrieb Daniel Gibson:
 
 There may be a more clever template-trick, however.
What about: void EVERY(int threshold, alias action, string file = __FILE__, int line = __LINE__)() { static int cnt; ++cnt; if(cnt==threshold) { action(); cnt=0; } } void main() { int i,j; while(i<10) { EVERY!(10, {++j;} )(); EVERY!(2, {++i;} ); // last () was optional } assert(j==2); assert(i==10); } :-)
May 10 2011