<?xml version='1.0' encoding='UTF-8'?><?xml-stylesheet href="http://www.blogger.com/styles/atom.css" type="text/css"?><feed xmlns='http://www.w3.org/2005/Atom' xmlns:openSearch='http://a9.com/-/spec/opensearchrss/1.0/' xmlns:georss='http://www.georss.org/georss' xmlns:gd='http://schemas.google.com/g/2005' xmlns:thr='http://purl.org/syndication/thread/1.0'><id>tag:blogger.com,1999:blog-1346661302680828934</id><updated>2011-07-28T18:19:17.937-06:00</updated><category term='five.year.olds'/><category term='personal'/><category term='vacation'/><category term='books'/><category term='teasing'/><category term='comics'/><category term='programming'/><category term='intro'/><category term='big.lebowski'/><category term='games'/><category term='wow'/><category term='olivier'/><category term='fight'/><category term='perforce'/><category term='integration'/><category term='hiking'/><category term='python'/><category term='personl'/><category term='trivia'/><category term='performance'/><category term='game.engine.design'/><category term='code'/><category term='multithreaded'/><category term='software-management'/><category term='good.habits'/><category term='c++'/><title type='text'>Lexical Ambiguity</title><subtitle type='html'></subtitle><link rel='http://schemas.google.com/g/2005#feed' type='application/atom+xml' href='http://lexamb.blogspot.com/feeds/posts/default'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/1346661302680828934/posts/default?max-results=100'/><link rel='alternate' type='text/html' href='http://lexamb.blogspot.com/'/><link rel='hub' href='http://pubsubhubbub.appspot.com/'/><author><name>McJohn</name><uri>http://www.blogger.com/profile/14617707594789305466</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='29' src='http://1.bp.blogspot.com/_1qhC8-5QU-s/SM2Cg_QWd3I/AAAAAAAAA5I/3PCIIiOi9gE/s1600-R/orly.jpeg'/></author><generator version='7.00' uri='http://www.blogger.com'>Blogger</generator><openSearch:totalResults>21</openSearch:totalResults><openSearch:startIndex>1</openSearch:startIndex><openSearch:itemsPerPage>100</openSearch:itemsPerPage><entry><id>tag:blogger.com,1999:blog-1346661302680828934.post-104069991843056527</id><published>2009-04-04T01:57:00.002-06:00</published><updated>2009-04-04T02:01:17.025-06:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='games'/><category scheme='http://www.blogger.com/atom/ns#' term='wow'/><title type='text'>Back in wow</title><content type='html'>I played wow for about 2 years--closed beta for a year and then for a year after launch.&lt;br /&gt;&lt;br /&gt;The game was a lot of fun, but when I was right about to hit 60, I realized that I didn't want to raid. The end game held nothing for me, so I stopped playing.&lt;br /&gt;&lt;br /&gt;After wotlk came out, Jason started hyping the game up like crazy. He was having a blast, and he finally talked me back into it.&lt;br /&gt;&lt;br /&gt;I have to say that I've actually been having a lot of fun. They got a lot of things right this time around that were just wrong before.&lt;br /&gt;&lt;br /&gt;Instances are short (generally less than an hour once you're properly geared), and they've added lots of paths to getting good gear. Jason and I are usually able to fill a group for any heroic in about 5 minutes.&lt;br /&gt;&lt;br /&gt;I haven't tried any raiding yet, but I think I'm going to this time around.&lt;br /&gt;&lt;br /&gt;Oh, and if you're on Stormrage, give &lt;A HREF="http://www.wowarmory.com/character-sheet.xml?r=Stormrage&amp;n=Sukitati"&gt;me&lt;/A&gt; a shout sometime.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/1346661302680828934-104069991843056527?l=lexamb.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://lexamb.blogspot.com/feeds/104069991843056527/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=1346661302680828934&amp;postID=104069991843056527' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/1346661302680828934/posts/default/104069991843056527'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/1346661302680828934/posts/default/104069991843056527'/><link rel='alternate' type='text/html' href='http://lexamb.blogspot.com/2009/04/back-in-wow.html' title='Back in wow'/><author><name>McJohn</name><uri>http://www.blogger.com/profile/14617707594789305466</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='29' src='http://1.bp.blogspot.com/_1qhC8-5QU-s/SM2Cg_QWd3I/AAAAAAAAA5I/3PCIIiOi9gE/s1600-R/orly.jpeg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-1346661302680828934.post-2912727387812733516</id><published>2008-09-27T14:34:00.004-06:00</published><updated>2008-09-27T14:34:01.230-06:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='performance'/><category scheme='http://www.blogger.com/atom/ns#' term='python'/><category scheme='http://www.blogger.com/atom/ns#' term='code'/><title type='text'>Python: Removing items from a list</title><content type='html'>While sitting on a plane from Santa Clara to Austin (more on that in another post), I finally sat down and wrote simple test program to determine "what's the fastest way to remove a known item from a list in Python?"&lt;br /&gt;&lt;br /&gt;Python has some surprising performance behaviors, and removing items from a list is no exception. I came up with five methods that I thought someone might implement for removing stuff from a list, along with a bit of glue code to time everything (I used the timeit module for that, hooray for 'coming with batteries').&lt;br /&gt;&lt;br /&gt;I've posted the code in a file, (you can get that &lt;a href="http://www.lexical-ambiguity.com/files/listremove.py"&gt;here&lt;/a&gt;), including the removal functions. I'll assume if you keep reading that you've looked at the source code. We have to have some basis for communication, after all.&lt;br /&gt;&lt;br /&gt;Here are the results of running the module on my MacBook Air, while sitting on the airplane. Your mileage may vary.&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;McJohn$ python listremove.py 1024 4096&lt;br /&gt;name            min      max      mean     median   &lt;br /&gt;ghetto_remove   0.011734 0.024060 0.015157 0.014703 &lt;br /&gt;repeated_remove 0.004583 0.025112 0.012059 0.010892 &lt;br /&gt;comp_remove     0.009502 0.020047 0.012323 0.011900 &lt;br /&gt;old_remove      0.017689 0.035935 0.022772 0.022382 &lt;br /&gt;hard_remove     0.005304 0.014384 0.007783 0.007205 &lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;What's surprising to me about this isn't that the 'hard remove' is the fastest (on average), but that it's worst case is better than the average runtime for most of the other algorithms--except for the 'repeated remove' best case, which is staggeringly good. That's actually quite shocking in and of itself, but I suspect that it would get worse if there were more collisions in the list itsef. We can test this by reducing the number of random elements allowable in the list while maintaining the list length. Here are the results for that trial:&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;McJohn$ python listremove.py 16 4096&lt;br /&gt;name            min      max      mean     median   &lt;br /&gt;ghetto_remove   0.139576 0.214912 0.175577 0.177581 &lt;br /&gt;repeated_remove 2.597167 4.307188 3.578634 3.592269 &lt;br /&gt;comp_remove     0.090875 0.144086 0.116044 0.116585 &lt;br /&gt;old_remove      0.168973 0.239456 0.206943 0.207778 &lt;br /&gt;hard_remove     0.399565 0.668813 0.534192 0.542308 &lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;It turns out that this approach makes the repeated_remove case staggeringly bad, causing a massive increase in runtime. I wasn't actually prepared for it to get this bad (I was fairly certain there was a hang in my code somewhere, the perf tanked so badly). Unfortunately, that wasn't the case--it was just that repeated_remove gets really slow in the case of massive collisions. That shouldn't be too surprising, though. In the face of an element appearing many times in the list, the repeated_remove code has an O(N^2) worst case performance. It effectively could have to traverse the list up to N times (where N is also the length of the list). Of course, each traversal in that case should be very fast, averaging 1 lookup before finding the element. Something definitely seems to be going wrong, though. &lt;br /&gt;&lt;br /&gt;Also a bit surprising in this case is that the hard_remove performance gets quite bad. In fact, only the 'comp_remove' seems to get good performance&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/1346661302680828934-2912727387812733516?l=lexamb.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://lexamb.blogspot.com/feeds/2912727387812733516/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=1346661302680828934&amp;postID=2912727387812733516' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/1346661302680828934/posts/default/2912727387812733516'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/1346661302680828934/posts/default/2912727387812733516'/><link rel='alternate' type='text/html' href='http://lexamb.blogspot.com/2008/09/python-removing-items-from-list.html' title='Python: Removing items from a list'/><author><name>McJohn</name><uri>http://www.blogger.com/profile/14617707594789305466</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='29' src='http://1.bp.blogspot.com/_1qhC8-5QU-s/SM2Cg_QWd3I/AAAAAAAAA5I/3PCIIiOi9gE/s1600-R/orly.jpeg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-1346661302680828934.post-7822962949131264543</id><published>2008-09-24T17:25:00.000-06:00</published><updated>2008-09-24T23:54:14.095-06:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='performance'/><category scheme='http://www.blogger.com/atom/ns#' term='python'/><category scheme='http://www.blogger.com/atom/ns#' term='code'/><title type='text'>Python: Surprising performance characteristics</title><content type='html'>There are some interesting performance properties of python. One of those, that often surprises newbies to python and veterans alike is the relatively (okay very) poor performance of numbers, versus the excellent performance of lists.&lt;br /&gt;&lt;br /&gt;This was actually something &lt;a href="http://picasaweb.google.com/jra101/"&gt;jra101&lt;/a&gt; and I stumbled across at work, when implementing an xml writer (as it turns out, python doesn't already have one, which is a bit surprising). In any event, we discovered this fact while attempting to optimize our writer. Initially, we had an API that looked something like this:&lt;code&gt;&lt;/code&gt;&lt;br /&gt;&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;class XMLWriter(object):&lt;br /&gt;  def openTag(self, tagName, attributes=None):&lt;br /&gt;    self._tagStack.append(tagName)&lt;br /&gt;    # other stuff&lt;br /&gt;&lt;br /&gt;  def closeTag(self, tagName):&lt;br /&gt;    assert self._tagStack[-1] == tagName&lt;br /&gt;    del self._tagStack[-1]&lt;br /&gt;    # other stuff&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;Without profiling, jra and I both decided that we could optimize this at the expense of a little safety by replacing the safety code with something a little less safe. This also made it so we didn't have to make sure the code closing the tag knew who had opened it. That version looked something like this:&lt;br /&gt;&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;class XMLWriter(object):&lt;br /&gt;  def openTag(self, tagName, attributes=None):&lt;br /&gt;    self._openTagCount += 1&lt;br /&gt;    # other stuff&lt;br /&gt;&lt;br /&gt;  def closeTag(self, tagName):&lt;br /&gt;    assert self._openTagCount &gt; 1&lt;br /&gt;    self._openTagCount -= 1&lt;br /&gt;    # other stuff&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;Shockingly, this version wasn't any faster than the inital version! After I thought about this for a bit, I decided that this was actually intuitive with a bit of python knowledge, although it was a little scary. In Python, everything is an object. Everything. &lt;b&gt;Everything.&lt;/b&gt; Even numbers. Moreover, python integers have infinite precision. The code below works correctly (although slowly, as you might expect):&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;pow(pow(2,136), 2)&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;In order to support this, python cannot take optimizations for numbers that would happen in C or C++. Moreover, python can never--even in the case of simple integers--treat integers as integers. It has to evaluate the mathemetical operation, determine the result, determine whether the result fits in the old type, then possibly create a new value, and finally overwrite the old value with the new. That's a lot of work, compared to what happens in (for example) C. In C, an add operation corresponds to a single ASM instruction. Hard to compete with.&lt;br /&gt;&lt;br /&gt;Meanwhile, list code is almost universally dispatched to a C library that is exceedingly fast and efficient. The morale of the story is one that Olivier has covered in "Mutable Conclusions" before: profile early, profile often.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/1346661302680828934-7822962949131264543?l=lexamb.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://lexamb.blogspot.com/feeds/7822962949131264543/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=1346661302680828934&amp;postID=7822962949131264543' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/1346661302680828934/posts/default/7822962949131264543'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/1346661302680828934/posts/default/7822962949131264543'/><link rel='alternate' type='text/html' href='http://lexamb.blogspot.com/2008/09/python-surprising-performance.html' title='Python: Surprising performance characteristics'/><author><name>McJohn</name><uri>http://www.blogger.com/profile/14617707594789305466</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='29' src='http://1.bp.blogspot.com/_1qhC8-5QU-s/SM2Cg_QWd3I/AAAAAAAAA5I/3PCIIiOi9gE/s1600-R/orly.jpeg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-1346661302680828934.post-704871045163020272</id><published>2008-09-23T17:30:00.000-06:00</published><updated>2008-09-23T19:21:01.653-06:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='programming'/><category scheme='http://www.blogger.com/atom/ns#' term='game.engine.design'/><title type='text'>Game Engine Topologies: Peer-to-peer</title><content type='html'>This is part two of a two-part post on game engine topologies (&lt;a href="http://lexamb.blogspot.com/2008/09/game-engine-topologies-client-server.html"&gt;first part&lt;/a&gt;). The first-half is not required reading to grok the second half.&lt;br /&gt;&lt;br /&gt;&lt;span style="font-size:130%;"&gt;Peer-to-Peer&lt;/span&gt;&lt;br /&gt;As I mentioned in the first part of this article, a client-server topology generally makes sense when you have relatively few objects that change, as each object update requires messages to be sent to the server and then to all the clients.&lt;br /&gt;&lt;br /&gt;In a peer-to-peer topology, the server is only sent stimulus, and that stimulus is rebroadcast to all clients. Synchronization is maintained by having all machines take the same actions at the same time. Not exactly in the wall-clock sense, but in the simulation sense (close to wall-clocks, though).&lt;br /&gt;&lt;br /&gt;A concrete example might clear up the differences here. P2P topologies are typically used in RTSes. Imagine that while playing, you select a group of units, then order them to move to a location, then follow-up with an order for them to attack a specific unit. Afterwards, you select another group of units and order them to defend a position.&lt;br /&gt;&lt;br /&gt;In a client-server topology, you would send updates about all of those objects as their state changed, and you would periodically have to deal with discrepancies (for example, on one client a unit is out of range but on another client the unit was killed, etc).&lt;br /&gt;&lt;br /&gt;In a P2P topology, the messages look something like this:&lt;br /&gt;&lt;code&gt;&lt;br /&gt;StartSelection(PlayerID)&lt;br /&gt;AddUnit(1)&lt;br /&gt;AddUnit(2)&lt;br /&gt;AddUnit(3)&lt;br /&gt;StopSelection(PlayerID)&lt;br /&gt;MoveSelectionTo(location)&lt;br /&gt;SelectionAttack(TargetObject)&lt;br /&gt;ClearSelection(PlayerID)&lt;br /&gt;StartSelection(PlayerID)&lt;br /&gt;AddUnit(4)&lt;br /&gt;AddUnit(5)&lt;br /&gt;StopSelection(PlayerID)&lt;br /&gt;SelectionDefend(location)&lt;br /&gt;&lt;/code&gt;&lt;br /&gt;&lt;br /&gt;While this seems like a lot of messages, it's actually quite sparse, particularly considering each message fits in 12 bytes or less. This means the total byte cost for all above messages is around 112 bytes. By comparison, 7 positional updates from a client-server topology would cost about the same. This could be less than one frame of movement!&lt;br /&gt;&lt;br /&gt;So these messages are sent, and 'at some point in the future', they are executed. Even on the local machine, commands are not executed immediately! You generally (okay, always) get some sort of client feedback, the unit acknowledges via sound, or a client-only effect is played (for example, a UI element flashes), and then nothing happens for a bit. This is because of the architecture under the covers. The message has to be sent to the server, the server figures out how much time to allow for the latency of all clients, and says 'at this point in the future, everyone execute this command'.&lt;br /&gt;&lt;br /&gt;P2P topologies have one other packet that they send 'every so often', a synchronization packet. In order to ensure that the clients are all still in sync--that is, they all still agree on the state of the universe--they need to send a packet that indicates what the state of their universe is. Although there are many ways to accomplish such a task, in practice I've only seen one implementation: CRC the entire simulation, then send the results to the server. If a client goes out of sync, you have only two choices: abort or try to regain syncronization. Although regaining syncronization is possible, I've yet to see a production title attempt it. The reason is simple: a synchronization error is often a sign that one player is trying to cheat.&lt;br /&gt;&lt;br /&gt;Peer-to-peer topologies are effectively the 'inductive proof' of game engine topologies. We all start in the same state, and we all issue every command on the same tick. We better all be in the same state on the next tick.&lt;a href="http://lexamb.blogspot.com/2008/09/game-engine-topologies-client-server.html"&gt;&lt;/a&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/1346661302680828934-704871045163020272?l=lexamb.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://lexamb.blogspot.com/feeds/704871045163020272/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=1346661302680828934&amp;postID=704871045163020272' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/1346661302680828934/posts/default/704871045163020272'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/1346661302680828934/posts/default/704871045163020272'/><link rel='alternate' type='text/html' href='http://lexamb.blogspot.com/2008/09/game-engine-topologies-peer-to-peer.html' title='Game Engine Topologies: Peer-to-peer'/><author><name>McJohn</name><uri>http://www.blogger.com/profile/14617707594789305466</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='29' src='http://1.bp.blogspot.com/_1qhC8-5QU-s/SM2Cg_QWd3I/AAAAAAAAA5I/3PCIIiOi9gE/s1600-R/orly.jpeg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-1346661302680828934.post-6973126120204018512</id><published>2008-09-14T19:42:00.001-06:00</published><updated>2008-09-14T19:42:00.856-06:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='personal'/><title type='text'>An Exercise in Sanity</title><content type='html'>I hate to post another "blogvertisement," but I finally started reading Braxton's blog, "&lt;a href="http://exercisesanity.com/blog/"&gt;An Exercise in Sanity&lt;/a&gt;." It's pretty interesting.&lt;br /&gt;&lt;br /&gt;Braxton is my next-cube mate, and a pretty nice guy. He and I don't always agree (in particular, he both likes Perl and doesn't have any problems with C++), but he does have some pretty savvy views about "the real world." Anyways, go check out his blog, maybe I'll post some rebuttals whenever he gets it wrong. :)&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/1346661302680828934-6973126120204018512?l=lexamb.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://lexamb.blogspot.com/feeds/6973126120204018512/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=1346661302680828934&amp;postID=6973126120204018512' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/1346661302680828934/posts/default/6973126120204018512'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/1346661302680828934/posts/default/6973126120204018512'/><link rel='alternate' type='text/html' href='http://lexamb.blogspot.com/2008/09/exercise-in-sanity.html' title='An Exercise in Sanity'/><author><name>McJohn</name><uri>http://www.blogger.com/profile/14617707594789305466</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='29' src='http://1.bp.blogspot.com/_1qhC8-5QU-s/SM2Cg_QWd3I/AAAAAAAAA5I/3PCIIiOi9gE/s1600-R/orly.jpeg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-1346661302680828934.post-8011036615647073530</id><published>2008-09-14T00:06:00.009-06:00</published><updated>2008-09-15T18:24:00.610-06:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='programming'/><category scheme='http://www.blogger.com/atom/ns#' term='game.engine.design'/><title type='text'>Game Engine Topologies: Client-Server</title><content type='html'>In multiplayer games, you have to communicate information between the clients to keep the game in sync. This simple requirement actually has quite a far-reaching impact in your game engine. Although there are many variations on the theme, there are two primary topologies that are used to achieve communication between clients: "Client-Server and Peer-to-peer". These topologies are used all the way from the simplest multiplayer game (think Worms), to the most complicated MMO. In a two-part post, I'll cover the topologies used by games today.&lt;br /&gt;&lt;br /&gt;&lt;span style="font-size:130%;"&gt;Client-Server (examples: WoW, every multiplayer FPS ever)&lt;/span&gt;&lt;br /&gt;In a client-server topology, you have one (or possibly more) servers, and you have many clients. The clients are each responsible for a set of one or more objects, and whenever a change occurs in one of these objects, the client communicates those objects to the server, who takes appropriate action. Likewise, when other game entities change, the server pushes that information to the various clients. The clients can be thought of as basically dumb, they aren't doing much (or possibly any) world simulation and instead are just rendering the state of the world as they were told on any given frame. It's probably obvious--but worth saying anyways--that in a client-server environment, all clients are lagging behind the server by some amount of time, known as their latency. That is, while the server may be processing simulation time 30, clients might all be processing at time 27, 28, 29, or 26, depending on how long it takes for them to receive network traffic as well as render the results of the current state of the simulation.&lt;br /&gt;&lt;br /&gt;The values of client-server topologies are that they are generally low-latency. You press a button, and the results are immediately sent to the server. The downside of such a system is that the network requirements scale linearly with the number of objects that exist in the game universe.&lt;br /&gt;&lt;br /&gt;In Client-Server topologies, there are two basic modes: Client Authoritative (CA) and Server Authoritative (SA). Valve has also clever spin on this approach, which I'll discuss seperately below.&lt;br /&gt;&lt;br /&gt;The primary difference between CA and SA involves the messages that are sent along with who validates that the message is legal. In a CA environment, the client sends messages like "Spawn a rocket here, travelling in this direction with this velocity," or "I hit this entity for XYZ points of damage." The server doesn't perform additional validation of these messages, which leads to an obvious problem: cheating. In fact, even if you have a completely robust and bulletproof client (which is pretty much impossible), these types of architectures are vulnerable to man-in-the-middle (MITM) attacks. It would be trivial, for example, to write a client that sat next to your game, listening for a global hotkey that would insert an automatic 'kill shot', for example. The upside of CA servers, however, are that the game feels virtually lag free for all players, because if it looks like a hit on your machine, it &lt;span style="font-weight: bold;"&gt;is&lt;/span&gt; a hit.&lt;br /&gt;&lt;br /&gt;By contrast, SA architectures validate the messages on the server, and the messages are typically more of an "I tried to take this action" message. For example "I pressed fire", or "I moved forward". The server validates that the messages are legal, and then sends the appropriate response (or issues the action). The upside of this sort of architecture is that cheating becomes enormously more difficult, because there typically isn't a "I hit so-and-so for 400 points of damage" message, and even if there is, the server will take steps to validate that the message occurs in a valid and well-formed state. The downside is, of course, latency. This leads to some complex architecture to try and deal with the latency, generally called Client Prediction. I'll cover this topic in another post at some point in the future.&lt;br /&gt;&lt;br /&gt;&lt;sa&gt;Team Fortress 2, along with other Valve FPS games based on the source engine, perform an interesting spin on client-server engine design. They try to pretend to be client authoritative, but are actually server authoritative. They do this by keeping a window backwards in time of up to a second (this is configurable) of where all the simulation objects have been. As said before, all clients must be running at some time slightly behind the leading edge (where the server is) due to latency (both rendering and network), even though this latency is very small. When a player takes an action (the most obvious of which is firing a weapon), he sends a timestamp to the server along with the "I've fired" message. The server gets the time stamp and essentially rolls the world backwards to that time to determine whether the player got a hit or not, and then sends the appropriate messages to all other clients. As long as the client and server agree on where objects are at a particular time, which is known as being in sync, then the client prediction is effectively completely accurate.&lt;br /&gt;&lt;br /&gt;The effect of this logic is great for the person shooting, but can be a bit surprising for the person being shot. Imagine that you're running across the battlefield, duck behind a wall and think "I'm safe," only to find out a half-second later that you've been headshot. It's happen many-a-time to me and my friends, and it's always a little surprising.&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;/sa&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/1346661302680828934-8011036615647073530?l=lexamb.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://lexamb.blogspot.com/feeds/8011036615647073530/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=1346661302680828934&amp;postID=8011036615647073530' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/1346661302680828934/posts/default/8011036615647073530'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/1346661302680828934/posts/default/8011036615647073530'/><link rel='alternate' type='text/html' href='http://lexamb.blogspot.com/2008/09/game-engine-topologies-client-server.html' title='Game Engine Topologies: Client-Server'/><author><name>McJohn</name><uri>http://www.blogger.com/profile/14617707594789305466</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='29' src='http://1.bp.blogspot.com/_1qhC8-5QU-s/SM2Cg_QWd3I/AAAAAAAAA5I/3PCIIiOi9gE/s1600-R/orly.jpeg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-1346661302680828934.post-8568048240253641213</id><published>2008-09-10T23:06:00.003-06:00</published><updated>2008-09-10T23:41:20.879-06:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='personal'/><category scheme='http://www.blogger.com/atom/ns#' term='vacation'/><title type='text'>The long way home</title><content type='html'>On Friday, I'm heading back to Austin. From Lake City, it's a long drive, about 980 miles. I'm doing it in two days, because 14 hours of driving is too much for one day. I've decided to stop in Abilene, which should only be about 11 hours on the first day, and a nice short drive the next day.&lt;br /&gt;&lt;br /&gt;There are some logistical issues with travelling with a dog as large as Samus (she weighs right around 75 pounds now), but dogfriendly.com has a listing for motels that are animal friendly, so that helps tremendously.&lt;br /&gt;&lt;br /&gt;We've had a great time out in CO. Samus has been playing with her "cousins" Penny and Nickel. They're english pointers, and they're cute. Penny is super hyperactive and Nickel has hip dysplasia. Otherwise, they are happy and healthy. Meanwhile, we've hiked about 50 miles while we've been in colorado, plus about 5 vertical miles. I have to say that the last year of working out has made a tremendous difference for me. During a short but steep hike, we maintained a rate of 1 1/3 mph, which is actually pretty impressive as we were also doing 1100' vertically per hour. In general, I haven't been getting overly tired on or after our hikes, so that's been really great.&lt;br /&gt;&lt;br /&gt;Although we've had fun, I'm ready to get home. I miss my wife a lot, and I think she's finally had time to miss me.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/1346661302680828934-8568048240253641213?l=lexamb.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://lexamb.blogspot.com/feeds/8568048240253641213/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=1346661302680828934&amp;postID=8568048240253641213' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/1346661302680828934/posts/default/8568048240253641213'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/1346661302680828934/posts/default/8568048240253641213'/><link rel='alternate' type='text/html' href='http://lexamb.blogspot.com/2008/09/long-way-home.html' title='The long way home'/><author><name>McJohn</name><uri>http://www.blogger.com/profile/14617707594789305466</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='29' src='http://1.bp.blogspot.com/_1qhC8-5QU-s/SM2Cg_QWd3I/AAAAAAAAA5I/3PCIIiOi9gE/s1600-R/orly.jpeg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-1346661302680828934.post-5295649031178928012</id><published>2008-09-08T17:02:00.001-06:00</published><updated>2008-09-08T17:02:00.241-06:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='code'/><category scheme='http://www.blogger.com/atom/ns#' term='programming'/><title type='text'>The cheapest code is the code that doesn't exist</title><content type='html'>We have a common idiom in software: the fastest code is the code that doesn't execute. For non-nerds, this basically means that you want to structure your code so that you don't execute unnecessary computations.&lt;br /&gt;&lt;br /&gt;As a corollary, I'll propose that every line of code you write (or every line of code you generate through C++ template facilities) is a line that costs you something in maintenance. Thus, the cheapest code is the code that doesn't exist.&lt;br /&gt;&lt;br /&gt;It seems obvious, and it should be. The problem (imho) is that programmers are drinking the kool-aid. There's this myth that  programming is a really hard, complicated problem. For some classes of programming, it's true. It is hard. Most programming is not hard, though. You think about a problem, you think about how you would sove the problem by hand and you speak another language to solve the problem in a more automatic way.&lt;br /&gt;&lt;br /&gt;Unfortunately, it seems that oftentimes programmers overthink problems. They solve problems that don't exist or they try to build scaffolding around an otherwise simple problem. And that results in an unmaintainable blob. This is often done in the name of 'code reuse.' Debunking the notion that code reuse is always a good idea is a subject for another post, though.&lt;br /&gt;&lt;br /&gt;Cheers.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/1346661302680828934-5295649031178928012?l=lexamb.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://lexamb.blogspot.com/feeds/5295649031178928012/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=1346661302680828934&amp;postID=5295649031178928012' title='1 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/1346661302680828934/posts/default/5295649031178928012'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/1346661302680828934/posts/default/5295649031178928012'/><link rel='alternate' type='text/html' href='http://lexamb.blogspot.com/2008/09/cheapest-code-is-code-that-doesnt-exist.html' title='The cheapest code is the code that doesn&apos;t exist'/><author><name>McJohn</name><uri>http://www.blogger.com/profile/14617707594789305466</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='29' src='http://1.bp.blogspot.com/_1qhC8-5QU-s/SM2Cg_QWd3I/AAAAAAAAA5I/3PCIIiOi9gE/s1600-R/orly.jpeg'/></author><thr:total>1</thr:total></entry><entry><id>tag:blogger.com,1999:blog-1346661302680828934.post-3428549868878728344</id><published>2008-09-07T23:30:00.003-06:00</published><updated>2008-09-07T23:42:33.847-06:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='personal'/><title type='text'>Go read Cass's blog</title><content type='html'>Cass used to work with me at NVIDIA. In fact, when I got hired at NVIDIA, he was my mentor in arch. He's stupidly brilliant, and yet somehow not a social retard, which is great. He's over at id now, which is a bummer because he and his family aren't in Austin anymore, but it's cool going up to visit them next to Lake Elron Hubbard.&lt;br /&gt;&lt;br /&gt;Anyways, he writes somewhat more regularly than I do over at his blog &lt;a href="http://xyzw.r3.nu/"&gt;The Hypothetical Third Dimension&lt;/a&gt;. Some of it is math-centric, but he usually follows that up with a segway into his second favorite topic: beer.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/1346661302680828934-3428549868878728344?l=lexamb.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://lexamb.blogspot.com/feeds/3428549868878728344/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=1346661302680828934&amp;postID=3428549868878728344' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/1346661302680828934/posts/default/3428549868878728344'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/1346661302680828934/posts/default/3428549868878728344'/><link rel='alternate' type='text/html' href='http://lexamb.blogspot.com/2008/09/go-read-casss-blog.html' title='Go read Cass&apos;s blog'/><author><name>McJohn</name><uri>http://www.blogger.com/profile/14617707594789305466</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='29' src='http://1.bp.blogspot.com/_1qhC8-5QU-s/SM2Cg_QWd3I/AAAAAAAAA5I/3PCIIiOi9gE/s1600-R/orly.jpeg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-1346661302680828934.post-1023128868366073055</id><published>2008-08-30T21:53:00.004-06:00</published><updated>2008-08-30T21:59:26.468-06:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='personal'/><category scheme='http://www.blogger.com/atom/ns#' term='vacation'/><title type='text'>Has it really been 7 months?</title><content type='html'>It seems like it's been awhile since I've posted, and if google isn't lieing (and I have no reason to assume they would), it's been &lt;span style="font-weight: bold;"&gt;eight months!&lt;/span&gt; Anyways, life has been good.&lt;br /&gt;&lt;br /&gt;I'm currently out visiting my dad for a month or so in Lake City, CO.&lt;br /&gt;&lt;br /&gt;&lt;iframe width="425" height="350" frameborder="0" scrolling="no" marginheight="0" marginwidth="0" src="http://maps.google.com/maps?f=q&amp;amp;hl=en&amp;amp;geocode=&amp;amp;q=lake+city,+co&amp;amp;ie=UTF8&amp;amp;ll=38.048091,-107.306042&amp;amp;spn=0.067198,0.145741&amp;amp;t=h&amp;amp;z=13&amp;amp;iwloc=addr&amp;amp;output=embed&amp;amp;s=AARTsJqNtCHthqzTKijfm6FG0r8QomC9Zw"&gt;&lt;/iframe&gt;&lt;br /&gt;&lt;small&gt;&lt;a href="http://maps.google.com/maps?f=q&amp;amp;hl=en&amp;amp;geocode=&amp;amp;q=lake+city,+co&amp;amp;ie=UTF8&amp;amp;ll=38.048091,-107.306042&amp;amp;spn=0.067198,0.145741&amp;amp;t=h&amp;amp;z=13&amp;amp;iwloc=addr&amp;amp;source=embed" style="color:#0000FF;text-align:left"&gt;View Larger Map&lt;/a&gt;&lt;/small&gt;&lt;br /&gt;&lt;br /&gt;Lake City is a paradise if you are into outdoor activities such as hiking, camping, fishing or even hunting (I'm not a hunter, but apparently it is a popular activity here).&lt;br /&gt;&lt;br /&gt;Samus and I have been hanging out with my dad and his two dogs, and we've been having a good time. We've been on a few hikes since we got here, including Williams Creek and Water Dog Lake Trail.&lt;br /&gt;&lt;br /&gt;Our trip is half over, and while I'm having a great time, I'm ready to get home to see TheFish as well.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/1346661302680828934-1023128868366073055?l=lexamb.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://lexamb.blogspot.com/feeds/1023128868366073055/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=1346661302680828934&amp;postID=1023128868366073055' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/1346661302680828934/posts/default/1023128868366073055'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/1346661302680828934/posts/default/1023128868366073055'/><link rel='alternate' type='text/html' href='http://lexamb.blogspot.com/2008/08/has-it-really-been-7-months.html' title='Has it really been 7 months?'/><author><name>McJohn</name><uri>http://www.blogger.com/profile/14617707594789305466</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='29' src='http://1.bp.blogspot.com/_1qhC8-5QU-s/SM2Cg_QWd3I/AAAAAAAAA5I/3PCIIiOi9gE/s1600-R/orly.jpeg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-1346661302680828934.post-8402571491389718014</id><published>2008-01-05T13:52:00.000-06:00</published><updated>2008-01-05T14:10:41.357-06:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='big.lebowski'/><category scheme='http://www.blogger.com/atom/ns#' term='trivia'/><category scheme='http://www.blogger.com/atom/ns#' term='personl'/><title type='text'>The Dude Abides</title><content type='html'>I've been off work for the last couple of weeks, mostly hanging out with my wife and the in-laws.&lt;br /&gt;&lt;br /&gt;I had a chance to watch The Big Lebowski a few times, which is one of the greatest comedies of all time. Apparently the movie sticks to me more than I'd like to admit:&lt;br /&gt;&lt;br /&gt;&lt;div id="testResultInfo"&gt;&lt;br /&gt;   &lt;h1&gt;&lt;!--t--&gt;Your Score&lt;!--/t--&gt;: &lt;span&gt;His Dudeness&lt;/span&gt;&lt;/h1&gt;&lt;br /&gt;   &lt;h2&gt;You scored 89%!&lt;/h2&gt;&lt;br /&gt;    &lt;div id="testResultInfoImg"&gt;&lt;img src="http://is0.okcupid.com/mt_pics/558/5587351814088564627/8349099354053551534-3.jpg"   style="float: left; margin: 4px;"/&gt;"Huh? wait wait, let me, let me explain something to you. Uh, I am not Mr. Lebowski; you're Mr. Lebowski.  I'm the Dude.  So that's what you call me. You know, uh, That, or uh, his  Dudeness, or uh Duder, or uh El Duderino, if, you know, you're not into the whole brevity thing--uh."&lt;br /&gt;&lt;/div&gt;&lt;p&gt;&lt;br /&gt;You are the Dude, you're keeping your mind limber by sticking to a strict drug regimen, you enjoy bowling, driving around, the occasional acid flash back.  Maybe a little too much, actually, as you have failed in even the modest task that is your charge, and couldn't get to the next level of this test.  But then, what else would the Dude do?&lt;/p&gt;&lt;/div&gt;Link: &lt;a href="http://www.okcupid.com/tests/8349099354053551534/Big-Lebowski-Trivia"&gt;The Big Lebowski Trivia Test&lt;/a&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/1346661302680828934-8402571491389718014?l=lexamb.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://lexamb.blogspot.com/feeds/8402571491389718014/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=1346661302680828934&amp;postID=8402571491389718014' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/1346661302680828934/posts/default/8402571491389718014'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/1346661302680828934/posts/default/8402571491389718014'/><link rel='alternate' type='text/html' href='http://lexamb.blogspot.com/2008/01/dude-abides.html' title='The Dude Abides'/><author><name>McJohn</name><uri>http://www.blogger.com/profile/14617707594789305466</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='29' src='http://1.bp.blogspot.com/_1qhC8-5QU-s/SM2Cg_QWd3I/AAAAAAAAA5I/3PCIIiOi9gE/s1600-R/orly.jpeg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-1346661302680828934.post-8166299317247240810</id><published>2007-12-15T13:15:00.002-06:00</published><updated>2007-12-15T13:18:11.737-06:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='five.year.olds'/><category scheme='http://www.blogger.com/atom/ns#' term='fight'/><title type='text'>I am a five-year-old ass-kicking machine</title><content type='html'>Through digg, I found this amusing site... You complete a shorty survey and it indicates how many five year olds you could take in a fight.&lt;br /&gt;&lt;br /&gt;I'm proud to say that I could take 32 of the little jerks in a fight.&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;a href="http://www.justsayhi.com/bb/fight5" style="background: transparent url(http://assets.justsayhi.com/badges/195/950/fight5.r09oznckri.jpg) no-repeat scroll 0% 50%; display: block; -moz-background-clip: -moz-initial; -moz-background-origin: -moz-initial; -moz-background-inline-policy: -moz-initial; width: 296px; height: 84px; font-family: Arial,sans-serif; font-size: 42px; color: rgb(255, 255, 255); text-decoration: none; text-align: center; padding-top: 145px;"&gt;32&lt;/a&gt;&lt;br /&gt;&lt;br /&gt;Check out your score over at their website. (Oh, but make sure you edit the HTML they give you before you post it somewhere, they include a link to a payday advance website. boo).&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/1346661302680828934-8166299317247240810?l=lexamb.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://lexamb.blogspot.com/feeds/8166299317247240810/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=1346661302680828934&amp;postID=8166299317247240810' title='1 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/1346661302680828934/posts/default/8166299317247240810'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/1346661302680828934/posts/default/8166299317247240810'/><link rel='alternate' type='text/html' href='http://lexamb.blogspot.com/2007/12/i-am-five-year-old-ass-kicking-machine.html' title='I am a five-year-old ass-kicking machine'/><author><name>McJohn</name><uri>http://www.blogger.com/profile/14617707594789305466</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='29' src='http://1.bp.blogspot.com/_1qhC8-5QU-s/SM2Cg_QWd3I/AAAAAAAAA5I/3PCIIiOi9gE/s1600-R/orly.jpeg'/></author><thr:total>1</thr:total></entry><entry><id>tag:blogger.com,1999:blog-1346661302680828934.post-5303177795466140050</id><published>2007-09-27T14:22:00.001-06:00</published><updated>2007-09-27T17:39:54.965-06:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='integration'/><category scheme='http://www.blogger.com/atom/ns#' term='perforce'/><category scheme='http://www.blogger.com/atom/ns#' term='software-management'/><title type='text'>Integration strategies</title><content type='html'>We use perforce at work. Although people talk a lot of smack about perforce, I actually find it to be generally quite acceptable at what it does.&lt;br /&gt;&lt;br /&gt;However, I've been hitting some snags as of late. Ultimately, the issue comes down to branching, and how to keep multiple trees in sync for a particular set of tools.&lt;br /&gt;&lt;br /&gt;Although the perforce website has a lot of &lt;a href="http://www.perforce.com/perforce/technical.html"&gt;white papers&lt;/a&gt; on various subjects, I haven't really found anything there that describes what I'm looking for. Even the white paper on &lt;a href="http://www.perforce.com/perforce/branch.html"&gt;branching&lt;/a&gt; describes exactly what I &lt;span style="font-weight: bold;"&gt;don't&lt;/span&gt; want. What I really want is a way to say: "Hey, even though I need to have this file replicated to these 17 different locations, they're really the same file. And if someone changes one of them, I want you to think of that as changing all of them."&lt;br /&gt;&lt;br /&gt;Unfortunately, this is functionality that perforce adamently denies has any place in the real world. Well, here I am, maintaining around 17 trees, and I'm telling you that this functionality would probably remove about three hours a day from my work day.&lt;br /&gt;&lt;br /&gt;The usual way we deal with this is to relocate the relevant tools outside of the branched codebase, and then point everything at the new location. Unfortunately, this is a manual, time consuming process. It sucks, and I don't like repeating work again and again (which is pretty much what I'm doing now).&lt;br /&gt;&lt;br /&gt;So the question I put out there is: &lt;span style="font-style: italic;"&gt;does anyone have a better way than these two methods?&lt;/span&gt; To sum up, here they were:&lt;br /&gt;&lt;ul&gt;&lt;li&gt;Code lives in an area that's branched, and changes need to be propogated everywhere to keep everything in sync.&lt;/li&gt;&lt;li&gt;Code lives in an area that isn't branched, but this is usually a hindsight realization so it requires manual updates of all related tools to pick the tool up from the new location.&lt;/li&gt;&lt;/ul&gt;Oh, and as an added complication, what do you do when 99% of the code is independent of the release branch, but the last little bit depends on code in a branch? &lt;span style="font-weight: bold;"&gt;What then?&lt;/span&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/1346661302680828934-5303177795466140050?l=lexamb.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://lexamb.blogspot.com/feeds/5303177795466140050/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=1346661302680828934&amp;postID=5303177795466140050' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/1346661302680828934/posts/default/5303177795466140050'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/1346661302680828934/posts/default/5303177795466140050'/><link rel='alternate' type='text/html' href='http://lexamb.blogspot.com/2007/09/integration-strategies.html' title='Integration strategies'/><author><name>McJohn</name><uri>http://www.blogger.com/profile/14617707594789305466</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='29' src='http://1.bp.blogspot.com/_1qhC8-5QU-s/SM2Cg_QWd3I/AAAAAAAAA5I/3PCIIiOi9gE/s1600-R/orly.jpeg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-1346661302680828934.post-3091396164852839324</id><published>2007-09-26T21:13:00.000-06:00</published><updated>2007-09-26T21:27:41.677-06:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='books'/><category scheme='http://www.blogger.com/atom/ns#' term='programming'/><title type='text'>Where are the books for experienced programmers?</title><content type='html'>Recently, I've been reading books on how to develop websites using "Web 2.0" techniques (basically AJAX). What I've noticed about these books--and even other coding books in my library--is that there is a serious lack of books for the experienced, well-disciplined programmer.&lt;br /&gt;&lt;br /&gt;I honestly find it a little insulting that every book on C++ feels the need to re-explain to me APIE (Abstraction, Polymorphism, Inheritance, Encapsulation). As does every Java book. As does every Python book. Even books that claim to cater to the experienced or advanced programmer &lt;span style="font-weight:bold;"&gt;always&lt;/span&gt; recover these topics. &lt;br /&gt;&lt;br /&gt;Of course, I've heard of projects like &lt;a href="http://rosettacode.org/wiki/Main_Page"&gt;Rosetta Code&lt;/a&gt;, but that's not really what I'm looking for either. For one thing, there are only 87 examples, not even covering topics I'm really interested in. (An example of a topic I'd like to see is, "How to remove items from a collection based on a predicate?") Yes, it's wiki. Yes, I could go write the article myself. No thanks. &lt;br /&gt;&lt;br /&gt;I guess what I really want to find are books that tell you the right way to perform tasks in a certain language, or why you would prefer one method over another. For example, in which situations should I prefer to use a vector instead of a map? (I'll give you a hint, even in key-value situations there are reasons to sometimes prefer a vector to a map).&lt;br /&gt;&lt;br /&gt;For example, let's say I'm writing a new piece of C++ code. Should I use the stl? &lt;span style="font-style:italic;"&gt;Almost always.&lt;/span&gt; Is there a good logging subsystem out there that I could use to avoid rewriting a new one? Are there any libraries I should avoid? &lt;span style="font-style:italic;"&gt;Boost.&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;I don't need to relearn why I should write objects or what should go into an object. Tell me how to define an object, how to use inheritance, how abstraction is implemented. Give me actual, realistic costs of making design tradeoffs. For example, how much will it cost me to call a virtual function? Is there a way I can mitigate this inside of a single class, if I really know what the type of the class is? &lt;br /&gt;&lt;br /&gt;I'm sure, one of these days, someone will get it. Till then, I'll just continue to suffer in silence.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/1346661302680828934-3091396164852839324?l=lexamb.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://lexamb.blogspot.com/feeds/3091396164852839324/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=1346661302680828934&amp;postID=3091396164852839324' title='1 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/1346661302680828934/posts/default/3091396164852839324'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/1346661302680828934/posts/default/3091396164852839324'/><link rel='alternate' type='text/html' href='http://lexamb.blogspot.com/2007/09/where-are-books-for-experienced.html' title='Where are the books for experienced programmers?'/><author><name>McJohn</name><uri>http://www.blogger.com/profile/14617707594789305466</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='29' src='http://1.bp.blogspot.com/_1qhC8-5QU-s/SM2Cg_QWd3I/AAAAAAAAA5I/3PCIIiOi9gE/s1600-R/orly.jpeg'/></author><thr:total>1</thr:total></entry><entry><id>tag:blogger.com,1999:blog-1346661302680828934.post-6171078249193964607</id><published>2007-09-26T12:23:00.000-06:00</published><updated>2007-09-26T12:32:38.078-06:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='multithreaded'/><category scheme='http://www.blogger.com/atom/ns#' term='python'/><title type='text'>Thread-safe printing in Python</title><content type='html'>Recently, a coworker was having issues printing from multiple threads in his python program.&lt;br /&gt;&lt;br /&gt;After a little googling, I came upon &lt;a href="http://lists.helixcommunity.org/pipermail/ribosome-dev/2006-July/001756.html"&gt;this discussion&lt;/a&gt; which covered this exact topic.&lt;br /&gt;&lt;br /&gt;At the lowest level, python appears to be threadsafe wrt a single file handle, which is good. Unfortunately, when you use the print statement, an implicit '\n' is added for you, and this apparently results in a second low-level write call, which is a seperate mutex grab.&lt;br /&gt;&lt;br /&gt;As a result, you often get lines of text that look like this:&lt;br /&gt;&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;This text comes from Thread 1.This text comes from Thread2.&lt;br /&gt;&lt;span style="font-style:italic;"&gt;&amp;lt; Empty Line &amp;gt;&lt;/span&gt;&lt;br /&gt;This text comes from Thread 1.This text comes from Thread2.&lt;br /&gt;&lt;span style="font-style:italic;"&gt;&amp;lt; Empty Line &amp;gt;&lt;/span&gt;&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;In this situation, you basically have two choices. You could replace your print statements with a custom written print function that grabs a mutex and adds a trailing '\n', or you could use the logging module. Even though the threadsafe print module is very short, I'd still suggest that the 'right thing' to do here is to just use the logging module. &lt;br /&gt;&lt;br /&gt;When you decide in the future that you want to log to a tcp connection on another server, your future self will pat yourself on the back that you just used logging up front.&lt;br /&gt;&lt;br /&gt;Cheers,&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/1346661302680828934-6171078249193964607?l=lexamb.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://lexamb.blogspot.com/feeds/6171078249193964607/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=1346661302680828934&amp;postID=6171078249193964607' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/1346661302680828934/posts/default/6171078249193964607'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/1346661302680828934/posts/default/6171078249193964607'/><link rel='alternate' type='text/html' href='http://lexamb.blogspot.com/2007/09/thread-safe-printing-in-python.html' title='Thread-safe printing in Python'/><author><name>McJohn</name><uri>http://www.blogger.com/profile/14617707594789305466</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='29' src='http://1.bp.blogspot.com/_1qhC8-5QU-s/SM2Cg_QWd3I/AAAAAAAAA5I/3PCIIiOi9gE/s1600-R/orly.jpeg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-1346661302680828934.post-5943192294140018997</id><published>2007-09-25T07:00:00.000-06:00</published><updated>2007-09-25T10:13:47.915-06:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='teasing'/><category scheme='http://www.blogger.com/atom/ns#' term='personal'/><category scheme='http://www.blogger.com/atom/ns#' term='olivier'/><title type='text'>Olivier could be a Visual Studio Installer Model</title><content type='html'>One of my fellow nvidians, Olivier, writes in his blog &lt;a href="http://ogiroux.blogspot.com"&gt;Mutable Conclusions&lt;/a&gt;. Prominently on his website, he features a picture of himself. I've included a copy here:&lt;br /&gt;&lt;br /&gt;&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://1.bp.blogspot.com/_1qhC8-5QU-s/RviJgBuG0eI/AAAAAAAAAhw/Kz8AqYi-oF8/s1600-h/Olivier.jpg"&gt;&lt;img style="display:block; margin:0px auto 10px; text-align:center;cursor:pointer; cursor:hand;" src="http://1.bp.blogspot.com/_1qhC8-5QU-s/RviJgBuG0eI/AAAAAAAAAhw/Kz8AqYi-oF8/s320/Olivier.jpg" border="0" alt="Olivier, in the flesh."id="BLOGGER_PHOTO_ID_5113988560189837794" /&gt;&lt;/a&gt;&lt;br /&gt;&lt;br /&gt;I've teased him about this mercilessly, but I think his picture would've been a perfect include in the Microsoft Visual Studio installer. Judge for yourself.&lt;br /&gt;&lt;br /&gt;&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://3.bp.blogspot.com/_1qhC8-5QU-s/RviJKhuG0dI/AAAAAAAAAho/RyR7dy8jtTk/s1600-h/Orcas+Install1.png"&gt;&lt;img style="display:block; margin:0px auto 10px; text-align:center;cursor:pointer; cursor:hand;" src="http://3.bp.blogspot.com/_1qhC8-5QU-s/RviJKhuG0dI/AAAAAAAAAho/RyR7dy8jtTk/s320/Orcas+Install1.png" border="0" alt="(Thanks, diditwith: http://diditwith.net/default,date,2007-03-15.aspx)" id="BLOGGER_PHOTO_ID_5113988190822650322" /&gt;&lt;/a&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/1346661302680828934-5943192294140018997?l=lexamb.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://lexamb.blogspot.com/feeds/5943192294140018997/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=1346661302680828934&amp;postID=5943192294140018997' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/1346661302680828934/posts/default/5943192294140018997'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/1346661302680828934/posts/default/5943192294140018997'/><link rel='alternate' type='text/html' href='http://lexamb.blogspot.com/2007/09/olivier-could-be-visual-studio.html' title='Olivier could be a Visual Studio Installer Model'/><author><name>McJohn</name><uri>http://www.blogger.com/profile/14617707594789305466</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='29' src='http://1.bp.blogspot.com/_1qhC8-5QU-s/SM2Cg_QWd3I/AAAAAAAAA5I/3PCIIiOi9gE/s1600-R/orly.jpeg'/></author><media:thumbnail xmlns:media='http://search.yahoo.com/mrss/' url='http://1.bp.blogspot.com/_1qhC8-5QU-s/RviJgBuG0eI/AAAAAAAAAhw/Kz8AqYi-oF8/s72-c/Olivier.jpg' height='72' width='72'/><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-1346661302680828934.post-3797531175144363401</id><published>2007-09-24T21:50:00.000-06:00</published><updated>2007-09-24T22:08:56.030-06:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='comics'/><category scheme='http://www.blogger.com/atom/ns#' term='personal'/><title type='text'>I'm a comic!</title><content type='html'>A friend of mine is a (yet undiscovered) great artist. She does phenomenal 2-D work, and keeps up a &lt;a href="http://alifeinscribbles.blogspot.com/"&gt;daily sketch site&lt;/a&gt;. I only recently discovered her site, but I was excited to see that I'd been &lt;a href="http://alifeinscribbles.blogspot.com/2007/08/game-night.html"&gt;rendered as a comic&lt;/a&gt;. &lt;br /&gt;&lt;br /&gt;I feel the representation is fairly accurate.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/1346661302680828934-3797531175144363401?l=lexamb.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://lexamb.blogspot.com/feeds/3797531175144363401/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=1346661302680828934&amp;postID=3797531175144363401' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/1346661302680828934/posts/default/3797531175144363401'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/1346661302680828934/posts/default/3797531175144363401'/><link rel='alternate' type='text/html' href='http://lexamb.blogspot.com/2007/09/im-comic.html' title='I&apos;m a comic!'/><author><name>McJohn</name><uri>http://www.blogger.com/profile/14617707594789305466</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='29' src='http://1.bp.blogspot.com/_1qhC8-5QU-s/SM2Cg_QWd3I/AAAAAAAAA5I/3PCIIiOi9gE/s1600-R/orly.jpeg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-1346661302680828934.post-1534132660837412341</id><published>2007-07-15T14:48:00.002-06:00</published><updated>2011-06-08T10:54:32.674-06:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='good.habits'/><category scheme='http://www.blogger.com/atom/ns#' term='python'/><category scheme='http://www.blogger.com/atom/ns#' term='code'/><title type='text'>Python: Enforcing Interface Requirements</title><content type='html'>I write a moderate amount of Python code for work. Not necessarily as much as I'd like, but that's a topic for another post.&lt;br /&gt;&lt;br /&gt;A short while back, one of my coworkers asked this question at lunch: "If I'm writing an interface that I want other people to be able to use with their own new types, how do I ensure that I write my code in such a way that they can clearly and easily understand the interface that they need to implement? As an additional problem, how do I ensure that I don't accidentally break my interface requirements?"&lt;br /&gt;&lt;br /&gt;Now, if you've written much python code at all, you're probably familiar with the term 'duck typing', which basically exerts that "if it quacks like a duck and walks like a duck, it might as well be a duck." This is probably one of python's greatest strengths as it reduces the syntactic complexity of 'templated' code; by default all functions work with all types that match the implicit interface that is their implementation.&lt;br /&gt;&lt;br /&gt;The unfortunate side effect of duck-typing is that in order to see the exact interface that you would need to implement for an API to work with, you'd have to pore through the entire codebase of the API (!!). Clearly, that wouldn't work at all. After a bit of discussion, we came up with the following solution:&lt;br /&gt;&lt;ul&gt;   &lt;li&gt;API Interfaces should begin by asserting that the arguments passed in match a specified, required Interface. This takes care of the first half of the problem, because a developer needs only to look just inside the function to determine which Interface class needs to be implemented (or derived from, if the developer so chooses).&lt;/li&gt;   &lt;li&gt;The objects passed in should be limited (for the duration of the function) to only expose the explicit members that the interface allows for. This solves the second half of the problem, because it makes it impossible to peek under the covers other than what the Interface allows for.&lt;/li&gt; &lt;/ul&gt; After some discussion (and mail on the topic), we came up with the following implementation:&lt;br /&gt;&lt;span style="color: rgb(0, 0, 0);font-size:85%;"&gt;&lt;pre&gt;&lt;span style="color: rgb(0, 0, 0);font-family:courier new;"&gt;class __frozen_iface (object):&lt;/span&gt;&lt;br /&gt;&lt;span style="color: rgb(0, 0, 0);font-family:courier new;"&gt;    def __init__ (self, ifspec, instance):&lt;/span&gt;&lt;br /&gt;&lt;span style="color: rgb(0, 0, 0);font-family:courier new;"&gt;        for member in inspect.getmembers(ifspec):         &lt;/span&gt;&lt;br /&gt;&lt;span style="color: rgb(0, 0, 0);font-family:courier new;"&gt;            if member[0].startswith ("__"):&lt;/span&gt;&lt;br /&gt;&lt;span style="color: rgb(0, 0, 0);font-family:courier new;"&gt;                continue&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span style="color: rgb(0, 0, 0);font-family:courier new;"&gt;            if not inspect.ismethod (member[1]):&lt;/span&gt;&lt;br /&gt;&lt;span style="color: rgb(0, 0, 0);font-family:courier new;"&gt;                continue&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span style="color: rgb(0, 0, 0);font-family:courier new;"&gt;            instance_attr = getattr (instance, member[0])&lt;/span&gt;&lt;br /&gt;&lt;span style="color: rgb(0, 0, 0);font-family:courier new;"&gt;            if instance_attr.im_func == member[1].im_func:&lt;/span&gt;&lt;br /&gt;&lt;span style="color: rgb(0, 0, 0);font-family:courier new;"&gt;                raise AttributeError, "Class '%s' has interface class implementation of attribute '%s'" % (instance.__class__, member[0])&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span style="color: rgb(0, 0, 0);font-family:courier new;"&gt;            # bypass our internal __setattr__ since that will raise an exception&lt;/span&gt;&lt;br /&gt;&lt;span style="color: rgb(0, 0, 0);font-family:courier new;"&gt;            object.__setattr__ (self, attr_name, instance_attr)&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span style="color: rgb(0, 0, 0);font-family:courier new;"&gt;    def __setattr__ (self, name, value):&lt;/span&gt;&lt;br /&gt;&lt;span style="color: rgb(0, 0, 0);font-family:courier new;"&gt;        # prevent anyone from accidentally assigning new attributes&lt;/span&gt;&lt;br /&gt;&lt;span style="color: rgb(0, 0, 0);font-family:courier new;"&gt;        raise AttributeError, "Attempt to set an attribute '%s' for frozen interface class '%s'" % (name, self)&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span style="color: rgb(0, 0, 0);font-family:courier new;"&gt;def UseInterface(ifspec, instance):&lt;/span&gt;&lt;br /&gt;&lt;span style="color: rgb(0, 0, 0);font-family:courier new;"&gt;    return __frozen_iface (ifspec, instance)&lt;br /&gt;&lt;/span&gt;&lt;/pre&gt;&lt;/span&gt;Then, client code would do something like this:&lt;br /&gt;&lt;span style="color: rgb(0, 0, 0);font-size:85%;"&gt;&lt;pre&gt;&lt;span style="color: rgb(0, 0, 0);font-family:courier new;"&gt;class Renderable(object):&lt;/span&gt;&lt;br /&gt;&lt;span style="color: rgb(0, 0, 0);font-family:courier new;"&gt;    def Draw(self, context):&lt;/span&gt;&lt;br /&gt;&lt;span style="color: rgb(0, 0, 0);font-family:courier new;"&gt;        abstract # Raises an execption if we get here.&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span style="color: rgb(0, 0, 0);font-family:courier new;"&gt;def RenderObject(someRenderable):&lt;/span&gt;&lt;br /&gt;&lt;span style="color: rgb(0, 0, 0);font-family:courier new;"&gt;    someRenderable = UseInterface(Renderable, someRenderable)&lt;/span&gt;&lt;br /&gt;&lt;span style="color: rgb(0, 0, 0);font-family:courier new;"&gt;    dir(someRenderable) # Outputs only 'Draw'&lt;/span&gt;&lt;br /&gt;&lt;/pre&gt;&lt;/span&gt;This could also be trivially wrapped into a decorator so your code could look like this:&lt;div&gt;&lt;span class="Apple-style-span" style="font-family: 'courier new'; font-size: 14px; white-space: pre; "&gt;&lt;br /&gt;&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span class="Apple-style-span" style="font-family: 'courier new'; font-size: 14px; white-space: pre; "&gt;class Renderable(object):&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span class="Apple-style-span" style="font-size: 14px; "&gt;&lt;pre&gt;&lt;span style="color: rgb(0, 0, 0); font-family: 'courier new'; "&gt;    @Interface(Context)&lt;/span&gt;&lt;br /&gt;&lt;span style="color: rgb(0, 0, 0); font-family: 'courier new'; "&gt;    def Draw(self, context):&lt;/span&gt;&lt;br /&gt;&lt;span style="color: rgb(0, 0, 0); font-family: 'courier new'; "&gt;        abstract # Raises an execption if we get here.&lt;/span&gt;&lt;br /&gt;&lt;/pre&gt;&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;PS: Thanks to JimR for the cool implementation of __frozen_iface.&lt;/div&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/1346661302680828934-1534132660837412341?l=lexamb.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://lexamb.blogspot.com/feeds/1534132660837412341/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=1346661302680828934&amp;postID=1534132660837412341' title='2 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/1346661302680828934/posts/default/1534132660837412341'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/1346661302680828934/posts/default/1534132660837412341'/><link rel='alternate' type='text/html' href='http://lexamb.blogspot.com/2007/07/python-enforcing-interface-requirements.html' title='Python: Enforcing Interface Requirements'/><author><name>McJohn</name><uri>http://www.blogger.com/profile/14617707594789305466</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='29' src='http://1.bp.blogspot.com/_1qhC8-5QU-s/SM2Cg_QWd3I/AAAAAAAAA5I/3PCIIiOi9gE/s1600-R/orly.jpeg'/></author><thr:total>2</thr:total></entry><entry><id>tag:blogger.com,1999:blog-1346661302680828934.post-6275653981593105989</id><published>2007-05-04T00:45:00.000-06:00</published><updated>2007-05-04T01:05:49.149-06:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='good.habits'/><category scheme='http://www.blogger.com/atom/ns#' term='c++'/><category scheme='http://www.blogger.com/atom/ns#' term='code'/><title type='text'>Structured Cleanup: Avoiding gotos</title><content type='html'>I think everyone in our profession has to write code, at some point, that grabs multiple resources, then do something.&lt;br /&gt;&lt;br /&gt;And unconditionally, that code needs to make sure to back out any resource acquisitions in cases of intermediate failure, and only back out the resources that are actually grabbed.&lt;br /&gt;&lt;br /&gt;Normally, you see this kind of code for that purpose:&lt;br /&gt;&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;&lt;tt&gt;int doStuff(void) {&lt;br /&gt;  char* a = (char*) malloc(100);&lt;br /&gt;  if (!a) goto failure_a;&lt;br /&gt;&lt;br /&gt;  char* b = (char*) malloc(100);&lt;br /&gt;  if (!b) goto failure_b;&lt;br /&gt;&lt;br /&gt;  // do some stuff&lt;br /&gt;&lt;br /&gt;  // Want to keep a and b around.&lt;br /&gt;  return 0;&lt;br /&gt;&lt;br /&gt;  // Future proofing, for when we add c.&lt;br /&gt;  free(b);&lt;br /&gt;failure_b:&lt;br /&gt;  free(a);&lt;br /&gt;failure_a:&lt;br /&gt;  return -1;&lt;br /&gt;}&lt;br /&gt;&lt;/tt&gt;&lt;tt&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;&lt;/tt&gt;&lt;span style="font-family: verdana;"&gt;I have two problems with this code:&lt;/span&gt;&lt;tt&gt;&lt;br /&gt;&lt;/tt&gt;&lt;ol&gt;&lt;li&gt;Updating this code requires updating both the allocation semantics and the 'free' semantics.&lt;/li&gt;&lt;li&gt;This doesn't lend itself towards any sort of 'pattern' which we can reuse for other things, like grabbing a mutex, semaphore, etc.&lt;/li&gt;&lt;/ol&gt;Instead of the above code, I propose that we use a system of Rollback objects. I haven't seen these in Design Patterns, but maybe they'll make their way into the next release of the book. :)&lt;br /&gt;&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;&lt;span style="font-size:85%;"&gt;&lt;span style="font-family: courier new;"&gt;template &amp;lt;typename T&amp;gt;&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;&lt;tt&gt;class RollbackAllocator&lt;/tt&gt;&lt;br /&gt;&lt;tt&gt;{&lt;br /&gt;  &lt;/tt&gt;&lt;tt&gt;RollbackAllocator&lt;/tt&gt;&lt;tt&gt;(int countToAlloc) : mAllocated(0) { mAllocated = new T[countToAlloc]; }&lt;br /&gt;  ~&lt;/tt&gt;&lt;tt&gt;RollbackAllocator&lt;/tt&gt;&lt;tt&gt;() { if (mAllocated) delete [] mAllocated; }&lt;nobr&gt;&lt;br /&gt;&lt;br /&gt;&lt;wbr&gt;&lt;/nobr&gt;  // For easy-to-read client code, no one should take ownership of allocated&lt;br /&gt;  // objects until&lt;nobr&gt; &lt;/nobr&gt;there are no further failure conditions possible. This same&lt;br /&gt;  // technique can be used for&lt;nobr&gt; &lt;wbr&gt;&lt;/nobr&gt;&lt;br /&gt;  // ANY resource acquisition, whether it's an allocation of memory,&lt;br /&gt;  // grabbing a socket, mutex,&lt;nobr&gt; &lt;/nobr&gt;semaphore, reading a file, etc.&lt;br /&gt;  T* TakeOwnership() { T* retVal = mAllocated; mAllocated = 0; return retVal; }&lt;br /&gt;&lt;br /&gt;        T* mAllocated;&lt;br /&gt;}&lt;br /&gt;&lt;br /&gt;int doStuff(void)&lt;br /&gt;{&lt;nobr&gt;&lt;br /&gt;&lt;wbr&gt;&lt;/nobr&gt;  // If any fails, it'll throw an exception and the others will be cleaned up.&lt;br /&gt;  &lt;/tt&gt;&lt;tt&gt;RollbackAllocator&lt;/tt&gt;&lt;tt&gt;&amp;lt;char&amp;gt; aAlloc(100);&lt;br /&gt;  &lt;/tt&gt;&lt;tt&gt;RollbackAllocator&lt;/tt&gt;&lt;tt&gt;&amp;lt;char&amp;gt; bAlloc(100);&lt;br /&gt;&lt;br /&gt;  // do additional things that could fail.            &lt;br /&gt;&lt;br /&gt;  a = aAlloc.TakeOwnership();&lt;br /&gt;  b = bAlloc.TakeOwnership();&lt;br /&gt;&lt;br /&gt;  return 0;&lt;br /&gt;}&lt;/tt&gt;&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;The beauty of this code is its simplicity. By taking advantage of the guarantee that destructors will always be called upon exiting scope*, we ensure that a and b will always either be dealt with properly, or will be freed. Furthermore, while we &lt;span style="font-weight: bold;"&gt;do&lt;/span&gt; have to update the function in two places when we add a new resource acquisition, we do not have to worry about what order we TakeOwnership! Additionally, this pattern lends itself towards all sorts of resource acquisitions, whether they are mutex grabs, file reads, allocations as we've done above, etc. We can make this pattern fit virtually any resource acquisition pattern, and ensure that we have consistent, future-proof, goto-free code.&lt;br /&gt;&lt;br /&gt;Don't get me wrong, gotos have their place. Just not in structured cleanup code.&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;span style="font-size:85%;"&gt;* C++ doesn't always guarantee that destructors will be called. Any abnormal program termination, including explicit calls to exit, abort, terminate, pure virtual calls, exceptions thrown from exceptions (or destructors), or infinite loops will prevent destructors from being called.&lt;/span&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/1346661302680828934-6275653981593105989?l=lexamb.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://lexamb.blogspot.com/feeds/6275653981593105989/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=1346661302680828934&amp;postID=6275653981593105989' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/1346661302680828934/posts/default/6275653981593105989'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/1346661302680828934/posts/default/6275653981593105989'/><link rel='alternate' type='text/html' href='http://lexamb.blogspot.com/2007/05/structured-cleanup-avoiding-gotos.html' title='Structured Cleanup: Avoiding gotos'/><author><name>McJohn</name><uri>http://www.blogger.com/profile/14617707594789305466</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='29' src='http://1.bp.blogspot.com/_1qhC8-5QU-s/SM2Cg_QWd3I/AAAAAAAAA5I/3PCIIiOi9gE/s1600-R/orly.jpeg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-1346661302680828934.post-1122164754159103166</id><published>2007-04-25T10:59:00.000-06:00</published><updated>2007-04-25T11:58:27.199-06:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='intro'/><title type='text'>I don't normally do this...</title><content type='html'>&lt;span style="font-family: verdana;font-size:100%;" &gt;&lt;span style="font-size:85%;"&gt;... &lt;span style="font-style: italic;"&gt;journaling&lt;/span&gt; (I refuse to call this blogging) is not something I normally do.&lt;br /&gt;&lt;br /&gt;Anyways, my actual website, &lt;a href="http://lexical-ambiguity.com"&gt;Lexical-Ambiguity&lt;/a&gt; is perpetually down because I'm too busy with other things to actually spend time making it work.&lt;br /&gt;&lt;br /&gt;Here, the three of you that read this will probably find the following categories of information:&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;&lt;ul&gt;&lt;li&gt;&lt;span style="font-family: verdana;font-size:100%;" &gt;&lt;span style="font-size:85%;"&gt;Code related topics&lt;/span&gt;&lt;/span&gt;&lt;/li&gt;&lt;li&gt;&lt;span style="font-family: verdana;font-size:100%;" &gt;&lt;span style="font-size:85%;"&gt;Exercise, fitness and weight-lifting information&lt;/span&gt;&lt;/span&gt;&lt;/li&gt;&lt;li&gt;&lt;span style="font-family: verdana;font-size:100%;" &gt;&lt;span style="font-size:85%;"&gt;Stuff about gaming&lt;/span&gt;&lt;/span&gt;&lt;/li&gt;&lt;li&gt;&lt;span style="font-family: verdana;font-size:100%;" &gt;&lt;span style="font-size:85%;"&gt;Stuff about books&lt;/span&gt;&lt;/span&gt;&lt;/li&gt;&lt;/ul&gt;&lt;span style="font-family: verdana;font-size:100%;" &gt;&lt;span style="font-size:85%;"&gt;That's pretty much all I've got right now. Maybe this will become more natural soon-ish. Don't color me hopeful, though.&lt;/span&gt;&lt;/span&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/1346661302680828934-1122164754159103166?l=lexamb.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://lexamb.blogspot.com/feeds/1122164754159103166/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=1346661302680828934&amp;postID=1122164754159103166' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/1346661302680828934/posts/default/1122164754159103166'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/1346661302680828934/posts/default/1122164754159103166'/><link rel='alternate' type='text/html' href='http://lexamb.blogspot.com/2007/04/i-dont-normally-do-this.html' title='I don&apos;t normally do this...'/><author><name>McJohn</name><uri>http://www.blogger.com/profile/14617707594789305466</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='29' src='http://1.bp.blogspot.com/_1qhC8-5QU-s/SM2Cg_QWd3I/AAAAAAAAA5I/3PCIIiOi9gE/s1600-R/orly.jpeg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-1346661302680828934.post-6365190858442002552</id><published>2006-08-28T20:09:00.000-06:00</published><updated>2010-06-06T20:14:44.838-06:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='vacation'/><category scheme='http://www.blogger.com/atom/ns#' term='hiking'/><title type='text'>Oh my God!! Are you serious?!</title><content type='html'>On Saturday, Jim and I attempted to summit &lt;a href="http://www.14ers.com/photos/peakmain.php?peak=Handies+Peak"&gt;Handies&lt;/a&gt;.&lt;br /&gt;&lt;br /&gt;We got up at 6:30 am, and finished prepping for our trip: filling our Camel Backs and eating a light breakfast of cereal. We commented on how neither of us had slept well. In addition to the fact that we both were really excited about attempting to climb our first fourteener of the trip, a large cold front moved into the area around 3:00 am, bringing lots of rain with it.&lt;br /&gt;&lt;br /&gt;We left the house just after 7:00 in the Tahoe, and headed towards &lt;a href="http://www.14ers.com/routemain.php?route=hand1&amp;amp;peak=Handies%20Peak"&gt;American Basin&lt;/a&gt; and Handies. As we came into view of Lake San Cristobal, we saw a sight that neither of us were prepared for: snow atop all of the mountains in the distance. I was concerned that the snow might prevent us from even reaching the trailhead, let alone summiting.&lt;br /&gt;&lt;br /&gt;We drove on, shocked at how much snow had fallen, and arrived at the trailhead around 9:00. There wasn’t any snow in the basin itself, but all of the mountains surrounding the basin were totally covered. Shortly after we geared up for our hike, another vehicle arrived. We spoke with the driver for a few minutes, and he was also planning on hiking Handies. I joked with him that he would pass us in a few minutes, and he laughed but said he certainly wouldn’t. He said he was from Denver and the extra 5000′ of elevation was hard on him.&lt;br /&gt;&lt;br /&gt;We started our hike up at 9:15, and signed into the log book about 10 minutes later. Five minutes later, a thunderstorm–complete with sleet–blew into the basin from the southwest. The storm totally snuck up on us, and we had just about decided to go back down and wait it out in the car when it cleared off. Jim and I discussed giving up for the day so I could go back and get more equipment (gloves, rain pants and my waterproof boots), but we decided to push on.&lt;br /&gt;&lt;br /&gt;About 30 minutes later, we came to a fork in the trail. Unfortunately, neither of us had remembered to bring a map, so we were at a loss as to which way to go. Jim decided to go on up and scout ahead, and I said I’d wait for “Denver” to catch up with us, as he appeared to know what was going on. After “Denver” caught up with us, we decided to all hike together. We introduced ourselves, and “Denver” became Joe. We learned that Joe was an experienced 14er, and we were glad for the company. (At the time, he had summited 28 fourteeners, with plans to do seven more during the week).&lt;br /&gt;&lt;br /&gt;The next two hours were largely uneventful, except that Jim and Joe were able to keep better pace than myself. I’m not the most physically fit person on earth to begin with, but being 10-14,000′ higher than my normal stomping grounds was hard on me. Plus the fact that my shoes were totally waterlogged and provided little traction on the 2-4″ of snow we were encountering made for a trip where I was roughly 1/2 a rest behind Jim and Joe.&lt;br /&gt;&lt;br /&gt;About 200′ (vertical) from the summit, it got very interesting. We heard thunder coming in over the same southwestern portion of the basin. Only this time, instead of being surrounded by cliffs, we were one ridge from the summit. We discussed for a minute or two whether we should turn around and go back or whether we should press on to the summit. I wanted to go back, but said nothing because I really, really wanted to finish up Handies. Jim and Joe clearly also had summit fever and we decided that with 10 minutes of effort we could be at the summit. We would stick around at the summit for a minute or two, rest, and then head back down as quickly as possible.&lt;br /&gt;&lt;br /&gt;Jim and Joe started heading up, when we heard another thunderclap, even closer than the last. Joe turned and began to run towards us, indicating that it was time to go. He asked us quickly if we knew what to do for lighting. For those of you who don’t, (I’d be with you), I’ll reiterate what he said:&lt;br /&gt;&lt;br /&gt;&lt;ul&gt;&lt;li&gt;If you feel a tingling, static charge feeling, that means that the ground around you is charged for a lightning strike.&lt;/li&gt;&lt;li&gt;Assume the lightning strike position. The position is crouched on the balls of your feet, your heels together, your hands over your ears and your eyes closed. You want to form a ball balancing on your feet.&lt;/li&gt;&lt;li&gt;Direct strikes are generally fatal.&lt;/li&gt;&lt;li&gt;Lightning travels along the ground. An indirect strike can cause your heart to stop, but usually (70%) you can be revived with CPR.&lt;/li&gt;&lt;li&gt;When caught in a thunderstorm, members of the party should keep at least 100′ between each other. The reason is that if one person is struck, the other members of the party can revive them.&lt;br /&gt;&lt;/li&gt;&lt;/ul&gt;Jim and I were, needless to say, terrified. Joe took out his hiking poles and began to run down the mountain. Not down the trail, the way we had come up, but straight down the mountain. After a single switchback, I realized that Joe was getting down the mountain significantly faster than I was, and I decided to take off straight after him.&lt;br /&gt;&lt;br /&gt;I fell several times, and my hands began to get tingly. Remembering his warning, I yelled an expletive and tucked into the crouch position. I threw the metal walking stick I was using as far as I could away from me (horizontally, so I could recover it later). From behind me I heard Jim yell “Oh my God!! Are you serious?” I yelled back that I was and waited for the worst.&lt;br /&gt;&lt;br /&gt;It never came. Thirty seconds later I realized that the tingling I was feeling in my hands was the result of their being wet and cold–from falling in the snow–not static buildup. I ran over and grabbed my walking stick and again began running down the mountain. The storm intensified, sending sleet nearly horizontally at our faces, and dark, frightening clouds up the basin towards us. I can safely say that I’ve never run for my life before… This was a first for me.&lt;br /&gt;&lt;br /&gt;Twenty-nine minutes after we had began our descent we all caught up with one another. We had managed to drop 1000′ (vertical) in less than half an hour–the ascent up the same distance had taken us nearly three hours. By this time, the storm had almost entirely abated.&lt;br /&gt;&lt;br /&gt;Although we weren’t technically out of the woods, we all felt much safer as there were now many more advantageous lightning targets than ourselves. We had a reasonably comfortable walk the rest of the way down the trail, back to the register and to the car.&lt;br /&gt;&lt;br /&gt;Back at the car, we met up with some other folks who had been chased off of Redcloud due to the first thunderstorm (that had nearly sent Jim and I back home). We spoke with them for a few minutes and traded numbers with Joe, offering to buy him a meal if he called us when he came to Lake City in the next week.&lt;br /&gt;&lt;br /&gt;I learned a few important things on this trip:&lt;br /&gt;&lt;ol&gt;&lt;li&gt;   Thunderstorms while hiking at altitude are not something to mess around with.&lt;/li&gt;&lt;li&gt;The best path down might not be the path taken up, especially in cases of emergency.&lt;/li&gt;&lt;li&gt;Having a map with you is key at all times.&lt;/li&gt;&lt;li&gt;There are more important things than summitting. (Like living to summit another day).&lt;br /&gt;&lt;/li&gt;&lt;/ol&gt;&lt;br /&gt;Although we didn’t make it to the summit, I felt we made the right decision in turning around. My only regret is that we didn’t turn around and start back sooner.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/1346661302680828934-6365190858442002552?l=lexamb.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://lexamb.blogspot.com/feeds/6365190858442002552/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=1346661302680828934&amp;postID=6365190858442002552' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/1346661302680828934/posts/default/6365190858442002552'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/1346661302680828934/posts/default/6365190858442002552'/><link rel='alternate' type='text/html' href='http://lexamb.blogspot.com/2006/08/oh-my-god-are-you-serious.html' title='Oh my God!! Are you serious?!'/><author><name>McJohn</name><uri>http://www.blogger.com/profile/14617707594789305466</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='29' src='http://1.bp.blogspot.com/_1qhC8-5QU-s/SM2Cg_QWd3I/AAAAAAAAA5I/3PCIIiOi9gE/s1600-R/orly.jpeg'/></author><thr:total>0</thr:total></entry></feed>
