Don't spend time micromanaging optimization options in Visual C++

— richardwb on Saturday, March 14, 2009 @ 14:37

It's surprisingly common to see people ask what the best Visual C++ optimization options are for their C++ code.

The best answer is to profile and benchmark.

Failing that, it's pretty simple. Use /O2 to optimize for speed, and /O1 to optimize for size. That's (almost) it. If you have a large program you will probably benefit more from optimizing for size:

Generally speaking, small applications should be compiled with /O2, and large applications should be compiled with /O1 because very large applications can end up putting a lot of stress on the instruction cache of the processor, and this can lead to worse performance. To minimize this, use /O1 to reduce the amount of "code bloat" introduced by the optimizer from certain transformations such a loop unrolling or selection of larger, faster sequences of code. [MSDN]

But what about Enable Intrinsic Functions (/Oi), Omit Frame Pointers (/Oy), etc.?

Check out the documentation for /O2 and /O1:

  • /O1 (Minimize Size) : Equivalent to /Og /Os /Oy /Ob2 /Gs /GF /Gy
  • /O2 (Maximize Speed) : Equivalent to /Og /Oi /Ot /Oy /Ob2 /Gs /GF /Gy

They are already all specified. Yes, the property pages say Disabled or No, but what they really mean is Default.

The only general optimization I'm aware of that doesn't fall in this category is Whole Program Optimization (/GL), which also requires Link Time Code Generation (/LTCG).

Finally, what about Full Optimization (/Ox)?

In general, /O2 should be preferred over /Ox and /O1 over /Oxs. [MSDN]

[Edit: Note that there is an IDE bug in Visual Studio 2008 which may affect whether or not optimizations are applied. I write more about it here.]

Comments

Several compilation options I use with C++ projects in Visual Studio

— richardwb on Thursday, March 12, 2009 @ 16:39

These are compilation options that I typically enable for any C++ projects in Visual Studio. They should go in the project's property settings, under Configuration Properties->C/C++->Command Line, in the Additional options textbox. They are listed here exactly as they should be entered in that box (equal signs and all).

  • /MP : This enables multi-process compilation, which can provide a significant speed boost on the multi-core processor you likely have in your development (or build) machine. As the MSDN documentation states, it is not compatible with the Enable Minimal Rebuild option, which, depending on the situation, may negate any performance advantage this switch has. Disabling minimal rebuild is not as bad as it sounds1 and in my projects I've found that using /MP confers a significant improvement in compilation time. You definitely want to enable it for Release builds, at least. Reference.
  • /D_CRT_SECURE_NO_WARNINGS : This switch disables the warnings that Visual Studio spits out if you use any of the C RunTime functions that Microsoft has provided secure replacements for (such as strncpy). This isn't necessarily a bad idea, but their safe functions are not part of the standard and are thus non-portable. Reference.
  • /D_SCL_SECURE_NO_WARNINGS : Just like the switch above, only for the Standard C++ Library (anything in the std namespace). Reference.
  • /D_SECURE_SCL=0 : Disables checked iterators. Disabling this feature can improve performance when using iterators, at the expense of less protection against unsafe iterator use. Generally there isn't too much of a performance difference (a Visual C++ lead states that they measured about a 6% difference in this feedback / bug page) so it may be worth leaving it on most of the time. Reference.

Note that /D_CRT_SECURE_NO_WARNINGS and /D_SCL_SECURE_NO_WARNINGS are /D_CRT_SECURE_NO_DEPRECATE and /D_SCL_SECURE_NO_DEPRECATE, respectively, in versions prior to Visual Studio 2008.


  1. Disabling Minimal Rebuild does not mean that the whole project gets rebuilt every time, just that Visual Studio uses a "was this .cpp/.h modified?" approach instead of trying to determine if the exact changes you made warrant a recompile. 

Comments

Stop wasting time stepping into functions you don't care about in Microsoft Visual Studio (C++)

— richardwb on Monday, March 09, 2009 @ 17:58

The Visual Studio debugger is amazingly useful. However, you soon learn to sigh the moment you come into contact with something like this:

boost::scoped_ptr<Startup> dlg(
    new Startup(settings->GetWindowPosition()));

You want to step into the Startup constructor, but there is a lot of stuff in your way.

  1. First you will step into shared_ptr::operator-> (did I mention that settings was a shared_ptr?).
  2. Then you will step into GetWindowPosition().
  3. Then into operator new.
  4. And then into your Startup constructor. Yes!
  5. And finally you'll finish with the scoped_ptr constructor if you so choose.

If you've done this a lot you've probably gotten really good at the Step Out shortcut (Shift-F11).

Well, here are two alternatives!

First, while program execution is paused, right-click anywhere in the source window and look for the Step Into Specific item. You can select any of these functions to instantly step inside.

Alternatively, there is a special undocumented (and unsupported) registry key that you can use to specify functions that you always want to skip. Navigate to HKLM\SOFTWARE\Microsoft\VisualStudio\9.0\NativeDE\StepOver (users of a 64-bit variant of Windows will want to check HKLM\SOFTWARE\Wow6432Node\Microsoft\... instead) and add some string values like this example:

Name: 50 (priority, higher values have greater priority)
Value: std\:\:.*=NoStepInto

Name: 60
Value: operator new=NoStepInto

Name: 70
Value: boost\:\:.*=NoStepInto

The backslashes are necessary because : (colon) is a special character in Visual Studio regex. You sometimes see two backslashes used in other examples, this is only necessary if you are directly editing a .reg file (which I don't recommend). These rules will adjust the behaviour of the Step-Into command and make it skip anything from the std or boost namespaces, as well as the basic operator new. It will still step into an overloaded operator new, so don't fear. There are more details on this blog post, as well as microsoft.public.vsnet.debugging.

One more thing: you can combine these two approaches. Say that you've added a skip for the entire boost namespace, but you decide you really want to take a peek inside one particular line and see exactly what is happening to your parameters. Pause execution on that line, and then right-click and use Step Into Specific. This is still the same list as before and it works the same as before, regardless of any NoStepInto rules.

Comments

Boost.Thread triggers warning C4103 in Microsoft Visual Studio

— richardwb on Monday, March 02, 2009 @ 17:28

Got a whole mess of warnings when I moved some of my includes around, particularly this one: #include <boost/thread.hpp>

abi_prefix.hpp(19) : warning C4103: 
'd:\\boost\\boost_1_38_0\\boost\\config\\abi_prefix.hpp' : 
alignment changed after including header, may be due to missing 
#pragma pack(pop)

Some experimentation reveals that including thread.hpp before winsock2.h triggers all these warnings. The simple solution is to just move #include <boost/thread.hpp> down below #include <winsock2.h>.

Comments

Building Boost (now with dual/quad/multi-core action!)

— richardwb on Monday, March 02, 2009 @ 16:27

Update: More recent versions of Boost (1.39.0 and later) have a simpler build process, which I've written about here.

Steps to build the non-header-only Boost libraries on Windows, such as Boost.Thread and Boost.Regex, using Microsoft Visual Studio:

  1. Download the latest version of Boost and extract it to some location, say, D:\Boost\, making sure to maintain the directory structure

  2. Download the latest version of Boost Jam (you want the ntx86 binaries) and extract it to some location (probably the same as above), again making sure to maintain the directory structure

  3. Your directory structure should now consist of D:\Boost\boost_1_38_0\ and D:\Boost\boost-jam-3.1.17-1-ntx86\

  4. Make sure you have lots of hard drive space (at least 5GB)

  5. In a command prompt, navigate to your Boost library folder (D:\Boost\boost_1_38_0\) and then use this command (read further down if you want details about what these arguments are doing):

    > ..\boost-jam-3.1.17-1-ntx86\bjam.exe --toolset=msvc --build-type=complete stage
    
  6. If you have a dual/quad/many-core processor or can otherwise take advantage of parallel jobs, also add the -jN switch, where N is the number of jobs to run at a time (e.g. bjam.exe -j4 --toolset=msvc).

  7. And wait. This will take a very long time.

  8. After it completes you will have a stage subdirectory (D:\Boost\boost_1_38_0\stage\) which will contain a lib subdirectory, containing the compiled libraries. You may move these anywhere you wish.

  9. You will also have a bin.v2 subdirectory which was used for temporary files. You may delete this folder to reclaim some hard drive space.

More details:

--toolset=msvc : tells Boost Jam to compile with Microsoft Visual Studio.

--build-type=complete : notes that you want every possible combination built (Debug/Release, Multi/Single threaded, Static/Shared).

stage : a build target which builds the compiled libraries to the stage directory (which you can set with --stagedir=<STAGEDIR>). Contrast this with the install target, which builds the compiled libraries, but also copies the header-only libraries to a particular location. This is generally an unnecessary step since you've already extracted the Boost headers somewhere already. Look inside the Jamroot file inside your Boost folder for more details.

Comments

Quick notes on how to use RapidXML

— richardwb on Friday, February 27, 2009 @ 21:11

There's a C++ XML library called RapidXML which is perfect for most non-enterprise uses of XML. I wouldn't call this a tutorial, but I hope this ends up helping someone. The documentation isn't very explicit on how to output an XML declaration, for example.

How to create your XML from scratch and then output this XML into a string, with an XML declaration:

<?xml version="1.0" encoding="utf-8"?>
<rootnode version="1.0" type="example">
  <childnode/>
</rootnode>
using namespace rapidxml;

xml_document<> doc;

// xml declaration
xml_node<>* decl = doc.allocate_node(node_declaration);
decl->append_attribute(doc.allocate_attribute("version", "1.0"));
decl->append_attribute(doc.allocate_attribute("encoding", "utf-8"));
doc.append_node(decl);

// root node
xml_node<>* root = doc.allocate_node(node_element, "rootnode");
root->append_attribute(doc.allocate_attribute("version", "1.0"));
root->append_attribute(doc.allocate_attribute("type", "example"));
doc.append_node(root);

// child node
xml_node<>* child = doc.allocate_node(node_element, "childnode");
root->append_node(child);

std::string xml_as_string;
// watch for name collisions here, print() is a very common function name!
print(std::back_inserter(xml_as_string), doc);
// xml_as_string now contains the XML in string form, indented
// (in all its angle bracket glory)

std::string xml_no_indent;
// print_no_indenting is the only flag that print() knows about
print(std::back_inserter(xml_no_indent), doc, print_no_indenting);
// xml_no_indent now contains non-indented XML



Parsing and traversing an XML document like this one:

<?xml version="1.0" encoding="utf-8"?>
<rootnode version="1.0" type="example">
  <childnode entry="1">
    <evendeepernode attr1="cat" attr2="dog"/>
    <evendeepernode attr1="lion" attr2="wolf"/>
  </childnode>
  <childnode entry="2">
  </childnode>
</rootnode>
void traverse_xml(const std::string& input_xml)
{
    // (input_xml contains the above XML)

    // make a safe-to-modify copy of input_xml
    // (you should never modify the contents of an std::string directly)
    vector<char> xml_copy(input_xml.begin(), input_xml.end());
    xml_copy.push_back('\0');

    // only use xml_copy from here on!
    xml_document<> doc;
    // we are choosing to parse the XML declaration
    // parse_no_data_nodes prevents RapidXML from using the somewhat surprising
    // behavior of having both values and data nodes, and having data nodes take
    // precedence over values when printing
    // >>> note that this will skip parsing of CDATA nodes <<<
    doc.parse<parse_declaration_node | parse_no_data_nodes>(&xml_copy[0]);

    // alternatively, use one of the two commented lines below to parse CDATA nodes, 
    // but please note the above caveat about surprising interactions between 
    // values and data nodes (also read http://www.setnode.com/blog/a-rapidxml-gotcha/)
    // if you use one of these two declarations try to use data nodes exclusively and
    // avoid using value()
    //doc.parse<parse_declaration_node>(&xml_copy[0]); // just get the XML declaration
    //doc.parse<parse_full>(&xml_copy[0]); // parses everything (slowest)

    // since we have parsed the XML declaration, it is the first node
    // (otherwise the first node would be our root node)
    string encoding = doc.first_node()->first_attribute("encoding")->value();
    // encoding == "utf-8"

    // we didn't keep track of our previous traversal, so let's start again
    // we can match nodes by name, skipping the xml declaration entirely
    xml_node<>* cur_node = doc.first_node("rootnode");
    string rootnode_type = cur_node->first_attribute("type")->value();
    // rootnode_type == "example"

    // go straight to the first evendeepernode
    cur_node = cur_node->first_node("childnode")->first_node("evendeepernode");
    string attr2 = cur_node->first_attribute("attr2")->value();
    // attr2 == "dog"

    // and then to the second evendeepernode
    cur_node = cur_node->next_sibling("evendeepernode");
    attr2 = cur_node->first_attribute("attr2")->value();
    // now attr2 == "wolf"
}

[Edit: Thanks to Michele Tavella for catching a silly bug on my part!]
[Edit: Thanks to Wei for noting that parse_no_data_nodes will skip over CDATA nodes]
[Edit: Thanks to remo for catching a typo]

Thanks for the comments everyone! I'm glad to help out where I can, but please do note that I check this blog rather infrequently at this point in time and will often not respond for days, if not weeks. If you have a question to which you need an answer within a reasonable time frame, I'd recommend using Stack Overflow. On the other hand, if you have corrections and/or suggestions, please continue to leave them here and I'll address them as they come up. Thanks again!

Comments

Remember that users do not read!

— richardwb on Wednesday, February 25, 2009 @ 14:42

Users do not read anything they are not interested in. This is well known and mentioned by many others.

You might not agree, or think that only a small percentage of users don't read. After all, you read. You love to read! Unfortunately, you don't count. You're a programmer, you better love reading!

I had absorbed this information before, but there is a definite difference between knowledge and experience. A couple of weeks ago I witnessed first-hand evidence that users do not read. I introduced a friend (who is the same age as I am and computer literate, uses Facebook and instant messaging, comfortable with Microsoft Office, etc.) to a computer game. This game, like most casual games nowadays, has a friendly tutorial, with large (~48pt) colorful fonts and a very gradual learning curve. The first thing the tutorial does is put up five lines of text which is maybe two sentences worth of information. All five lines are promptly ignored and the cursor heads straight for the "Next" button.

Well, that's okay, there's nothing important in that dialog anyway.

Next; next; next. The next three dialogs are skipped. The last dialog happens to contain important information on the more subtle points of the game.

This starts to drive me nuts, as I always eagerly soak up any bits of information a game throws at me. I want to play it to the best of my ability, after all. It hit me then: my friend just wanted to play and have fun. These dialogs were getting in the way! However, the game is entirely possible to play and enjoy without knowing every little intricate detail --- it's a 'casual' game after all.

All good software is like this. You should be able to dive in and use the program without reading anything. Your power users---the ones who use your program everyday---will know it's advantageous to learn as many shortcuts and tips as possible, so they can complete their tasks faster. They are the ones who might end up reading the information you throw at them (though it's still not a certainty). Your occasional user, on the other hand, wants to get their task done as soon as possible, because they have other things to do. You may assume they will not read anything you throw at them that is over two sentences long.

The most surprising thing to me was that those game dialogs were not close to being long. They were well under the "typical-users-see-nothing-past-this-word" limit and they still didn't get read. To me that implies that the only words you can assume will always get read are the words on the buttons themselves. Is your software still usable if all your explanatory text is missing?

Perhaps, since users read the text on buttons, you could somehow associate explanatory text with a button? I suspect the new Task Dialogs in Windows Vista were developed with this in mind: they use massive buttons and associate explanatory text with a button. It's unfortunate that they weren't introduced earlier, as the main reason you so often see simple "Yes / No" or "OK / Cancel" dialogs in Windows applications is they are really simple to code:

MessageBox(hWnd, "Isn't this easy?", "Yet Another Windows Application", MB_YESNO);

A task dialog type affair (pre-Vista), on the other hand, requires separate templates for each dialog or creating an indirect dialog (on the fly, with lots of simple but tedious math). Both of these approaches take infinitely longer to write.

If you really care about usability, though, perhaps it's time well spent.

Comments

lighttpd tips

— richardwb on Monday, February 09, 2009 @ 16:32

lighttpd is a web server that is optimized for speed. It is particularly adept at serving static content. Since most web hosts provide Apache and most web applications assume you are running on Apache (which is why web hosts provide Apache and why web applications assume you use Apache and why web hosts provide Apache and ...) it can take a bit more effort to find lighttpd documentation.

Some little things that I have picked up for lighttpd:

lighttpd version:

> lighttpd -v

lighttpd version and supported features: (note: that is a capital V)

> lighttpd -V


Using Apache to forward requests to lighttpd? Lighttpd will see that all incoming requests have the IP of the Apache proxy. There is a module mod_extforward which will extract a real IP from the X-Forwarded-For header. Add these lines in the appropriate locations to your lighttpd.conf file:

server.modules = (
    ...                      # (other modules)
    "mod_extforward",        # change x-forwarded-for IPs into real IPs, 
                             # load after mod_accesslog
)
extforward.forwarder = (
    "127.0.0.1" => "trust"   # where 127.0.0.1 is the IP address of the proxy
)


You can use variables in lighttpd.conf:

server.username = "myusername"                  # lighttpd configuration option
var.basedir = "/users/home/" + server.username  # declares a variable called <em>basedir</em>
server.document-root = basedir + "/www/"        # equal to <em>/users/home/myusername/www/</em>


Set a different server.document-root depending on the host:

$HTTP["host"] == "www.example.org" {
    server.document-root = basedir + "/www/www.example.org/"  # basedir is our variable
}


Redirect people to a single canonical web address (in this example http://example.org/blog/candy-is-yummy will redirect to http://www.example.org/blog/candy-is-yummy):

# make sure to include the mod_redirect module
$HTTP["host"] == "example.org" {                 # if host matches "example.org" then...
    url.redirect = (                             #   if the URL matches ^/(.*)$
        "^/(.*)$" => "http://www.example.org/$1" #   redirect to http://www.example.org/$1
    )                                            #   where $1 is what was captured by the
}                                                #   the parenthesis (.*)

^/(.*)$ is an example of a regular expression. Specifically, lighttpd uses PCRE. Instead of equality, ==, you can also test for inequality with !=. If you want to use regular expressions in the match conditional, you can use =~ and !~ for equality and inequality respectively (this is useful for paths):

# make sure to include the mod_access module
$HTTP["host"] == "www.example.org" {
    server.document-root = basedir + "/www/www.example.org/"

    $HTTP["url"] =~ "^/private/" {  # if url starts with /private then...
        url.access-deny = ("")      #   deny access to URLs that end with ""
    }                               #   (i.e. deny access to all URLs)
}

If you use the regexp conditionals remember that many symbols have a special meaning. The '.' (period) is a common example, as it has the special meaning of "match any single character". You can escape it by prepending a backslash: '\.'

Comments

What is duck typing?

— richardwb on Tuesday, January 27, 2009 @ 14:12

I found that when I started dabbling in Python that it was a bit hard to get a grasp on what duck typing actually is. You hear that it achieves polymorphism without inheritance, but that really explains very little. I have a C++/Java/C# background and it took a bit of time before that wonderful epiphany came, where all of a sudden I seemed to get a good grasp on what the concept actually is.

From the Duck typing article on Wikipedia we find that the term comes from the phrase "If it walks like a duck and quacks like a duck, I would call it a duck."

That makes perfect sense now, but it definitely didn't help further my understanding when I first came across it.

Perhaps the best way to explain would be with some code. I'm using Java here because it's wordy enough that it's understandable even if you don't know the syntax. For this "real world" example we will be designing that ever so classic "real world application of object oriented programming": the hierarchy of vehicles.

So suppose we have an existing Driver class that will drive our vehicles for us, as long as we provide the gas(), brake(), turnLeft() and turnRight() methods. In Java this would likely be expressed as an interface:

public interface IDriveable {
    public void gas();
    public void brake();
    public void turnLeft();
    public void turnRight();
}

And then we would have a class that implements that interface, for example:

public class Car implements IDriveable {
    public void gas() {
        System.out.println("Vroom vroom");
    }
    public void brake() {
        System.out.println("Screeeechhhh!");
    }
    public void turnLeft() {
        System.out.println("Left");
    }
    public void turnRight() {
        System.out.println("Right");
    }
}

And the Driver class would be similar to:

public class Driver {
    public void drive(IDriveable vehicle) {
        vehicle.gas();
        vehicle.turnLeft();
        vehicle.brake();
    }
}

And the main class:

public class Start {
    public static void main(String[] args) {
        Car c = new Car();
        Driver d = new Driver();
        d.drive(c);
    }
}

Sample output:

Vroom vroom
Left
Screeeechhhh!

This is standard OOP stuff, where as long as our class implements the IDriveable interface, it'll be driveable by the Driver class. If the class does not implement that interface, the code will not even compile.

If we bring duck typing into the picture, things change. Instead of having a defined interface (IDriveable) which must be implemented, all we require is that a given class implements the specific methods needed. There is no strict interface. In Python our code would look like:

class Car:
    def gas(self):
        print "Vroom vroom"
    def brake(self):
        print "Screeeechhhh!"
    def turnLeft(self):
        print "Left"
    def turnRight(self):
        print "Right"

class Driver:
    def drive(self, vehicle):
        vehicle.gas()
        vehicle.turnLeft()
        vehicle.brake()

c = Car()
d = Driver()
d.drive(c)

Which produces the same output as above.

Let's add a new class to the Python code:

class Pencil:
    def gas(self):
        print "Scribble scribble"
    def turnLeft(self):
        print "Left pencil turn?"
    # note that turnRight is missing!
    def brake(self):
        print "PUT YOUR PENCILS DOWN"

Although Pencil having these methods makes absolutely no sense, we could pass this to the drive() method and there will be no errors. Yep, even though Pencil doesn't implement turnRight(), because turnRight() is never called, all this code will run, error free!

We have a Pencil that can gas(), turnLeft(), and brake(), just like a Car. What is the difference between them, as far as this code is concerned? None!

This is what duck typing is. If a class acts like another class, it can substitute for that class. There's no need for there to be strictly defined relationships between classes.

Comments

MapVirtualKey(), GetKeyNameText(), and a story of how to get proper names for the arrow keys

— richardwb on Saturday, December 20, 2008 @ 14:50

So let's say you are currently storing and using virtual key codes to handle keyboard input for a game. You decide to let your users customize their keys, so you go ahead and write some key configuration code. This requires that you map virtual key codes to key names.

Luckily, Windows provides some functions that do exactly what you want! Essentially you use MapVirtualKey() to get a scancode for that key and then pass that scancode (left-shifted by 16) into GetKeyNameText().

Some minutes later you finish your code and you go to test it. You hit the A key and you see 'A'. The 5 key outputs '5'. Even the Backspace key outputs 'Backspace'. It works as expected.

Then you hit one of the arrow keys and your code spits out 'Num 8'. It's right yet it's wrong.

Michael Kaplan has a blog entry that describes a recent update to the MapVirtualKeyEx() call:

MAPVK_VK_TO_VSC_EX:

Windows Vista and later: The uCode parameter is a virtual-key code and is translated into a scan code. If it is a virtual-key code that does not distinguish between left- and right-hand keys, the left-hand scan code is returned. If the scan code is an extended scan code, the high byte of the uCode value can contain either 0xe0 or 0xe1 to specify the extended scan code. If there is no translation, the function returns 0.

Unfortunately this doesn't help much as Windows XP is still used by a majority of users. However, this does imply that the current MapVirtualKey[Ex]() call does not specify the extended scan code. So if we just set the extended bit for some characters we may end up with a function similar to this:

std::string GetKeyName(unsigned int virtualKey)
{
    unsigned int scanCode = MapVirtualKey(virtualKey, MAPVK_VK_TO_VSC);

    // because MapVirtualKey strips the extended bit for some keys
    switch (virtualKey)
    {
        case VK_LEFT: case VK_UP: case VK_RIGHT: case VK_DOWN: // arrow keys
        case VK_PRIOR: case VK_NEXT: // page up and page down
        case VK_END: case VK_HOME:
        case VK_INSERT: case VK_DELETE:
        case VK_DIVIDE: // numpad slash
        case VK_NUMLOCK:
        {
            scanCode |= 0x100; // set extended bit
            break;
        }
    }

    char keyName[50];
    if (GetKeyNameText(scanCode &lt;&lt; 16, keyName, sizeof(keyName)) != 0)
    {
        return keyName;
    }
    else
    {
        return "[Error]";
    }
}

Comments

std::map access violation (or not)

— richardwb on Saturday, October 11, 2008 @ 18:42

I just spent some time tracking down this fun little bug:

I use a mix of std::map and Boost.Function to reduce the ugliness of the Windows message loop. Everything is working just fine, until I make a couple of changes and all of a sudden one of my std::maps is throwing an exception in the destructor:

Access violation reading location 0xccccccd4.

In this code in xtree:

#if _HAS_ITERATOR_DEBUGGING
    void _Orphan_ptr(_Myt& _Cont, _Nodeptr _Ptr) const
    {       // orphan iterators with specified node pointers
        _Lockit _Lock(_LOCK_DEBUG);
        const_iterator **_Pnext = (const_iterator **)&_Cont._Myfirstiter;
        while (*_Pnext != 0)
        {
            if ((*_Pnext)->_Ptr == _Myhead
                || _Ptr != 0 && (*_Pnext)->_Ptr != _Ptr)
            {
                _Pnext = (const_iterator **)&(*_Pnext)->_Mynextiter;
            }
            else
            {       // orphan the iterator
                (*_Pnext)->_Mycont = 0;
                *_Pnext = (const_iterator *)(*_Pnext)->_Mynextiter;
            }
        }
    }
#endif /* _HAS_ITERATOR_DEBUGGING */

Of course, that is suspiciously close to 0xcccccccc, which is used by the debugging runtimes to mark uninitalized stack. (This will be important later.) In any case I go through the usual debugging motions, making sure I'm not putting anything funny into my map container and that values inside the container are what they should be.

So, what was the cause of this bug? It was a result of the change I was making earlier; this is the original code:

LVITEM itemText;
ZeroMemory(&itemText, sizeof(itemText));
itemText.cchTextMax = MAX_PATH;
char buff[MAX_PATH];
itemText.pszText = buff;
SendMessage(listViewHandle, LVM_GETITEMTEXT, index, reinterpret_cast<LPARAM>(&itemText));

And I changed it to use the ListView_GetItemText macro, which is far more succinct:

char itemText[MAX_PATH];
ListView_GetItemText(listViewHandle, index, 0, itemText, _countof(itemText));

No problem yet, right? Ready for the punchline?

I left this line in at the bottom:

SendMessage(listViewHandle, LVM_GETITEMTEXT, index, reinterpret_cast<LPARAM>(&itemText));

For those lucky folks who aren't familiar with all the wonderful intricacies of Windows programming:

SendMessage() is a magical "do all" function that sends a message to a particular window, with WPARAM and LPARAM values tagging along. In this case, we're sending a message of LVM_GETITEMTEXT to the control denoted by listViewHandle. The MSDN documentation for LVM_GETITEMTEXT states that it wants WPARAM and LPARAM to be:

WPARAM : iItem - Index of the list-view item
LPARAM : pitem - Pointer to an LVITEM structure.

My LPARAM, itemText, is no longer an LVITEM structure (it is now a char array of size MAX_PATH). There is absolutely no way that the LVM_GETITEMTEXT code can know that I've made a colossal (yet tiny!) mistake, so I assume that it dutifully casts itemText into an LVITEM structure and does the right stuff with horrendously wrong data. Tada, stack corrupted.

Notice that the source of this bug appears nowhere near an std::map. It is, in fact, in a different function (and a different class and a different file...) entirely! This is why memory corruption errors are one of the most feared bugs around.

Conclusion: if std::map is dying in clear() (or ever, frankly), you may very well be doing something bad, memory wise.

Comments

Proctors in Calgary

— richardwb on Tuesday, July 11, 2006 @ 16:24

"Proctor" is the fancy word for those people who administer exams and tests. If you're taking distance education courses, you may need to find one. I had to find a proctor a year ago to make up for an exam I missed, and it took some time to figure out how to even begin. (A search for "calgary proctors" did not bring up very much...)

I finally found some information on proctors by calling the librarians at the Castell Central Library. So...my tip for finding a proctor is to call the library in your city.

In case there are any other Calgarians who need a proctor, here’s some information on proctors from 2005, along with per exam proctor fees:

Athabasca University (403) 263-6465 $40
University of Calgary (403) 220-5524 $80
SAIT (403) 210-4283 $50
Rocky Mountain College (403) 284-5100 $20

Comments

Linksys BEFSR81 disconnects

— richardwb on Tuesday, July 11, 2006 @ 12:46

For the longest time I had this constant problem with my router, a Linksys BEFSR81 (the 8-port version of the BEFSR41) constantly disconnecting the computers connected to it. It wasn’t the Internet connection going down, since I had a computer that was directly connected to the modem as well, and it never had any problems.

So of course when troubleshooting something like a router, you go and get the latest firmware. No luck there. I also tried resetting the router, restoring default settings, changing the ports used on the back, and so on.

Finally, I tried giving one computer DMZ access (I’m not forgetting about the security concerns raised by doing this). It worked. No more disconnections for that computer, and for now, I’m satisfied.

Posting this in case it comes in handy for anyone else experiencing disconnection problems.

[2008 Addendum: The only real solution to this problem is to get a better router. The BEFSR81 is at least 7 years old now.]

Comments

University of Waterloo cheers

— richardwb on Sunday, April 30, 2006 @ 17:23

Here are some cheers from the University of Waterloo Math orientation (which I went through 4 years ago). These include such classics as Math Rocks, The Biggest Tie, How's the Tie, and Water Water Water. I'm not sure why, but my personal favorite is The Biggest Tie.

Math Rocks

M-A
M-A-T
M-A-T-H
Math Rocks!

Say it loud
Say it big
Let it boil in your blood, feel it zag as you zig
We survive
All the knocks
We have pride when we cheer and we think Math Rocks!

One group chants the first verse while the other group chants the second verse, at the same time. The chants should converge on the phrase "Math Rocks!".

The Biggest Tie

The biggest tie
I ever saw
Was hanging from
The MC wall

I looked at it
And then I knew
I wanted to be
A Mathie too

I stole the tie
It made me laugh
Because it's true
That I love Math

And so began
My new journey
To be part of
Math faculty

My school is sweet
SWEET LIKE A FOX!
Because I know
UW Math Rocks

Sung to the tune and style of The Littlest Worm, The Princess Pat, or I Met a Bear.

Water Water Water

Water water water!
Loo loo loo!
Water water water!
Loo loo loo!
Water!
Loo!
Water!
Loo!
Water water water!
Loo loo loo!

One group (typically the orientation leaders) starts the cheer by yelling "Water water water!". The other group then replies with "Loo loo loo!". It continues in this fashion until the end of the cheer.

There are more Math cheers in the Math Handbook Math Orientation 2008 guide.

Comments