Goto statement

  • (3 Pages)
  • +
  • 1
  • 2
  • 3

32 Replies - 21534 Views - Last Post: 26 December 2012 - 11:47 AM

#16 baavgai  Icon User is offline

  • Dreaming Coder
  • member icon

Reputation: 5829
  • View blog
  • Posts: 12,683
  • Joined: 16-October 07

Re: Goto statement

Posted 24 December 2012 - 12:26 PM

View Postishkabible, on 24 December 2012 - 02:01 PM, said:

I'll maintain that sometimes it is the best solution even in C and C++.


Examples?
Was This Post Helpful? 0
  • +
  • -

#17 jon.kiparsky  Icon User is online

  • Pancakes!
  • member icon


Reputation: 7744
  • View blog
  • Posts: 13,084
  • Joined: 19-March 11

Re: Goto statement

Posted 24 December 2012 - 12:39 PM

Loop exit. Functions cost money, you know! :)
Was This Post Helpful? 0
  • +
  • -

#18 ishkabible  Icon User is offline

  • spelling expret
  • member icon




Reputation: 1622
  • View blog
  • Posts: 5,709
  • Joined: 03-August 09

Re: Goto statement

Posted 24 December 2012 - 03:32 PM

View Postbaavgai, on 24 December 2012 - 07:26 PM, said:

View Postishkabible, on 24 December 2012 - 02:01 PM, said:

I'll maintain that sometimes it is the best solution even in C and C++.


Examples?


linux networking code, where shaving a cycle or 2 on an inner loop might be helped by a goto, C code where you don't have exceptions but need to abruptly stop running code and go somewhere else.

These are situations that after experimentation, thought, and testing one might discover a goto was ideal. Maintainability is FAR more often the key design goal than performance but sometimes performance is part of meeting the demands of the application and becomes more important. In this case, on something which your profiler has dubbed important you should consider several redesigns of the code to get the most efficient algorithm possible. Experimenting with goto could be a possible candidate for breaking out of an inner loop or continuing an outer loop as a last resort is to me to be allowable. In the other cases I presented there was a lot of thought as to the maintainability and functionality and goto was considered ok.

This post has been edited by ishkabible: 24 December 2012 - 03:34 PM

Was This Post Helpful? 0
  • +
  • -

#19 baavgai  Icon User is offline

  • Dreaming Coder
  • member icon

Reputation: 5829
  • View blog
  • Posts: 12,683
  • Joined: 16-October 07

Re: Goto statement

Posted 24 December 2012 - 04:49 PM

View Postishkabible, on 24 December 2012 - 05:32 PM, said:

linux networking code, where shaving a cycle or 2 on an inner loop might be helped by a goto


Excellent example of goto used in the wild. I've seen that code. It's actually unix, propagated in code and some iconic network programming books.

It is not used to save a cycle. Why the hell would you bother optimizing network code to shave a few cycles when your bottle neck is the communications channel itself? Rather, it's used to simplify cleanup. I'm sure we've actually dredged up examples of this for another "OMG gotos gooder" thread.

But just because a goto was used, particularly before it was pretty much universally acknowledged as a bad idea, is hardly an argument.

I want to SEE a good example of where goto reveals why it is a superior choice. Still waiting on that.
Was This Post Helpful? 0
  • +
  • -

#20 ishkabible  Icon User is offline

  • spelling expret
  • member icon




Reputation: 1622
  • View blog
  • Posts: 5,709
  • Joined: 03-August 09

Re: Goto statement

Posted 24 December 2012 - 05:24 PM

I wasn't talking about the networking code when I was talking about shaving cycles. I was giving 3 district examples(ok the last one was similar to the first) of cases where goto is used. I'll show the sorting algorithm from libc++ where I actually got the idea to ever use a goto in any code I wrote beyond using goto for the sake of it.

it's an obtuse monster of a sorting algorithm but it's quite efficient. becuase efficiency was a greater concern that maintainability you see this kind of code. They have to use all double underscores and single underscores with capital letters becuase those identifiers are reserved by the standard, disregard that if you can.

this is a modern C++11 standard library and was only started a few years ago by the LLVM project as a standard library for clang++.

template <class _Compare, class _RandomAccessIterator>
void
__sort(_RandomAccessIterator __first, _RandomAccessIterator __last, _Compare __comp)
{
    // _Compare is known to be a reference type
    typedef typename iterator_traits<_RandomAccessIterator>::difference_type difference_type;
    typedef typename iterator_traits<_RandomAccessIterator>::value_type value_type;
    const difference_type __limit = is_trivially_copy_constructible<value_type>::value &&
                                    is_trivially_copy_assignable<value_type>::value ? 30 : 6;
    while (true)
    {
    __restart:
        difference_type __len = __last - __first;
        switch (__len)
        {
        case 0:
        case 1:
            return;
        case 2:
            if (__comp(*--__last, *__first))
                swap(*__first, *__last);
            return;
        case 3:
            _VSTD::__sort3<_Compare>(__first, __first+1, --__last, __comp);
            return;
        case 4:
            _VSTD::__sort4<_Compare>(__first, __first+1, __first+2, --__last, __comp);
            return;
        case 5:
            _VSTD::__sort5<_Compare>(__first, __first+1, __first+2, __first+3, --__last, __comp);
            return;
        }
        if (__len <= __limit)
        {
            _VSTD::__insertion_sort_3<_Compare>(__first, __last, __comp);
            return;
        }
        // __len > 5
        _RandomAccessIterator __m = __first;
        _RandomAccessIterator __lm1 = __last;
        --__lm1;
        unsigned __n_swaps;
        {
        difference_type __delta;
        if (__len >= 1000)
        {
            __delta = __len/2;
            __m += __delta;
            __delta /= 2;
            __n_swaps = _VSTD::__sort5<_Compare>(__first, __first + __delta, __m, __m+__delta, __lm1, __comp);
        }
        else
        {
            __delta = __len/2;
            __m += __delta;
            __n_swaps = _VSTD::__sort3<_Compare>(__first, __m, __lm1, __comp);
        }
        }
        // *__m is median
        // partition [__first, __m) < *__m and *__m <= [__m, __last)
        // (this inhibits tossing elements equivalent to __m around unnecessarily)
        _RandomAccessIterator __i = __first;
        _RandomAccessIterator __j = __lm1;
        // j points beyond range to be tested, *__m is known to be <= *__lm1
        // The search going up is known to be guarded but the search coming down isn't.
        // Prime the downward search with a guard.
        if (!__comp(*__i, *__m))  // if *__first == *__m
        {
            // *__first == *__m, *__first doesn't go in first part
            // manually guard downward moving __j against __i
            while (true)
            {
                if (__i == --__j)
                {
                    // *__first == *__m, *__m <= all other elements
                    // Parition instead into [__first, __i) == *__first and *__first < [__i, __last)
                    ++__i;  // __first + 1
                    __j = __last;
                    if (!__comp(*__first, *--__j))  // we need a guard if *__first == *(__last-1)
                    {
                        while (true)
                        {
                            if (__i == __j)
                                return;  // [__first, __last) all equivalent elements
                            if (__comp(*__first, *__i))
                            {
                                swap(*__i, *__j);
                                ++__n_swaps;
                                ++__i;
                                break;
                            }
                            ++__i;
                        }
                    }
                    // [__first, __i) == *__first and *__first < [__j, __last) and __j == __last - 1
                    if (__i == __j)
                        return;
                    while (true)
                    {
                        while (!__comp(*__first, *__i))
                            ++__i;
                        while (__comp(*__first, *--__j))
                            ;
                        if (__i >= __j)
                            break;
                        swap(*__i, *__j);
                        ++__n_swaps;
                        ++__i;
                    }
                    // [__first, __i) == *__first and *__first < [__i, __last)
                    // The first part is sorted, sort the secod part
                    // _VSTD::__sort<_Compare>(__i, __last, __comp);
                    __first = __i;
                    goto __restart;
                }
                if (__comp(*__j, *__m))
                {
                    swap(*__i, *__j);
                    ++__n_swaps;
                    break;  // found guard for downward moving __j, now use unguarded partition
                }
            }
        }
        // It is known that *__i < *__m
        ++__i;
        // j points beyond range to be tested, *__m is known to be <= *__lm1
        // if not yet partitioned...
        if (__i < __j)
        {
            // known that *(__i - 1) < *__m
            // known that __i <= __m
            while (true)
            {
                // __m still guards upward moving __i
                while (__comp(*__i, *__m))
                    ++__i;
                // It is now known that a guard exists for downward moving __j
                while (!__comp(*--__j, *__m))
                    ;
                if (__i > __j)
                    break;
                swap(*__i, *__j);
                ++__n_swaps;
                // It is known that __m != __j
                // If __m just moved, follow it
                if (__m == __i)
                    __m = __j;
                ++__i;
            }
        }
        // [__first, __i) < *__m and *__m <= [__i, __last)
        if (__i != __m && __comp(*__m, *__i))
        {
            swap(*__i, *__m);
            ++__n_swaps;
        }
        // [__first, __i) < *__i and *__i <= [__i+1, __last)
        // If we were given a perfect partition, see if insertion sort is quick...
        if (__n_swaps == 0)
        {
            bool __fs = _VSTD::__insertion_sort_incomplete<_Compare>(__first, __i, __comp);
            if (_VSTD::__insertion_sort_incomplete<_Compare>(__i+1, __last, __comp))
            {
                if (__fs)
                    return;
                __last = __i;
                continue;
            }
            else
            {
                if (__fs)
                {
                    __first = ++__i;
                    continue;
                }
            }
        }
        // sort smaller range with recursive call and larger with tail recursion elimination
        if (__i - __first < __last - __i)
        {
            _VSTD::__sort<_Compare>(__first, __i, __comp);
            // _VSTD::__sort<_Compare>(__i+1, __last, __comp);
            __first = ++__i;
        }
        else
        {
            _VSTD::__sort<_Compare>(__i+1, __last, __comp);
            // _VSTD::__sort<_Compare>(__first, __i, __comp);
            __last = __i;
        }
    }
}



you can find that code here. there are actually a number of uses in there becuase one of the biggest goals of that library is efficiency. they have tested and tested over and over again and they came up with these functions.

proof enough?

This post has been edited by ishkabible: 24 December 2012 - 05:31 PM

Was This Post Helpful? 0
  • +
  • -

#21 baavgai  Icon User is offline

  • Dreaming Coder
  • member icon

Reputation: 5829
  • View blog
  • Posts: 12,683
  • Joined: 16-October 07

Re: Goto statement

Posted 24 December 2012 - 06:19 PM

Odd. Just cleaned out most of the code to follow the flow logic:
while (true) {
__restart:
	if (__len <= __limit) { return; }
	{ /* strange, pointless, block notation */ }
	if (!__comp(*__i, *__m)) {
		while (true) {
			if (__i == --__j) {
				if (!__comp(*__first, *--__j)) { // we need a guard if *__first == *(__last-1)
					while (true) {
						if (__i == __j) return;
						if (__comp(*__first, *__i)) { break; }
					}
				}
				if (__i == __j) { return; }
				while (true) { if (__i >= __j) { break; } }
				goto __restart;
			}
			if (__comp(*__j, *__m)) { break; }
		}
	}
	if (__i < __j) { while (true) { if (__i > __j) { break; } } }
	if (__n_swaps == 0) {
		if (_VSTD::__insertion_sort_incomplete<_Compare>(__i+1, __last, __comp)) {
			if (__fs) { return; }
			continue; // pointless
		// pointless else
			continue; // pointless
		}
	}
}





So the issue:
while (true) {
__restart:
	if (foo) {
		while (true) {
			if (bar) {
				// stuff
				goto __restart;
			}
			if (baz) { break; }
		}
	}
	// stuff
}



Seriously? That technicolor yawn of code justifies a goto?

Hmm...
while (true) {
	if (foo) {
		bool done2 = false;
		while (!done2) {
			if (bar) {
				// stuff
				done2 = true;
			}
			if (!done2 && baz) { break; }
		}
		if (done2) { continue; }
	}
	// stuff
}



Still not convinced. If possible, less convinced.

This post has been edited by baavgai: 24 December 2012 - 06:22 PM

Was This Post Helpful? 2
  • +
  • -

#22 ishkabible  Icon User is offline

  • spelling expret
  • member icon




Reputation: 1622
  • View blog
  • Posts: 5,709
  • Joined: 03-August 09

Re: Goto statement

Posted 24 December 2012 - 06:41 PM

That code probably doesn't compile quite as efficiently. It's possible but I don't believe it would in the case of the actual presented code. The compiler would have to prove an awful lot to get rid of the extra checks you added. I also feel like the goto is a more readable LESS error prone solution than using flags and extra if statements. To me, it's cleaner, less confusing and has fewer points of failure. I don't think the monstrosity that that code is is what justifies the goto; the goto is justified by saving cycles. I also think it ends up being more readable but that's just my opinion.

edit:
"technicolor yawn", that was a new one for me. I like it :)

This post has been edited by ishkabible: 24 December 2012 - 06:53 PM

Was This Post Helpful? 0
  • +
  • -

#23 vividexstance  Icon User is offline

  • D.I.C Lover
  • member icon

Reputation: 659
  • View blog
  • Posts: 2,270
  • Joined: 31-December 10

Re: Goto statement

Posted 25 December 2012 - 09:04 AM

goto might be used in clang but after looking into the gcc standard C++ library's algorithm header, they don't use goto at all. For the most part, they use a lot of helper functions to make writing the different algorithms fairly simple.
Was This Post Helpful? 0
  • +
  • -

#24 Skydiver  Icon User is offline

  • Code herder
  • member icon

Reputation: 3574
  • View blog
  • Posts: 11,112
  • Joined: 05-May 12

Re: Goto statement

Posted 25 December 2012 - 07:41 PM

Slightly off topic, but to throw more fuel on the fire, also consider the semantics of short circuiting the evaluation of Boolean expressions. Isn't that there an implied 'goto' present? Doesn't the short circuiting cause confusion for people reading the code and who don't know that the language does short circuiting?

if (current == null || current->nextPtr == endPtr)
{
    // have to deal with current potentially being null or not null here and causing confusion.
}


Was This Post Helpful? 0
  • +
  • -

#25 baavgai  Icon User is offline

  • Dreaming Coder
  • member icon

Reputation: 5829
  • View blog
  • Posts: 12,683
  • Joined: 16-October 07

Re: Goto statement

Posted 26 December 2012 - 06:18 AM

View PostSkydiver, on 25 December 2012 - 09:41 PM, said:

Doesn't the short circuiting cause confusion for people reading the code and who don't know that the language does short circuiting?


If someone is reading any expression and doesn't understand the order of operations, they're going to be confused. e.g. 2 + 3 * 4.

The boolean thing reenforces the logic of the operation. With an OR, only one TRUE is required. Even a computer is smart enough realized it needn't go any farther after hitting a TRUE. We expect programmers to be a little ahead of their digital slaves.

If you are confused by the code you wrote to handle your own boolean expression, then you've simply written it wrong. e.g.
if (current == null) {
   // this knows exactly what the variable contains
} else if(current->nextPtr == endPtr) {
   // so does this
}


Was This Post Helpful? 2
  • +
  • -

#26 jon.kiparsky  Icon User is online

  • Pancakes!
  • member icon


Reputation: 7744
  • View blog
  • Posts: 13,084
  • Joined: 19-March 11

Re: Goto statement

Posted 26 December 2012 - 06:55 AM

Quote

consider the semantics of short circuiting the evaluation of Boolean expressions. Isn't that there an implied 'goto' present?


Short answer: no. There's flow control happening, but again it's regular and the destination is a condition of the instruction, not a free choice of the programmer. It's a confusing idiom, sure, and it's one that I think should be avoided, but it's not a goto.
Was This Post Helpful? 1
  • +
  • -

#27 Skydiver  Icon User is offline

  • Code herder
  • member icon

Reputation: 3574
  • View blog
  • Posts: 11,112
  • Joined: 05-May 12

Re: Goto statement

Posted 26 December 2012 - 07:18 AM

+1 to what baavgai said. We expect some intelligence in those who will come after us and read our code. We expect them to be able to parse out expressions, but we don't expect them to trace through complex logic that may cause jumping through multiple contexts (either real code contexts, or virtual semantic contexts).

I avoid goto's whenever I can, but there are some cases where it's what makes sense at the time. Personally, I would rather refactor code to avoid the goto, but if the program is meant to ship the next day and it's a choice between a 2 line code fix that adds a goto, and only a single module recompile, or a 50 line code change that will require recompiling the world, it's pretty clear which choice management will support during the bug triage.

This post has been edited by Skydiver: 26 December 2012 - 07:25 AM

Was This Post Helpful? 0
  • +
  • -

#28 jon.kiparsky  Icon User is online

  • Pancakes!
  • member icon


Reputation: 7744
  • View blog
  • Posts: 13,084
  • Joined: 19-March 11

Re: Goto statement

Posted 26 December 2012 - 07:43 AM

View PostSkydiver, on 26 December 2012 - 09:18 AM, said:

+1 to what baavgai said. We expect some intelligence in those who will come after us and read our code. We expect them to be able to parse out expressions, but we don't expect them to trace through complex logic that may cause jumping through multiple contexts (either real code contexts, or virtual semantic contexts).


I think we're all in violent agreement on this one. There's a baseline standard that we hold our fellow programmers to, and it includes both "write sensible, correct code" and "read sensible code correctly".

Quote

I avoid goto's whenever I can, but there are some cases where it's what makes sense at the time. Personally, I would rather refactor code to avoid the goto, but if the program is meant to ship the next day and it's a choice between a 2 line code fix that adds a goto, and only a single module recompile, or a 50 line code change that will require recompiling the world, it's pretty clear which choice management will support during the bug triage.


Yes, if you're behind that particular eight-ball, that's what happens. This is basically the case for the "get it right the first time" approach. Yes, you can hack something together and get it to run, and yes you'll intend to go back and make it right later, but management always cuts out the "get it right" phase. Therefore, make sure that it's acceptable on the inside before you present it as acceptable on the outside.

Where I am, I sit on the border between project management and development - I actually fix bugs and work on the code, but I'm also in the meetings that the developers aren't in - and I can tell you that this phenomenon is not an accident. Management will literally say "they say they want more time to fix all of these bugs, but it looks like it works, so we're moving on". They really think that fixing this stuff is just silly perfectionism.

The safest rule is to assume that anything you give them will be shipped as a final release, and never give them code that needs to be fixed up on the inside unless the outside is broken as well.
Was This Post Helpful? 2
  • +
  • -

#29 baavgai  Icon User is offline

  • Dreaming Coder
  • member icon

Reputation: 5829
  • View blog
  • Posts: 12,683
  • Joined: 16-October 07

Re: Goto statement

Posted 26 December 2012 - 08:29 AM

View Postjon.kiparsky, on 26 December 2012 - 09:43 AM, said:

The safest rule is to assume that anything you give them will be shipped as a final release, and never give them code that needs to be fixed up on the inside unless the outside is broken as well.


Yep. Agree. 100%.

Stupidest idea ever: Just make a visual prototype they can see. With forms and buttons, so they can get an idea of what the final version will look like. The mock up is made and presented to great enthusiasm, immediately followed by "is it done yet?" It's a facade: the real work hasn't even been started. But they've "seen" it now. They know it's "almost" done.

I have let beta versions escape me. For testing purposes only, final is weeks away. Only to be diverted and have that beta go into production for years. The temporary solution is rarely, if ever, as temporary as you intended.
Was This Post Helpful? 0
  • +
  • -

#30 vividexstance  Icon User is offline

  • D.I.C Lover
  • member icon

Reputation: 659
  • View blog
  • Posts: 2,270
  • Joined: 31-December 10

Re: Goto statement

Posted 26 December 2012 - 09:58 AM

In the book "C++ Coding Standards 101 Rules, Guidelines, and Best Practices" by Herb Sutter and Andrei Alexandrescu, rule #6 is "Correctness, simplicity, and clarity come first." They have a quote that I really like:

Quote

Programs must be written for people to read, and only incidentally for machines to execute.

by Harold Abelson and Gerald Jay Sussman
Was This Post Helpful? 2
  • +
  • -

  • (3 Pages)
  • +
  • 1
  • 2
  • 3