[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.
- Fwd: Re: Fwd: Considering adding a "dispatch" function for compile-time polymorphism,
John W. Eaton <=