Register a SA Forums Account here!
JOINING THE SA FORUMS WILL REMOVE THIS BIG AD, THE ANNOYING UNDERLINED ADS, AND STUPID INTERSTITIAL ADS!!!

You can: log in, read the tech support FAQ, or request your lost password. This dumb message (and those ads) will appear on every screen until you register! Get rid of this crap by registering your own SA Forums Account and joining roughly 150,000 Goons, for the one-time price of $9.95! We charge money because it costs us money per month for bills, and since we don't believe in showing ads to our users, we try to make the money back through forum registrations.
 
  • Post
  • Reply
Lexical Unit
Sep 16, 2003

Contero posted:

Is there some nanosleep or usleep equivalent in windows? Milliseconds are just slightly too coarse.
Earlier this year there was some discussion on the boost mailing list about timers which led me to this site. I was particularly impressed with the author's effort to document the pros and cons of all the available time devices his library can support. It looks like you might be able to use QueryPerformanceCounter() to get a high resolution timer, but it's dependent on your machine having a high resolution performance counter. I don't know if his library is any good as I haven't personally used it, but regardless, check out QueryPerformanceCounter() if you haven't already. Beyond that, I'm afraid I just don't know.

Adbot
ADBOT LOVES YOU

Avenging Dentist
Oct 1, 2005

oh my god is that a circular saw that does not go in my mouth aaaaagh

Lexical Unit posted:

Earlier this year there was some discussion on the boost mailing list about timers which led me to this site. I was particularly impressed with the author's effort to document the pros and cons of all the available time devices his library can support. It looks like you might be able to use QueryPerformanceCounter() to get a high resolution timer, but it's dependent on your machine having a high resolution performance counter. I don't know if his library is any good as I haven't personally used it, but regardless, check out QueryPerformanceCounter() if you haven't already. Beyond that, I'm afraid I just don't know.

I'm pretty sure most of that page is lies. QueryPerformanceCounter, by definition, measures wall time, so unless you have faulty hardware, CPU frequency scaling won't affect it.

Lexical Unit
Sep 16, 2003

I thought QPC() measured counts by definition, and getting to wall time was a matter of discovering the frequency of those counts via QueryPerformanceFrequency(), no? Maybe he's saying that CPU frequency scaling affects QPF() (or possibly that it doesn't when it should). Regardless, after googling for a while I've gotten the impression that QPC() is rubbish for a lot of reasons, even if it is the Microsoft endorsed way of measuring nanosecond resolution time. Sucks.

Olly the Otter
Jul 22, 2007

Pookdaddy G posted:

Ok, so now i can actually see my full output. I noticed that i can find URL given the IP, but if given the URL i cannot find the IP. This leads me to think that my error is within
code:
else//Its a URL, not a IP
{
differ = searchtable(IP, length, first);
if (differ == -7)//Seintinal Logic
cout<< "Sorry, not found."<< endl;//Cant find
else
cout<< "Found IP Number " << IP[differ] << endl;//Outputs the IP
}
Am i looking in the right spot?

Edit: OK, i changed it to say "differ != 7). This works so it says it found the IP number, but now i am having to figure out how to have it cout the IP[differ] which is being stored as a "".

Yes, you're looking in the right spot. No, you shouldn't change it to "if (differ != 7)", that's not the part that has a problem.

You know that it's working for the IP lookups but not for the name lookups. So you should compare the two and see what you're doing differently (or, more to the point, what you're not doing differently), and you'll see what the problem is.

Good luck!

Pookdaddy G
Jun 13, 2007

Olly the Otter posted:

Yes, you're looking in the right spot. No, you shouldn't change it to "if (differ != 7)", that's not the part that has a problem.

You know that it's working for the IP lookups but not for the name lookups. So you should compare the two and see what you're doing differently (or, more to the point, what you're not doing differently), and you'll see what the problem is.

Good luck!

But when i change "the if (differ == 7)" back to that, then the file will get only to the "first process =" then cause a huge error.
EDIT: Wait, nevermind, that was because of something else i changed.
EDIT 2: I got it, i think... Please tell me that i was right in changing" differ = searchtable(IP, length, first);" to "differ = searchtable(URL, length, first);"

Pookdaddy G fucked around with this message at 05:31 on Dec 12, 2008

Olly the Otter
Jul 22, 2007

Pookdaddy G posted:

But when i change "the if (differ == 7)" back to that, then the file will get only to the "first process =" then cause a huge error.
EDIT: Wait, nevermind, that was because of something else i changed.
EDIT 2: I got it, i think... Please tell me that i was right in changing" differ = searchtable(IP, length, first);" to "differ = searchtable(URL, length, first);"

That is correct.

Pookdaddy G
Jun 13, 2007

Olly the Otter posted:

That is correct.

gently caress yeah! Thanks so much man, you have probably just made my day, if not week.

Avenging Dentist
Oct 1, 2005

oh my god is that a circular saw that does not go in my mouth aaaaagh

Lexical Unit posted:

I thought QPC() measured counts by definition, and getting to wall time was a matter of discovering the frequency of those counts via QueryPerformanceFrequency(), no? Maybe he's saying that CPU frequency scaling affects QPF() (or possibly that it doesn't when it should). Regardless, after googling for a while I've gotten the impression that QPC() is rubbish for a lot of reasons, even if it is the Microsoft endorsed way of measuring nanosecond resolution time. Sucks.

I don't see how it's crap, except that it has more overhead than some (but not all) other methods of measuring time. QueryPerformanceFrequency is required to return the same value each time for a given hardware configuration, and QueryPerformanceCounter is based on wall time. The only legitimate complaint I know of is that QPF/QPC doesn't work too well when dealing with buggy hardware/BIOSes.

There's a lot of misinformation about QPC/QPF (I'm probably victim to some of it, myself!).

Contero
Mar 28, 2004

Lexical Unit posted:

Earlier this year there was some discussion on the boost mailing list about timers which led me to this site. I was particularly impressed with the author's effort to document the pros and cons of all the available time devices his library can support. It looks like you might be able to use QueryPerformanceCounter() to get a high resolution timer, but it's dependent on your machine having a high resolution performance counter. I don't know if his library is any good as I haven't personally used it, but regardless, check out QueryPerformanceCounter() if you haven't already. Beyond that, I'm afraid I just don't know.

Well that's all well and good. I'm already using QPC for my timer, but I was asking about Sleep() calls. I'm just wondering if windows has a way of suspending the process/thread for an amount of time in chunks less than 1 millisecond.

I'm really trying to figure out how I can get my little game to stay at a steady and reliable 60 frames per second. I originally thought I should do this by sleeping for exactly the time needed, but I'm starting to think that I should just sleep for 1 ms every time, and only draw when QPC says the last time I drew to the screen was more than 16.6 milliseconds ago.

Avenging Dentist
Oct 1, 2005

oh my god is that a circular saw that does not go in my mouth aaaaagh
Sleep isn't a reliable way to do that anyway, since there's no guarantee that you will Sleep for precisely the length of time you request (usually it's a few ms more). What you want to do is to enable vertical sync through your API. I'm not really familiar with OpenGL, but in DirectX, you'd set D3DPRESENT_PARAMETERS::PresentationInterval to D3DPRESENT_INTERVAL_ONE. This has the effect of blocking all calls to ID3DDevice9::Present until the next draw update on the monitor.

Apparently, using GLFW (a framework for OpenGL), you can call glfwSwapInterval(1) to turn on vsync. There's also wglSwapInterval, which I gather is a Windows extension in OpenGL.

Avenging Dentist fucked around with this message at 09:14 on Dec 12, 2008

newsomnuke
Feb 25, 2007

Whilst farting I posted:

:words:

I'd have a really clear idea on how to do the entire sort, but to do it with recursion is confusing the poo poo out of me.
The idea behind quicksort is that you sort the data by swapping it around in-place. By splitting it up into two separate lists (less than and greater than pivot), you need to recombine the lists, which is closer to mergesort (which is actually the best way of sorting lists). The trick here is to pass two iterators into the function, which define the start and end of the (sub)list that you want to sort, rather than the actual list itself. Then recursion is simply a matter of updating where those iterators point to, and passing them back in. Don't forget to check for the termination condition where startIterator == endIterator.

Rottbott
Jul 27, 2006
DMC

Avenging Dentist posted:

I don't see how it's crap, except that it has more overhead than some (but not all) other methods of measuring time. QueryPerformanceFrequency is required to return the same value each time for a given hardware configuration, and QueryPerformanceCounter is based on wall time. The only legitimate complaint I know of is that QPF/QPC doesn't work too well when dealing with buggy hardware/BIOSes.

There's a lot of misinformation about QPC/QPF (I'm probably victim to some of it, myself!).
I've found that the reported time can jump backwards on some dual core systems when the thread changes core. The only workaround we found that worked was setting the thread affinity to a particular core before the QPC call.

beuges
Jul 4, 2005
fluffy bunny butterfly broomstick
Some info on QPC/QPF:

http://blogs.msdn.com/oldnewthing/archive/2007/07/05/3695356.aspx
http://blogs.msdn.com/oldnewthing/archive/2008/09/08/8931563.aspx

Whilst farting I
Apr 25, 2006

ultra-inquisitor posted:

The idea behind quicksort is that you sort the data by swapping it around in-place. By splitting it up into two separate lists (less than and greater than pivot), you need to recombine the lists, which is closer to mergesort (which is actually the best way of sorting lists). The trick here is to pass two iterators into the function, which define the start and end of the (sub)list that you want to sort, rather than the actual list itself. Then recursion is simply a matter of updating where those iterators point to, and passing them back in. Don't forget to check for the termination condition where startIterator == endIterator.

I guess I'm just confused about what'll happen when I recurse if I have a loop in there that splits up the initial list A into two separate lists. Every time I call it, it'll be splitting it into two different lists.

newsomnuke
Feb 25, 2007

Whilst farting I posted:

I guess I'm just confused about what'll happen when I recurse if I have a loop in there that splits up the initial list A into two separate lists. Every time I call it, it'll be splitting it into two different lists.
You'll recursively split the list into lots of lists of size 0, and then the function stack will 'unwind' and you'll need to merge the lists back together. If you want to do quicksort that way, then your function will look like this:

code:
void quickSort(list<T> &input)
{
    if(input.size() < 2)
        return;

    T pivot = input.front(); input.pop_front();
    // Partition into 2 lists using pivot value
    // ...

    // Recurse
    quickSort(newList1);
    quickSort(newList2);

    // Merge two lists and pivot back into input
    // ...
}
That check at the beginning is crucial, because it stops infinite recursion (or in this case, actually you'll get a crash when trying to pop from an empty list). Recursion is tricky to get your head around, it's just a matter of doing lots of examples, and eventually you begin to see patterns. It's very helpful to manually go through an example and write out the call stack.

edit: it's worth pointing out of course that doing quicksort this way is really an inefficient version of mergesort, one of the strengths of quicksort is that it can be done in-place.

newsomnuke fucked around with this message at 16:43 on Dec 12, 2008

ehnus
Apr 16, 2003

Now you're thinking with portals!

Rottbott posted:

I've found that the reported time can jump backwards on some dual core systems when the thread changes core. The only workaround we found that worked was setting the thread affinity to a particular core before the QPC call.

QPC was reportedly fixed for Vista but at a huge execution cost. It already took thousands of cycles to execute on XP and it's much more expensive now

Whilst farting I
Apr 25, 2006

ultra-inquisitor posted:

You'll recursively split the list into lots of lists of size 0, and then the function stack will 'unwind' and you'll need to merge the lists back together. If you want to do quicksort that way, then your function will look like this:

That check at the beginning is crucial, because it stops infinite recursion (or in this case, actually you'll get a crash when trying to pop from an empty list). Recursion is tricky to get your head around, it's just a matter of doing lots of examples, and eventually you begin to see patterns. It's very helpful to manually go through an example and write out the call stack.

edit: it's worth pointing out of course that doing quicksort this way is really an inefficient version of mergesort, one of the strengths of quicksort is that it can be done in-place.

Ahhh, thanks! Recursion is always something that has kind of escaped me, and I knew it bore some really striking similarities to mergesort but I hadn't quite realized how. :)

I wrote something that I think should otherwise work, but now I'm getting compiler errors at the recursive calls and I'm unsure of the syntax needed

code:
void quicksort(SinglyLL<T> & A, bool (* needSwap)(T &, T &))
{
    SinglyLL<T> befList;
    SinglyLL<T> piv;
    SinglyLL<T> aftList;

// ...

    typename SinglyLL<T>::iterator itr = piv.begin();
    typename SinglyLL<T>::iterator itr2;
    typename SinglyLL<T>::iterator itr3;

    for (itr2 = A.begin(); itr2 != A.end(); ++itr2)
    {
       if ((*needSwap)(*itr, *itr2))
       // ...
     }

     itr2 = befList.begin();
     itr3 = aftList.begin();
	
     quicksort(befList, needSwap(*itr,*itr2));
     quicksort(aftList, needSwap(*itr3,*itr));
}
Here's what it gives me

quote:

listSort.cpp In function `void quicksort(SinglyLL<T>&, bool (*)(T&, T&)) [with T = std::vector<std::string, std::allocator<std::string> >*]':
109 main.cpp instantiated from here
89 listSort.cpp no matching function for call to `quicksort(SinglyLL<std::vector<std::string, std::allocator<std::string> >*>&, bool)'
90 listSort.cpp no matching function for call to `quicksort(SinglyLL<std::vector<std::string, std::allocator<std::string> >*>&, bool)'

This is the line in the main it's complaining about

code:
quicksort(list3, needSwap2);
and this is needSwap2's parameters

code:
bool needSwap2(vector<string>* & vec1, vector<string>* & vec2)
I've tried variations like

(needSwap)(*itr,*itr2)
(*needSwap)(*itr,*itr2)
(needSwap)(itr,itr2)

but nothing I've tried will not give me an error for those two recursive calls. I'm admittedly very new to function pointers, and I'm absolutely clueless as to what the syntax should be.

Whilst farting I fucked around with this message at 21:35 on Dec 12, 2008

Fifty-Nine
Oct 15, 2003
You're dereferencing needSwap when you do needSwap(a,b), which calls the function pointed to by needSwap and yields the return value (a boolean). You probably just want to pass the value of the pointer directly (i.e., without dereferencing).

E.g.:
code:
quicksort(befList, needSwap);
quicksort(aftList, needSwap);

floWenoL
Oct 23, 2002

ultra-inquisitor posted:

edit: it's worth pointing out of course that doing quicksort this way is really an inefficient version of mergesort, one of the strengths of quicksort is that it can be done in-place.

I don't understand why you think it's like mergesort. There's no merging that needs to be done as you know one of the lists is sorted and less than the pivot, and the other is sorted and greater than the pivot, and concatenating two lists together is a simple constant-time operation.

Edit:
In fact, if you're careful to work only with node pointers you can quicksort "in-place" in a different sense, and if T is a large object it might actually be faster than quicksorting an array.

floWenoL fucked around with this message at 22:35 on Dec 12, 2008

newsomnuke
Feb 25, 2007

floWenoL posted:

I don't understand why you think it's like mergesort. There's no merging that needs to be done as you know one of the lists is sorted and less than the pivot, and the other is sorted and greater than the pivot, and concatenating two lists together is a simple constant-time operation.
Well it's like mergesort in that you're splitting a list, sorting and then merging. But you're right, when I posted that I forgot that the sublists are sorted by the partitioning, not by the merging.

Whilst farting I
Apr 25, 2006

Ack, I'm confused all over again. :(

code:
void quicksort(SinglyLL<T> & A, bool (* needSwap)(T &, T &))
{
    if (A.hasOneElement() || A.isEmpty()
        return;    

    SinglyLL<T> befList;
    SinglyLL<T> piv;
    SinglyLL<T> aftList;

// ...

    typename SinglyLL<T>::iterator itr = piv.begin();
    typename SinglyLL<T>::iterator itr2;

    for (itr2 = A.begin(); itr2 != A.end(); itr2 = A.begin())
    {
       if ((*needSwap)(*itr, *itr2))
       // ...
    }
	
     quicksort(befList, needSwap);
     quicksort(aftList, needSwap);

     cout << "does it ever reach this point\n";

     A.appendandclear(befList);
     A.appendandclear(piv);
     A.appendandclear(aftList);
}
It always just returns every time a list has one element or 0 elements, it never outputs that statement toward the bottom and thus never merges the lists.

Recursion :argh::eng99:

floWenoL
Oct 23, 2002

Whilst farting I posted:

It always just returns every time a list has one element or 0 elements,

Isn't that what you want? It's what the top of your code does, anyway.

Also, your for loop simply infinite-loops.

Whilst farting I
Apr 25, 2006

floWenoL posted:

Isn't that what you want? It's what the top of your code does, anyway.

Also, your for loop simply infinite-loops.

The for-loop determines which list to place each node in - but the function it uses to do that pops the head off of list A and places it in another list, so the head is constantly changing and it's necessary to reset it each time through. It doesn't loop infinitely.

Isn't the case of 1 or 0 elements the base case? I'm not sure how to address that in the context of this function. That's what I'm confused about, every quicksort/mergesort algorithm I've seen requires that.

newsomnuke
Feb 25, 2007

Whilst farting I posted:

Isn't the case of 1 or 0 elements the base case? I'm not sure how to address that in the context of this function. That's what I'm confused about, every quicksort/mergesort algorithm I've seen requires that.
You're doing the recursion correctly, it's almost certainly a bug in your for-loop (can't tell because you haven't posted the full code). Put some prints in to make sure it's doing what you expect.

Also you don't need a whole list for the pivot, just a simple T will do.

Whilst farting I
Apr 25, 2006

ultra-inquisitor posted:

You're doing the recursion correctly, it's almost certainly a bug in your for-loop (can't tell because you haven't posted the full code). Put some prints in to make sure it's doing what you expect.

Also you don't need a whole list for the pivot, just a simple T will do.

I did put some prints in the for-loop, it's doing what I expect.

It turns out that my problem was in my append function - there was an error in the compiler that I misread as an unused variable warning. :nyoron:

Now my problem is that the list it's supposed to be returning by reference is returned as totally, completely empty. It has 0 elements.

Here's my full code.

code:
void quicksort(SinglyLL<T> & A, bool (* needSwap)(T &, T &))
{
    if (A.hasOneElement() || A.isEmpty())
        return;    

    SinglyLL<T> befList;
    SinglyLL<T> piv;
    SinglyLL<T> aftList;

    // Pops the front off of A, pushes it to the front of the list "piv"
    A.pop_front_push_front_elsewhere(piv);

    typename SinglyLL<T>::iterator itr = piv.begin();
    typename SinglyLL<T>::iterator itr2;

    for (itr2 = A.begin(); itr2 != A.end() && A.size() > 0; itr2 = A.begin())
    {
       // If the pivot's value is greater than the value in the beginning of the list, 
       // pop the value off of A and push it to the front of the before list
       if ((*needSwap)(*itr, *itr2))
          A.pop_front_push_front_elsewhere(befList);

       // Otherwise, it comes after A. Values equal to the pivot are treated as greater than, as well
       else
          A.pop_front_push_front_elsewhere(aftList);
    }
	
     quicksort(befList, needSwap);
     quicksort(aftList, needSwap);

     cout << "Done with recursive call, about to appendandclear\n";

     A.appendandclear(befList);
     A.appendandclear(piv);
     A.appendandclear(aftList);
}
The pivot value needs to be in its own list so they can all be appended one at a time to A. First everything before it, then the pivot, then everything after it. I'm beginning to think my problem lies with the appendandclear function - I threw a bunch of debug statements in my code and run a lot of tests.

pop_front_push_front_elsewhere seems to work just fine - if I comment out everything after I first pop off the front after the variable declarations, quicksort will return the list passed by reference, and its size is appropriate.

Let's say I give it the following numbers in a certain type of list:

53327
348554
54832
82435
90560

it will return

348554
54832
82435
90560

So the front was popped off, and the loop ran until its tail, 4 times, and didn't segfault, so it looks like the head and tail are being set appropriately.

But if I run it with the full code, here's what the debug statements output (I didn't put them for when I pop off the pivot, should I? that seems to work okay, considering that it verifies that it pops off 53327, which would make 348554 bigger than everything else, so its sorting is correct)

quote:

after! size of A is 3 and size of after is 1
after! size of A is 2 and size of after is 2
after! size of A is 1 and size of after is 3
after! size of A is 0 and size of after is 4
List size is 1 or 0! Returning

before! size of A is 2 and size of before is 1
before! size of A is 1 and size of before is 2
after! size of A is 0 and size of after is 1
after! size of A is 0 and size of after is 1
List size is 1 or 0! Returning

List size is 1 or 0! Returning

Done with recursive call, about to appendandclear

Entered appendandclear
Entered appendandclear
Entered appendandclear
List size is 1 or 0! Returning

Done with recursive call, about to appendandclear

Entered appendandclear
Entered appendandclear
Entered appendandclear
Done with recursive call, about to appendandclear

Entered appendandclear
Entered appendandclear
Entered appendandclear
sort time: 0

Normally the list is supposed to be printed before it says the sort time - but it seems to think the list is empty. It'll print just fine if I comment out everything after I pop the pivot, it'll print the last 4 numbers listed above.

Here's my code for appendandclear.

code:
template <typename T>
void SinglyLL<T>::appendandclear(
    SinglyLL<T> & y)
{	
        // Temp head node
        Node *tempHeadNode = new Node;

        cout << "Entered appendandclear" << endl;

        // Saves the original head of the list to be reset at the end of the function
        tempHeadNode = head;

        // Sets the tail to be equal to 0
        tail = 0;

        // Set the head to be equal to 0 - to trick the list when
        // pop_front_push_front_elsewhere is called into thinking it's pushing things onto the front,
        // when really, it's pushing them onto the back
        head = tail;

	// Pops each element off y and pushes it onto the "front" of the current list
        for (int i = 0; i < y.size(); i++)
		y.pop_front_push_front_elsewhere(*this);

	// Resets the head node to the real head
        head = tempHeadNode;
}
I'm thinking it has something to do with how I'm resetting the private members but I don't understand where it's going wrong. I'm not sure if I'm losing the tail or what.

newsomnuke
Feb 25, 2007

Whilst farting I posted:

code:
        Node *tempHeadNode = new Node;
        tempHeadNode = head;
This is a memory leak, there's no need to create a new Node here, just do Node *tempHeadNode = head. appendandclear() shouldn't need to fiddle around with its internal pointers like this, pop_front_push_front_elsewhere() should handle everything on its own - it does this in the swap loop, why not in appendandclear?

Whilst farting I
Apr 25, 2006

ultra-inquisitor posted:

This is a memory leak, there's no need to create a new Node here, just do Node *tempHeadNode = head. appendandclear() shouldn't need to fiddle around with its internal pointers like this, pop_front_push_front_elsewhere() should handle everything on its own - it does this in the swap loop, why not in appendandclear?

Because append needs to append one list to another, and there's no push back function - only a push front function. I need to trick the list into thinking the tail of one list is actually its front, so when it pushes to the "front" it's actually pushing it back.

newsomnuke
Feb 25, 2007

After you've finished the for loop, you've popped every item off A, so A is empty. Thus when you run appendandclear(), head is going to be NULL. And so tempHeadNode will be NULL, and after appending all the items onto A, you then set head back to NULL again, losing all the items.

Whilst farting I
Apr 25, 2006

You're right - I've added some conditions to check for empty lists being appended/dealt with, and the list isn't returned as empty anymore, but it does return the lowest element in the list.

quote:

after! size of A is 3 and size of after is 1
after! size of A is 2 and size of after is 2
after! size of A is 1 and size of after is 3
after! size of A is 0 and size of after is 4
List size is 1 or 0! Returning

before! size of A is 2 and size of before is 1
before! size of A is 1 and size of before is 2
after! size of A is 0 and size of after is 1
after! size of A is 0 and size of after is 1
List size is 1 or 0! Returning

List size is 1 or 0! Returning

Done with recursive call, about to appendandclear

Size of current list is 0 and size of y is 1
Size of current list is 1 and size of y is 1
List size is 1 or 0! Returning

Done with recursive call, about to appendandclear

Size of current list is 0 and size of y is 1
Size of current list is 1 and size of y is 1
Size of current list is 1 and size of y is 1
Done with recursive call, about to appendandclear

Size of current list is 0 and size of y is 1
Size of current list is 1 and size of y is 1
52327
sort time: 0

I played around with appending a bit, and discovered that the list the sort returns is actually just the very first item appended to it - if I append the after list first, the list that returns will consist just of the highest element in the list, no matter what I append after it. I'm somehow keeping only the first list/element I append while losing everything else. So this tells me that the sort works, and that I've got major problems with how I'm setting my head and tail.

Here's how I've modified appendandclear

code:
template <typename T>
void SinglyLL<T>::appendandclear(SinglyLL<T> & y)
{	
	// Checks to make sure an empty list isn't being appended
	if (!y.empty())
	{
		// Holding variable for the head in case it needs to be reset
		Node *tempHeadNode = head;
		bool notEmpty = false;

		cout << "Size of current list is " << size() << " and size of y is " << y.size() << endl;
		
		// If the original list to which we're appending is not empty to begin 
                // with, flag it as such so we know to restore the head
		if (!empty())
			notEmpty = true;

		// Sets the tail to be equal to 0 so the first item pushed in 
                // becomes the tail
		tail = 0;

		// Pushes each item from one list onto the other
                for (int i = 0; i < y.size(); i++)
                   y.pop_front_push_front_elsewhere(*this);

                // If the list wasn't empty when passed in, restore the head
                if (notEmpty)
                   head = tempHeadNode;
	}
}
I think the problem is that I'm losing the tail, and that's why the list keeps getting reset once it reaches more than one element. I'm not sure exactly how it is that I can prevent losing the tail, though. I don't really know how to explain it without showing the other two core functions, so here they are.

code:
// Pops the front node off of the current list and pushes it to the front of list y
template <typename T>
void SinglyLL<T>::pop_front_push_front_elsewhere(SinglyLL<T> & y)
{
    // Check to make sure there is something to pop
    if (!empty())
    {
        // Creates new nodes and T values for storage of the head's
        // information
        Node *popped = head;
        T poppedVal = head->value;

        // If there's only one element left in the list, it's going to be popped off,
	// so make the list empty (the entirety of the empty function is 
        // { return head == 0; }
        if (oneElement())
           head = 0;

        // Otherwise, there is a next node, so set the head equal to that
        else
           head = head->next;

        // Pushes the head of the first list onto the second list
        y.push_front(poppedVal);

        // Deletes the head node - "pops" it
        delete popped;
    }
}
code:
template <typename T>
void SinglyLL<T>::push_front(const T & x)
{
    Node *p = new Node;

    p->value = x;
    p->next = head;
    head = p;

    if (tail == 0)
        tail = head;
}
If the tail equals 0, then it's supposed to be set equal to the head so if the list is empty, the very first element getting pushed on becomes the tail, because everything is getting pushed in front of it. But since I'm only going to be appending and clearing lists that have only one element, the head will ALWAYS be equal to the tail, and even if it did have more than one element, I'd probably wind up losing one or several nodes with how everything else is written.

How to approach this should be blindingly obvious but it's not coming to me. I know it has something to do with how I'm "appending" one list to another and how I'm setting the head and tail in that function but I can't get any further.

Whilst farting I fucked around with this message at 04:14 on Dec 14, 2008

Jo
Jan 24, 2005

:allears:
Soiled Meat
Somewhere in my loving huge (tm) operating systems project, a there is a memblock->next = memblock. I can't do a simple search through the text, as the assignment happens when the program is running. (Lots of pointer dumbfuckery and the like.) What tools can I use other than gdb and ddd exist to help with this screaming headache?

Avenging Dentist
Oct 1, 2005

oh my god is that a circular saw that does not go in my mouth aaaaagh

Jo posted:

I can't do a simple search through the text, as the assignment happens when the program is running.

All assignments happen when a program is running. :confused: (Excluding const PODs, etc).

You could just write a regex and filter the results manually. Unless you're dealing with several million lines of code, this shouldn't be too bad.

Alternately, you could use something like Intellisense and use "Find Symbol".

TheSleeper
Feb 20, 2003
How many places in your code are you actually assigning to memblock->next? I mean would it be reasonable to just assert(&memblock != &memblockToBeNext) before the assignment and backtrack from there to find the bug?

Jo
Jan 24, 2005

:allears:
Soiled Meat

Avenging Dentist posted:

All assignments happen when a program is running. :confused: (Excluding const PODs, etc).

Ehr, yes. That was poorly phrased. The assignment is the end result of a series of operations when I run the application. I.e. a->next = b; b->next = c; c->next = d; d->next = a; Remove c, b->next = d; Remove b, a->next = d; Remove d, a->next = a;

The bug doesn't arise in small scale tests. It's large-scale 5 minute long stress tests that make it pop up. :argh:

In a perfect world, I'd like to be able to set up a monitor that detects when the assignment obj->next = obj is made. Is this possible?

TheSleeper posted:

How many places in your code are you actually assigning to memblock->next? I mean would it be reasonable to just assert(&memblock != &memblockToBeNext) before the assignment and backtrack from there to find the bug?

Only a few, and there are a number of checks in place to catch that sort of thing; making it all the more maddening when it shows up.

Jo fucked around with this message at 05:42 on Dec 14, 2008

newsomnuke
Feb 25, 2007

Whilst farting I posted:

How to approach this should be blindingly obvious but it's not coming to me. I know it has something to do with how I'm "appending" one list to another and how I'm setting the head and tail in that function but I can't get any further.
appendandclear() seems to be the simplest thing to change. First you need to check whether the list you're appending onto is empty. If it is, pop_front_push_elsewhere the first element. Then, just loop through and do the rest normally:

code:
template <typename T>
void SinglyLL<T>::appendandclear(SinglyLL<T> & y)
{	
    // Make sure this list is not empty before we start messing with head
    if (empty())
        y.pop_front_push_front_elsewhere(*this);

    Node *oldHead = head;
    while (!y.empty())
    {
        // Set head to 0 so that new element->next points to 0 (ie is tail)
        head = 0;

        // This sets head to point to the new element...
        y.pop_front_push_front_elsewhere(*this);

        // So we set the end of the tail to the new element like so:
        tail->next = head;
        tail = head;
        // The new element's next node is presumably created as 0, otherwise set
        // it manually.
        //tail->next = 0;
    }

    head = oldHead;
}

Shryke
Jan 11, 2007

Hi, I've got a month to program a sequencer in visual c++ with no prior knowledge of the language. It has to take an input such as a string of chosen notes, and play it out through midi out.

Can anyone give me any general tips on how I might go about this? I have a book that should help me pick up what I need to do this. Expect to see me in this thread quite a bit!

theg sprank
Feb 3, 2008
pillage your village
I have a question for C++ language nazis.

Clearly it's unacceptable to call a virtual method on a NULL pointer. Is it acceptable to call a non-virtual method on a NULL pointer, from the standpoint of the language definition? It happens to work on g++, but I want to know whether it is required by the standard to work.

many thanks

Avenging Dentist
Oct 1, 2005

oh my god is that a circular saw that does not go in my mouth aaaaagh

theg sprank posted:

I have a question for C++ language nazis.

Clearly it's unacceptable to call a virtual method on a NULL pointer. Is it acceptable to call a non-virtual method on a NULL pointer, from the standpoint of the language definition? It happens to work on g++, but I want to know whether it is required by the standard to work.

many thanks

It doesn't even work in GCC, at least not for any reasonable value of "works":

http://codepad.org/H8lxLv1U

Steampunk Mario
Aug 12, 2004

DIAGNOSIS ACQUIRED

theg sprank posted:

I have a question for C++ language nazis.

Clearly it's unacceptable to call a virtual method on a NULL pointer. Is it acceptable to call a non-virtual method on a NULL pointer, from the standpoint of the language definition? It happens to work on g++, but I want to know whether it is required by the standard to work.

many thanks

I'm not sure about the standard, but in all the implementations that I've seen, it's safe if and only if the function doesn't access any member data (as in, use the this pointer). This basically makes it a static function though, so you would probably just refactor it that way. I'm sure the standard says it's undefined in general though, since it's a NULL deref technically.

theg sprank
Feb 3, 2008
pillage your village

Avenging Dentist posted:

It doesn't even work in GCC, at least not for any reasonable value of "works":

http://codepad.org/H8lxLv1U

Well yeah, obviously if you try to use member data it'll crash and burn.

Here's some background: my University has a series of courses for people who just want to learn a language. I tutor several of these courses, including the C++ one. I notice a lot of people writing recursive code whose base cases go something like
code:
...
if(this == NULL) {
  return;
}
...
Obviously I say "you probably don't want to do that because even though it works now, if you use it in a virtual method things will explode" but I was wondering if it's even allowed by the definition of the language.

Steampunk Mario posted:

I'm sure the standard says it's undefined in general though, since it's a NULL deref technically.

This is pretty much what I suspected, just looking for confirmation.

Adbot
ADBOT LOVES YOU

Avenging Dentist
Oct 1, 2005

oh my god is that a circular saw that does not go in my mouth aaaaagh
Quoth the Standard:

ISO/IEC 14882-1998 §5.2.5 #3 posted:

If E1 has the type "pointer to class X," then the expression E1->E2 is converted to the equivalent form (*(E1)).E2; ...

  • 1
  • 2
  • 3
  • 4
  • 5
  • Post
  • Reply