octave-maintainers
[Top][All Lists]
Advanced

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

Fwd: Re: Fwd: Considering adding a "dispatch" function for compile-time


From: John W. Eaton
Subject: Fwd: Re: Fwd: Considering adding a "dispatch" function for compile-time polymorphism
Date: Mon, 11 Aug 2014 14:03:58 -0400
User-agent: Mozilla/5.0 (X11; Linux x86_64; rv:24.0) Gecko/20100101 Icedove/24.5.0

I'm forwarding this to keep the discussion on the list.

jwe

-------- Original Message --------
Subject:        Re: Fwd: Considering adding a "dispatch" function for
compile-time polymorphism
Date:   Sat, 9 Aug 2014 13:30:03 -0600
From:   David Spies <address@hidden>
To:     John W. Eaton <address@hidden>


On Sat, Aug 9, 2014 at 11:21 AM, John W. Eaton <address@hidden
<mailto:address@hidden>> wrote:

    On 08/08/2014 03:40 PM, David Spies wrote:

             How is the type of resvec unknown?  It appears to me that
        the type
             of resvec is R.

        R is a template parameter.  That's what I meant by unknown


    That doesn't make it unknown.  It has type R, which presumably must
    provide some kind of interface to be used.  And the way it is
    currently used, it is actually constructed inside the function
    because you have

       resvec = R (res_dims);

    inside the function even though resvec is passed by reference.


Yes, that works because the constructors for the various possible inputs
to R all take only a dim_vector as an argument.  As I mentioned in my
last reply, I forgot that I made that change.



             But putting stuff in a namespace doesn't actually make it
        private,
             so if you really want to have all these internal/private
        functions,
             maybe some other mechanism is appropriate?

        Not in C++.  We already had this whole discussion and I thought the
        final decision was that the best approach would be to use a
        namespace to
        conceal the "private" functions.


    Perhaps I agreed that introducing a namespace would OK even though
    we are not doing that uniformly in Octave yet.  (We probably should
    be, but that's a much larger and separate issue.)  However, I don't
    recall agreeing that using a namespace should imply that anything
    inside the namespace is somehow considered private.  That certainly
    won't be true of any other namespaces we introduce in Octave later,
    and I don't think it is a good convention.


Then how should I indicate that it's private?

         There has to be a better name than "find_to_R".  Just "find" would
         be better.  Most functions do something and store the result in
    some
         object, but they are not all named VERB_to_OBJECT and naming
         something that way does not help with understanding.

    There already is a "find".


    With the same signature?

Function overloading should be reserved for public/interface methods
where it doesn't matter which one is called.

This is why I never write functions that behave like:

myFun(x) {
   //Do some stuff
}

myFun(y) {
   myFun(y_to_x(y))
}

Sure, it's legal, but depending on the circumstances, the compiler still
might decide y_to_x(y) should be implicitly cast (or in this case,
template-resolved) to a y and end up calling the second myFun (resulting
in baseless recursion).

(What's fun about this bug in general is that if you compile and run it
with O0, you get a stack-overflow, but with O3, it uses tail-call
optimization to eliminate the recursive call and the program hangs instead)

The defensive driving practice is:

myFun(x) {
   private_myFun(x)
}

myFun(y) {
   private_myFun(y_to_x(y))
}

private_myFun(x) {
   //Do some stuff
}

This avoids giving the compiler an opportunity to misinterpret.



             As it is, your two switch cases are nearly identical
        copy-pasted
             code. Perhaps I'm wrong, but I think there has to be a
        better way of
             writing this.

Three lines of copy-pasted boilerplate right next to each other is a
        very different thing from copy-pasting the entire find function.


    Perhaps, but if the find function works on iterators, why would copy
    and pasting be needed at all?  Wouldn't it be the same in both
    cases, with the iterator handling the direction?  Isn't the function
    essentially something like

       find (iterator it, accumulator acc)
       {
          while (it != it.end ())
            {
               if (it.value ())
                 acc.record_location (it);

               it.next ();

            }
       }

I agree this would work, but then the template for the direction is
really just wrapped up in the iterator, so it hasn't actually been
removed, just hidden.  The iterator itself is less versatile because it
can only step in one direction, so any algorithm which steps back and
forth can't use it.
Also, I don't like using != to check for the endpoints because in
general there are algorithms that can overshoot, and I'd like to make
the iterator generic enough to handle those.
In any case as I mentioned at the top of  this email, I wrote this two
months ago and I've already used these iterators for everything I've
written in this entire project.  This is a fundamental rewrite to the
way iterators work and I don't think I have time to make that change in
the last week of GSoC.

                      In find.cc:

                      namespace find
                      {
                         [...]

                         template<int nargout, typename T>
                         struct ffind_result;


    Something I forgot to mention earlier is that I'm not really sold on
    this style of using constant template parameters.  Something about
    this feels clunky and wrong to me, so if you think this is a good
    way to write this code, you're going to have to convince me of that.

It's the most efficient alternative. Whether using a template parameter
or a regular parameter, when inlined, the bottleneck of the find method
will (at best) look something like:
Loop over nz-elems {
   Deal-with-early-termination-conditions
   if(nargout == 1)
     computeStoreLinearIndex
   else if(nargout >= 2) {
     computeStoreRow
     computeStoreCol
     if(nargout == 3)
       computeStoreElement
   }
}

Perhaps the compiler's smart enough to transpose the condition and the
for loop (but that becomes less likely as more stuff is added) or
perhaps I can rely on branch prediction to know which path to take, but
why worry about it at all?  The template parameter ensures only the
correct path is compiled and avoids the overhead completely without
unnecessary code duplication.






reply via email to

[Prev in Thread] Current Thread [Next in Thread]