Jump to page: 1 2
Thread overview
How would you do this in D?
May 11, 2011
Jonathan M Davis
May 11, 2011
Jens Mueller
May 11, 2011
Don
May 11, 2011
Jonathan M Davis
May 11, 2011
so
May 11, 2011
Alexander
May 11, 2011
Alexander
May 11, 2011
Daniel Gibson
May 11, 2011
Daniel Gibson
May 11, 2011
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 11, 2011
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 11, 2011
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 11, 2011
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 11, 2011
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 = 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 11, 2011
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 11, 2011
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
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
> 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
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
« First   ‹ Prev
1 2