www.digitalmars.com         C & C++   DMDScript  

digitalmars.D.learn - Factoring out looping structure

reply Doctor J <nobody nowhere.com> writes:
I'm new to template programming and I have a problem for which I think it would
be a good match, but I'd like to get some advice on how to go about it.

I have a family of algorithms with the same looping structure, but different
particulars:

------------------------
state_t state ;
for (int i = 0 ; i < imax ; ++i)
{
    compute_i(state, i);
    for (int j = 0 ; j < jmax ; ++j)
    {
        compute_j(state,i,j);
        for (int k = 0 ; k < kmax ; ++k)
        {
            compute_k(state,i,j,k);
            for (int m = 0 ; m < mmax ; ++m)
            {
                compute_m(state,i,j,k,m);
            }
            finish_k(state,i,j,k);
        }
        finish_j(state,i,j);
    }
}
--------------------------

I'd like to factor out the particulars from the looping structure, so I can
write the loops once and then implement a bunch of variants with different
particulars (the looping structure is in fact more complicated than the
abstract example given).  Obviously I could do this by passing in an object
implementing an interface with all my functions; however, the whole point of
this exercise is to avoid the overhead of function calls.  The code needs to be
efficient, and (especially the inner loop) functions need to be inlined.

So: what are my options in D?  Templates, delegates, mixins...?  I'd like to
avoid string mixins if possible because they're a bit ugly.

Thanks!
Apr 06 2009
next sibling parent BCS <ao pathlink.com> writes:
Reply to Doctor,

 I'm new to template programming and I have a problem for which I think
 it would be a good match, but I'd like to get some advice on how to go
 about it.
 
 I have a family of algorithms with the same looping structure, but
 different particulars:
 
 ------------------------
 state_t state ;
 for (int i = 0 ; i < imax ; ++i)
 {
 compute_i(state, i);
 for (int j = 0 ; j < jmax ; ++j)
 {
 compute_j(state,i,j);
 for (int k = 0 ; k < kmax ; ++k)
 {
 compute_k(state,i,j,k);
 for (int m = 0 ; m < mmax ; ++m)
 {
 compute_m(state,i,j,k,m);
 }
 finish_k(state,i,j,k);
 }
 finish_j(state,i,j);
 }
 }
 --------------------------
 
 I'd like to factor out the particulars from the looping structure, so
 I can write the loops once and then implement a bunch of variants with
 different particulars (the looping structure is in fact more
 complicated than the abstract example given).  Obviously I could do
 this by passing in an object implementing an interface with all my
 functions; however, the whole point of this exercise is to avoid the
 overhead of function calls.  The code needs to be efficient, and
 (especially the inner loop) functions need to be inlined.
 
 So: what are my options in D?  Templates, delegates, mixins...?  I'd
 like to avoid string mixins if possible because they're a bit ugly.
 
 Thanks!
 
void Go(T)() { T.state_t state ; for (int i = 0 ; i < imax ; ++i) { T.compute_i(state, i); for (int j = 0 ; j < jmax ; ++j) { T.compute_j(state,i,j); for (int k = 0 ; k < kmax ; ++k) { T.compute_k(state,i,j,k); for (int m = 0 ; m < mmax ; ++m) { compute_m(state,i,j,k,m); } T.finish_k(state,i,j,k); } T.finish_j(state,i,j); } } } struct T_a { static struct state_t { ... } // or alias static void compute_i(state_t, int i){ ... } static void compute_j(state_t, int i, int j){ ... } static void compute_k(state_t, int i, int j, int k){ ... } static void compute_l(state_t, int i, int j, int k, int l){ ... } } Go!(T_a)(); struct T_b { static struct state_t { ... } // or alias static void compute_i(state_t, int i){ ... } static void compute_j(state_t, int i, int j){ ... } static void compute_k(state_t, int i, int j, int k){ ... } static void compute_l(state_t, int i, int j, int k, int l){ ... } } Go!(T_b)(); untested but you get the idea A similar approach can be done replacing (T) with (alias T) and using 'template T_a()' in places of the structs
Apr 06 2009
prev sibling next sibling parent "Denis Koroskin" <2korden gmail.com> writes:
On Tue, 07 Apr 2009 00:33:04 +0400, Doctor J <nobody nowhere.com> wrote:

 I'm new to template programming and I have a problem for which I think  
 it would be a good match, but I'd like to get some advice on how to go  
 about it.

 I have a family of algorithms with the same looping structure, but  
 different particulars:

 ------------------------
 state_t state ;
 for (int i = 0 ; i < imax ; ++i)
 {
     compute_i(state, i);
     for (int j = 0 ; j < jmax ; ++j)
     {
         compute_j(state,i,j);
         for (int k = 0 ; k < kmax ; ++k)
         {
             compute_k(state,i,j,k);
             for (int m = 0 ; m < mmax ; ++m)
             {
                 compute_m(state,i,j,k,m);
             }
             finish_k(state,i,j,k);
         }
         finish_j(state,i,j);
     }
 }
 --------------------------

 I'd like to factor out the particulars from the looping structure, so I  
 can write the loops once and then implement a bunch of variants with  
 different particulars (the looping structure is in fact more complicated  
 than the abstract example given).  Obviously I could do this by passing  
 in an object implementing an interface with all my functions; however,  
 the whole point of this exercise is to avoid the overhead of function  
 calls.  The code needs to be efficient, and (especially the inner loop)  
 functions need to be inlined.

 So: what are my options in D?  Templates, delegates, mixins...?  I'd  
 like to avoid string mixins if possible because they're a bit ugly.

 Thanks!
An obvious way is to do a straight convertion: template (alias compute_i, alias compute_j, alias compute_k, alias compute_m, alias finish_k, alias finish_j, int imax, int jmax, int kmax, int mmax) { void compure(State)(ref State state) { for (int i = 0; i < imax; ++i) { compute_i(state, i); for (int j = 0; j < jmax; ++j) { compute_j(state,i,j); for (int k = 0; k < kmax; ++k) { compute_k(state,i,j,k); for (int m = 0; m < mmax; ++m) { compute_m(state,i,j,k,m); } finish_k(state,i,j,k); } finish_j(state,i,j); } } } } Usage: void foo(ref State state, int i) { ... } void bar(ref State state, int i, int j) { ... } void baz(ref State state, int i, int j, int k) { ... } void qux(ref State state, int i, int j, int k, int m) { ... } void corge(ref State state, int i, int j, int k) { ... } void grault(ref State state, int i, int j) { ... } void garply(ref State state, int i) { ... } struct State { // ... } State state; compute!(foo,bar,baz,quz,corge,grault,garply)(state); // state is modified and stores a result (pass nothing if it's not needed)
Apr 06 2009
prev sibling parent Doctor J <nobody nowhere.com> writes:
Thank you for your prompt replies, that helps a lot.  I'll combine both ideas
so I can keep the functions for one variant together.  Much appreciated.
Apr 06 2009