Jump to content
  • Advertisement

Conner McCloud

Member
  • Content count

    8632
  • Joined

  • Last visited

Community Reputation

1135 Excellent

About Conner McCloud

  • Rank
    Legend
  1. Conner McCloud

    C# was designed to not implement the SGL

    Quote:Original post by snk_kid I haven't been following your progress however I think it's not worth trying to emulate std C++ containers & algorithms model, this model isn't entirely correct (theoretically-wise) besides. Yeah I largely agree that emulating them in C# is a bad idea. My primary purpose has always been just to prove it was possible given the limitations placed on generics. I think I succeeded in this goal, so I'm not entirely dissatisfied with where it ended up. It just would have been nice if I could have taken it to completion, even though I never had any intention of actually using the library. I have more than enough half finished projects sitting around on my harddrive. And besides, the last few weeks I've been itching to make something real, possibly with XNA, and this gives me just the excuse I need to switch gears. Quote:Original post by snk_kid Anyways I think you could use a more suitable & more theoretically correct model for C# (especially with C# 3.0 and above) however there is just on major problem, you cannot constrain type variables (generic type parameters) for all types t such that t supports a particular or a set of operator overloads. You would have to use named methods which kinda of sucks. One of the MS blogs has suggested this is on the drawing board for things to fix in the future. Naming them intuitively might be clumsy, but after that they're just function calls like anything else. The fact that they're static may make things a bit more tricky, but it generated the proper code for me, so that must not be an impossible barrier. CM
  2. That's the conclusion I've reached. After jumping through dozens of hoops to finally get everything to a point where it pretty much works as you'd expect, they light one of the hoops on fire and put it over a shark tank. And the sharks have fricken lazers on their fricken heads. Basically, as my recent post in the .Net forum has mentioned, I've discovered you can't apply operator++ to unnamed temporaries. I don't know if that's the proper word for it or not, but essentially it means ++container.Begin() is invalid. This mostly makes sense, but it doesn't seem like something that would be impossible to overcome. Just create an unnamed temporary that is immediately discarded, no big deal. That wouldn't be quite so big a deal except for this: the C# compiler isn't smart enough to know that its against the rules. Instead, it spits out invalid code that, at run time, throws a nasty exception. The situation is slightly better if Begin is a read-only property instead of a function --- then, the compiler crashes when it sees the code. So at least you know in advance something is wrong, rather than silently emitting invalid code, even if a compiler error would be more appropriate. So what, exactly, does this mean for the SGL? Well, it could well mean that I'm done. Things like for(Iterator i = ++vec.Begin(); i != vec.End(); ++i) are not exactly uncommon in my code. I certainly can't imagine a more straight forward way of skipping the first element in a container. Technically, I could use vec.Begin().Advance(1), but that's ugly. Seriously, I can't stand it; my test code is littered with it, and every time I write a loop I die a little inside. And more importantly, the compiler won't stop me, or anybody else, from using the first form. It'll just happily spit out invalid code that'll cause the program to choke when executed. That's unacceptable. Perhaps I should buckle down and learn C++/CLI. Perhaps, since that is perfectly valid C++ code, it will also be perfectly valid C++/CLI code. And the whole project will be saved. I doubt it, though. I'm a little too tired to be optimistic at this stage. CM
  3. Conceptually, what is the difference between a Sequence and a Sorted Container? Answer: The fact that you specify the order of a sequence, while the sorted container decides on its own where to put things. Its a bit of a simplification, but overall I think that really sums it up. With that in mind, having Insert(i, x) being defined for a Sorted Container simply doesn't make sense. More importantly, expecting a function that relies on Insert(i, x) to work properly on both Sequences and Sorted Containers doesn't make sense. So, this is the final breakdown I have decided on: Container -> ErasableContainer -> ISequence -> IRandomAccessSequence -> IBackSequence -> IFrontSequence -> ISortedContainer Unfortunately, this makes for a system that is rather verbose. List, for instance, is an ErasableContainer, an ISequence, an IBackSequence, and an IFrontSequence all at once. Vector is slightly better, being an ErasableContainer, an IRandomAccessSequence, and an IBackSequence. Stack would simply be a Container, but it would accept an IBackSequence as a parameter. This is what I meant when I said a couple posts back that the hardest part of working with Containers is figuring out what to inherit from. At some point, I would like to sit down and see if I can't come up with something a little less clunky by dropping the STL association. But I doubt I will. Once I have actually finished making these changes [they aren't substantially different from what I had originally], I think I'll go ahead and publish a version 0.1. I'll be leaving out all the SortedContainers, because they're in a rather broken state, but I do have both List and Vector, as well as quite a few standard algorithms. So there's still plenty to laugh at. Until next time. CM
  4. Conner McCloud

    On containers

    When designing my iterators, I pretty much just went to the C++ standard [chapter 24, Section 1: Iterator Requirements], and copied it. That worked great, the end results were quite pleasing. Initially, I did the same thing with the containers. In fact, I did the same thing with containers before I even did it with iterators. However, I now realize this was rather silly. There's a lot of duplication in the definitions of sequences and associative containers. For instance, the only Sequence methods that aren't also in Associative Containers are insert(p, n, t) and insert(p, i, j) [erase(q) and erase(q1, q2) are present in both, but have different return values ... I must admit I don't understand why]. Theoretically, this isn't that big a deal; you can just duplicate the definitions. This is exactly what I did initially. However, then generic algorithms can't work on both Sequences and Associative Containers except via iterators, even if they are restricted to the common interface. See, for instance, insert_iterator. In C++ that works with both types, but C# requires a common type that defines the insert(p, i) method. So should that common type exist? If so, should it do all of the common methods? What I'm leaning towards now is inserting a ModifiableContainer type between Container and Sequence/Associative Container. Right now, Container is pretty much read only, and I figure that's a pretty useful distinction to have. So adding a second layer with the inserts and erases makes sense to me. But then I wonder if the extra complexity is justified, and if I shouldn't just put it all into the base Container type. Or maybe even make it completely orthogonal...right now I have a BackInsertSequence type that defines PushBack, PopBack, and Back; why not have an InsertSequence type that defines Insert and Erase? Bah, explicit type constraints are making this a lot harder than it needs to be [sad] It was so much nicer when I could let the C++ standards committee do my design for me. Now I'm going to have to sit down and draw out exactly what I want everything to be capable of, and what I don't want them to be capable of, and go from there. Uber boring. CM
  5. Conner McCloud

    Generic cycling

    In the wake of the untimely demise of my car, I've lost my will to do much of anything other than play Baldur's Gate, a game I find entertaining but ultimately rather klunky and poorly executed. Being without a television, however, its all I've got to satisfy my need for some role playing action. I've been feeling the itch to code, however, and so I'm going to give a quick update on the SGL in the hopes that it'll push me over the edge and I'll start back up again. It turns out that learning generics at the same time as designing a complicated series of generic containers wasn't that good an idea. The whole equality issue I encountered is a prime example of why: I did it right initially, but a misunderstanding in how generics work resulted in my replacing my correct Equals functions with incorrect versions that still did the job. I later corrected my misunderstanding, but the incorrect Equals remained. Later attempts to perform trickery then failed, because Equals wasn't implemented right, and I concluded that such trickery was impossible when in reality it was quite trivial to fix. All told, creating those writeups last week were a god send. They made me realize how shoddy my initial design was, and I have since cleaned it up quite a bit. The actual use of the iterators remains largely unchanged, but now you can do this: void function(RandomAccessIterator begin, RandomAccessIterator end) where Itr : RandomAccessIterator { //Some algorithm specialized for RandomAccessIterators } void function(BidirectionalIterator begin, RandomAccessIterator end) where Itr : BidirectionalIterator { //Same algorithm, but using the more generic BidirectionalIterator } By doing this, I have also made it possible for the compiler to infer all the type arguments on its own. To see why, consider what the functions would have been before: void function(Itr begin, Itr end) where Itr : BidirectionalIterator { //All I can do is use the Bidirectional version, no random access optimizations :( } In both versions, the function has two generic parameters, T and Itr. When infering those parameters, the compiler only looks at the actual function arguments...it doesn't consider the where clauses. In the second version, T is only present in the where clause, so the compiler has to be told explicitly what type to subsitute in. Now that I can specify the specific type of iterator directly [rather than delegating it to the where clause], that is no longer an issue. The other change this has made possible is the addition of iterator adaptors. For instance, I now have a BackInsertIterator that mimics the behavior of std::back_insert_iterator. How it works will have to wait for another day, though, since I haven't gotten to the chapter on generic containers. Why not? Because when I went to write up that section, I realized my containers were a horrible mess. I wrote them first, so that I had something to iterate over, but the radical changes to my iterator design only propagated backwards so far as was neccessary to keep things compiling properly. The end result was a nightmare, with a horrible hodgepodge of classes and interfaces and general crap that needs to be gotten rid of. If this post does the trick, it should only take a day or so to get right. Then we can continue our discussion of the SGL. In the mean time, I need to figure out some way to get snazzy screenshots out of this project so that my journal is a little less boring. CM *edit: PS, I've implemented a bunch of the generic algorithms required by the C++ standard. I'm rather proud of the fact that I can actually copy the code from an actual implementation, fix the stylistic differences [* -> Value, in particular], and end up with a fully working function. I don't, of course, because that would be stealing, and most of them are near impossible to read anyhow. But the fact that I could makes me feel all proud of my little generic iterators.
  6. First of all, if you at all enjoyed Super Troopers, go see Beerfest right now. I have never in my life heard an entire theater laughing uncontrollably at the same time until this movie. I have heard individual people laugh, and had the occasional "ha ha ha ha done" laugh pass through the croud, but this was full force "I can't breath" laughter. This movie is awesome enough that it could share some of the funny with Club Dread and end up with two good movies afterwards. So, when last we met, I had given you an overview of some of the high level decisions I had made. In the interim, snk_kid was kind enough to point out just how pointless this project really is: Dinkumware is working on STL/CLR, and it will be included with VS at some point in the near future. But that's OK. I'll bet I'll have SGL finished first, so when I'm done I'll forward them a copy for inspiration [wink] Today, I would like to get down into the nitty gritty details of how everything actually works up to this point. We will begin with iterators, because they were the hard part. The C++ iterator library defines five classes of iterators: RandomAccess -> Bidirectional -> Forward -> Input -> Output All iterators support forward movement via operator++, so you can consider enumerations to be a form of InputIterator1. I have chosen to follow suit, and make use of the same heirarchy. I somewhat regret not making OutputIterator independant of the basic chain, but fixing that now would be non-trivial. In C++, this heirarchy is largely imagined...you specify what type of iterator you are using the iterator_category tag, but otherwise a BidirectionalIterator is simply one that defines operator-- in addition to operator++. In C#, such behavior would not work...generic types and methods depend on inheritance to see what behavior is and is not allowed. For this reason, I define six types: interface InputIterator -- specifies a read-only property Value interface OutputIterator -- specifies a write-only property Value abstract class Iterator -- Implements operator++, Advance, and Distance in terms of the abstract method Increment(), and implements operator== in terms of Equals. abstract class ForwardIterator -- Derives from Iterator, InputIterator, and OutputIterator. Provides no additional functionality, and leaves the Value property abstract. abstract class BidirectionalIterator -- Adds an abstract method Decrement, and implements operator-- in terms of it. Also adds support for using operator- in place of Distance2, and overrides the default Advance to allow for negative values of n. abstract class RandomAccessIterator -- Adds an abstract indexer (essentially operator[]) that allows constant time movement of the iterator. Implements operator+ and operator- (when used with integral operands) and overrides the default Advance to make use of the indexer. Now, lets say you are implementing an iterator for an iterator for a List class [err..LinkedList class...damn .Net calling arrays lists [flaming]]. In this case, you would want to inherit from BidirectionalIterator. In order to get all the functionality of a BidirectionalIterator you only have to provide five functions: self equality via Equals, the Value property, Increment, and Decrement. That's it...everything else you need is provided transparently by the underlying classes. If you were implementing Vector.Iterator, you would inherit from RandomAccessIterator, and provide the same functions, plus the indexer. So what about using them? That's just as straight forward. Just decide what type of iterator you are working with, and go from there. As an example, here's a hypothetical implementation of for_each: delegate void Function(T v); void for_each(Itr begin, Itr end, Function f) where Itr : Iterator, InputIterator { for(; begin != end; ++begin) f(begin.Value); } //Later, within some function: List l; for_each(l.Begin(), l.End(), delegate(int v) {System.Console.Write("{0} ", v);}); If you implementing Sort, which requires RandomAccessIterators, then you would simply replace the where line above with where Itr : RandomAccessIterator, and you'd be free to use begin[5] or end[-10]. Now, as I mentioned, there is a rather significant problem with the overview I just gave. Namely, overloaded operators don't work that way. In .Net, overloaded operators are static functions...this means you can't inherit them, and the return value is permanantly stuck with one type. You would not think this would be a problem, because even if dealing with a base class the virtual functions would still call the proper methods. And indeed, I am now entirely unable to recreate the bugs I encountered at the time.3 However, the combination of inheritance, generics, and overloaded operators resulted in some subtle bugs, especially with regards to equality. Sometimes, they could be gotten around with simple casts to the proper type, but not always. The solution I found was the Curiously Recurring Generic. Observe: class Iterator { public static Iterator operator++() {/*implement me*/} } class VectorIterator : Iterator {} Becomes class Iterator where Itr : Iterator { public static Itr operator++() {/*implement me*/} } class VectorIterator : Iterator {} Now, all the operators operate on the derived class instead of on the base class, even though the operator itself is defined in the base class. It is really quite slick, I think. This recurence goes all the way up, with ForwardIterator, BidirectionalIterator and RandomAccessIterator all accepting the same second parameter. Only InputIterator and OutputIterator, which only define a property that doesn't care about the actual iterator in any way, are exempt. This is not without its pitfalls, of course. In addition to making the definition of iterators slightly more complex, it seriously hinders the ability of the compiler to infer types automatically. You have to parameterize based on iterator, because you can not have a BidirectionIterator, for example, on its own. If it isn't obvious why, try it out: void SomeFunc(BidirectionalIterator public class RangeEnumerator : IEnumerator, IEnumerable where U : Iterator, InputIterator { public RangeEnumerator(U begin_, U end_) { begin = begin_; end = end_; } IEnumerator IEnumerable.GetEnumerator() { return this; } public System.Collections.IEnumerator GetEnumerator() { return this; } public bool MoveNext() { //Enumerations start one before the begining of the range, so itr // itr being null signifies that position. if(itr == null) { itr = (U)begin.Clone(); return itr != end; } itr++; return itr != end; } public T Current { get { if(itr == null || itr == end) throw new InvalidOperationException(); return itr.Value; } } Object System.Collections.IEnumerator.Current { get { //Is it possible to somehow use the other form of Current for // this? I should probably yank both bodies into a separate // function. if(itr == null || itr == end) throw new InvalidOperationException(); return (Object)itr.Value; } } public void Reset() { itr = null; } // I don't know what this function is for, but I get yelled at if it // isn't here. public void Dispose() { } private U itr; private U begin; private U end; } //This is a helper class that allows the compiler to infer the two type // parameters. It occurs to me now that I could possibly move // RangeEnumerator into this class and simplify things a bit. public static class Range { public static RangeEnumerator Create(U begin, U end) where U : Iterator, InputIterator { return new RangeEnumerator(begin, end); } } This is how you actually use them: class Program { public static void Main(string[] args) { //I know we haven't covered vectors yet, but I'm use you can figure out what this does :) Vector v = new Vector(); for(int i = 0; i < 10; i++) v.PushBack(i); foreach(int i in new RangeEnumerator(v.Begin(), v.End())) System.Console.WriteLine(i); //Or, more simply: foreach(int i in Range.Create(v.Begin(), v.End())) System.Console.WriteLine(i); //Although it is not the point of the code, you can also do this: foreach(int i in v) System.Console.WriteLine(i); } } As an aside, when I finished typing out that demo program I pressed B. And with that, I bid you fare well. CM 1This is a bit of a simplification...you are only allowed to read from an input iterator once [this restriction is lifted by ForwardIterator], but you can reset an enumerator and pass through it multiple times if you like. 2I have to look through my notes to see if there's a reason why this functionality is provided by BidirectionalIterator and not by Iterator itself...the actual implementation is simply rhs.Distance(lhs), so there's no reason why it wouldn't work with ForwardIterators as well. 3The fact that I can't recreate the bugs, despite them being quite obvious at the time, bothers me. This calls into question all the decisions I have made since. I think over the next couple days I will try and work backwards and recreate the problems I had before. Hopefully, it wasn't just a misunderstanding that I have since rectified. I fully understood the source of the problems at the time, but I was learning a lot at the time so you never know. 4There is a third option I just now tried out: void SomeFunc(BidirectionalIterator iterator) where Itr : BidirectionalIterator {} In that case, it infers both parameters just fine. However, equality doesn't work right. As I mentioned before, equality is the area that was most severely affected by the issues that lead to implementing the recusion in the first place. I know exactly how to fix it, and I'm willing to bet that doing so will lead me to remember what caused the bugs in the first place. So that is where I shall begin my search.
  7. Conner McCloud

    Happy Panda

    Quote:Original post by snk_kid Okay so the name has changed to STL/CLR and it's been put on hold until the next version of VS, read more here, oh well.... STL/CLR...that's a way better name than I came up with [sad] I think I'll officially adopt SGL. It seems a bit presumptuous to call it the Standard Generics Library, so lets call the S Supreme. Nothing presumptuous there at all. I heard they were working on that, but was under the impression they were basically just giving vector an IList interface. From the looks of things, they're going considerably farther. That's pretty cool...I'll be able to compare my work against theirs, and then file it away and use STL/CLR afterwards [grin] I've been using Dinkumware's code for algorithm inspirations anyhow, so that'll be appropriate. CM
  8. Conner McCloud

    Happy Panda

    Yeah, I kind of figured I could simplify things somewhat by dabbling in other languages. But that would have defeated the purpose...the faster I get it done, the sooner I have to come up with something new to do [grin] That said, do you have any specific examples you could point me to of such features being exposed to C#? CM
  9. Conner McCloud

    Happy Panda

    Windows is back. Hooray! It was even easier than I thought it would be...I envisioned losing all my data and having to reinstall all my software, but all it took was putting in the new harddrive, loading it with the ghost I made before things went up in smoke, and performing a repair installation of Windows. So now I'm back to work on Super Awesome Library 5000. Or possibly STL.Net. Or maybe SGL. I haven't decided yet. Its pretty close to being at a point where I would feel comfortable showing off actual code, but not quite. I think I'm going to finish up with the basic algorithms and maybe implement a deque, and then release a demo version before I do any hard work on the sorted containers [although I have a basic Set class put together, it is pretty ugly and doesn't quite work yet]. In the mean time, I'm going to give a basic overview of the project as a whole, and some of the design decisions I've made along the way. First of all, let me make it clear that this was a project born of boredom. Neccessity may be the mother of invention, but boredom is the drunk uncle that nobody really likes. It basically started because I was a little annoyed that enumerators in .Net don't allow you to modify the values you are enumerating. I missed my iterators, but realized that given the behavior of .Net generics iterators wouldn't work anyhow. But then one day I got bored [my internet broke down so I couldn't play WoW, and I have no television] and decided that I would go ahead and try to implement iterators anyhow. I knew I would fail, but I had nothing better to do with my time. As luck would have it, I succeeded. Or somewhat succeeded...it was rudimentary, but a good proof of concept. My internet was still down, so I figured what the hell...why not just go all the way and recreate the whole shebang? Now, its evolved into something I want to do just because I can. The reason I say all that is because I'm still convincing myself that its OK to reinvent the wheel in this case [embarrass] I'm fully expecting the end result to be slower than the standard counterparts, as well as harder to use in some regards, and probably less safe to boot. And I know it isn't portable as of yet, because mono is incapable of running it [although gmcs does compile it all properly]. But maybe I'll learn something useful1, or people will find it handy. At the very least, I'll have something to chug away at while I'm bored [grin] So, onto the actual design. To begin with, let me explain where it isn't going to be a straight port, and why. The biggest difference is that I've decided to omit custom allocators. From a design standpoint, they seem to go against the idea of running in a managed environment. From a practical standpoint, I wouldn't know how to program a custom allocator in .Net anyhow. Also, .Net doesn't allow you to specify default parameters, so you would have to explicitly state that you wanted the standard allocator every single time. This fact is compounded by MSs decision to not implement typedefs in C#, so even that shorthand isn't possible in the language I chose to implement this system in. All together, the decision to remove custom allocators was a pretty easy one to make. A similar process will probably result in sorted containers not being parameterized on comparators. As I mentioned, I haven't actually completed this portion of things yet, but I will almost certainly go the route of System.Collections.Generic.SortedDictionary, and accept a Comparer object. I haven't quite finished analyzing what effects this will have, however, so the final result remains to be seen. I am also discarding the SC++L style of using_lots_of_underscores in favor of a more .Net-friendly UsingLotsOfCapitalization. Iterators are an actual concrete type rather than the abstract pseudo-type they are in C++. In addition to allowing Iterators to work at all within the confines of .Net generics, they allowed me to make Distance and Advance member functions rather than global functions. This is quite important, as I'm pretty sure its the only way functions like std::lower_bound can perform better with vectors than they do with lists. It is impossible to specialize generics, so there can only be one implementation of Advance(itr, n), which would have to be to the effect of while(n--) itr++;. But, itr.Advance(n) can be inherited by a RandomAccessIterator to be just itr += n;2 For obvious reasons, iterators aren't dereferenced via *itr. Instead, a property Value is exposed. There are no iterator tags, or type-traits, or anything like that, because generics would be completely unable to utilize them. Instead, such information is carried in the inheritance. If you are implementing a BidirectionalIterator, then you inherit from BidirectionalIterator. I suppose that isn't entirely accurate...you can use inheritance in C++ to achieve similar effects, but you don't *have* to. distance_type is always int, and you just have to keep track of value_type on your own3. Although they do not now, ultimately the containers will all expose .Net-style interfaces. Vector will inherit from IList, and so forth. The primary reason they don't already is because I'm lazy. I'm also torn on whether or not to rename List so it is a little less confusing when mingled in with the standard containers. I suppose naming it LinkedList wouldn't be *that* bad, but that can wait. They do currently inherit from IEnumerable [and its generic cousin] so they can be used with foreach. I think that pretty much covers the big changes that I'm aware of. Although I used a lot of words to describe them, they're largely superficial and don't really effect how you actually use everything. Implementations are a little odd, but that's something I have to worry about, not you [grin] The weirdness, however, all comes from inheriting from the proper objects. Once you've done that, all the grunt work is done, and you just have to provide three or four implementations so that all the real hard stuff [like overloaded operators, which were a bitch and a half to get working right] is done for you automagically. I want to move onto implementation details, but I have to work in two hours and honestly, I'm not sure I have the time. Its pretty complex, and I'd rather not just throw the stuff out there without explaining why I did what I had to do. So that can wait for another day. CM 1 Speaking of learning something, did you know that _ is a valid name in C#? I assume it works in other languages as well, I've just never bothered to try it and see. I'm tempted to write a class named _ and fill it with functions named __ and ___ and ____ just for the hell of it. Unfortunately, this knowledge was learned independant of STL.Net, so I haven't quite justified my time yet. 2 In my last journal entry, I stated the following: Quote: The biggest hurdle I think I have left to overcome is how to specialize algorithms for specific types of iterators. Haven't had any inspirations on that front in a while, so I fear it might not happen. This was only partially true, and was mostly my memory playing tricks on me. I haven't had any inspirations as to how to specialize algorithms...instead, I think making Distance and Advance member functions has allowed me to work around the problem to a certain extent. I forgot about that. It isn't a perfect solution, but its good enough for government work. 3 The lack of a way to specify value_type is, to date, the biggest annoyance I have with this whole system. I say that only half-heartedly, however, as I'm not positive a simple typedef would actually solve any of the problems I associate with the lack of said typedef. It is a conventient scapegoat, though.
  10. Conner McCloud

    Sad Panda

    A while back I got bored and decided to develop a CLR compliant version of the C++ standard containers library in C#. In particular, I wanted to see if I could get iterators working. So far, my success has been rather promising. I've got Vector and List working, with a rudimentary Set implementation, and my experiments with generic algorithms have proven mostly successful. The biggest hurdle I think I have left to overcome is how to specialize algorithms for specific types of iterators. Haven't had any inspirations on that front in a while, so I fear it might not happen. However, then my harddrive died, and I got pushed onto my secondary harddrive, which is occupied by Linux. This itself doesn't really bother me. I dislike working on the Dark Side, but Ubuntu is relatively painless, so I just run with it. Eventually, I'll find my missing XP CD and everythign will start coming up Milhouse again. In the meantime, I caught mono, and spent a fair amount of time figuring out how to work with the 2.0 compiler. Unfortunately, Monodevelop does not support compiling with gmcs, so I'm stuck with make. Again, not that big a deal. I figured it all out, set my project directories up in a nice tidy way, and compiled my test program. It compiled flawlessly, without so much as a warning. I then run the resulting executable. First, I get a warning: "Implement me." Then, I get an error: "Code should not be reached. Aborting...aborted." Superb. Apparently, mono's generic support is not quite at 100%, and my [admitedly complicated] use of them falls in the part still left to be finished. Which means I can develop my code, but not test it. /sigh CM
  11. Conner McCloud

    The death and return of Conner McCloud

    Quote:Original post by ApochPiQ Yeah, "them as can't do," and all that - but secretly, we all have at least one teacher who really could, and I suspect that we all owe some part of our success in life to those people. I must admit, that the professors I had that were truly gifted have been a great inspiration to me. Taking their courses changed me, both as a student and as a person. If I can capture a fraction of that for my own students, then I'll consider myself a lucky man.
  12. Conner McCloud

    The death and return of Conner McCloud

    Quote:Original post by Frequency As a junior in high school looking to go to a nice college for a major in Computer Science, this worries me and makes me think maybe I should analyze my choice more closely! But it looks like you have a happy ending and I'm glad to hear it. Keep us all informed on your projects and your teaching. [smile] Don't let my story get in the way of your own dreams. Computer Science, as a career, wasn't for me. But look at the countless people on this very site who found Computer Science was for them. If right now, you feel that that is the direction your life is taking you, by all means follow through. Even if, heaven forbid, you end up in a situation similar to my own in five or six years, you'll still have gotten a lot out of the college experience. I certainly don't regret the five years I spent at the School of Mines, even if I'll only make use of a fraction of what I studied. As a quick aside [and I hope to have more information once my computer works again], my current project is a port of the STL to .NET. Its been immensely fun, trying to coax generics into pretending they're templates. It is, of course, a complete waste of time. Some primitive benchmarks suggest Vector.Net is considerably slower than Collections.Generic.List, for instance, and Map.Net's implementation would likely make somebody more experienced in writing containers cry. But as a proof of concept, it has been rather entertaining. And honestly, I'm rather proud of my iterator setup. Iterators are what inspired the project initially, so the fact that I have them working is rather pleasing.
  13. Life is certainly strange. Some fourteen months ago, I was graduating from a respected university with two sought after degrees. Today, I am posting from a library [twelve minutes left on this session, I had better hurry], preparing for my next shift at a local Dairy Queen. And I honestly do not remember when I was last happier. It isn't so much that I enjoy being an assistant manager at Dairy Queen. Although, I do. Very much so. It is more that I have a vision again. A new goal for my life after the last faded years ago to the turmoil of earning those two degrees. But I am getting ahead of myself, and I am down to eight and a half minutes. No time for dillies, and less time for dallies. As I said, some fourteen months ago I graduated. I immediately began searching for employment, both with software firms [my first degree being in Computer Science], and with engineering firms [my second degree being in Electical Engineering]. I found employment difficult to find. As the months wore on, I realized the reason employers did not find me attractive was that I myself had little to no desire to work in either field. I love developing software, and I love developing hardware, and I especially love the magical realm where the two disciplines meet. But I simply could not envision myself doing it for a living. They are a hobby to me. A hobby I love, and will always love, but a hobby none the less. Not a career. But what else could I do? I have, after all, nearly killed myself getting through college, not to mention putting myself rather seriously in debt, all to enter the very fields I was not discarding. I was distraught. It was not a happy couple of weeks as I searched my soul for some sign of what I should do. Fortune and I have a very healthy relationship, and in this case it did not let me down. At the very same time I was was going through this dilemma, my brother was having trouble with Algebra, and attending study sessions with his teacher and a bunch of other students over the summer. As I was currently unemployed, I volunteered to help out in my spare time. The experience was, for lack of a better word, breathtaking. I felt satisfaction watching those kids grasp math that I never felt writing programs or building robots. It was a satisfaction that I have only felt in passing here on Gamedev, pretending I know enough about programming to help others in the General Programming forum. It became immediately clear what I am meant to do. Teach. I've heard the jokes from my peers; those who can't, and so forth. I don't really care. This is what will make me happy, I am more certain of that then anything I've ever felt before. And so I am returning to school. Apparently, to teach high school math requires actual coursework. Not much, of course, since I already have a degree in math. Just a handful of courses in education. And some reading and writing bull shit that the School of Mines never saw fit to officially teach me. I figure I could knock it out in a year, maybe three semesters, if I felt so inclined. But I've done the full time student thing, and I think I want to take a break from it. What's the hurry? I have my entire life ahead of me, and now that I have a goal I can enjoy myself for a while. And yes, make ice cream for ten dollars an hour. Sure, its not the $40k a year job I was told to look forward to when I left highschool, but it is honest work, I get experience with Teenagers, and five nights a week I get a free meal. Really, what more could a guy want? And so there you all have it. The quick and dirty summary of where Conner McCloud is. I am sure you have all been dying for an update on my amazing life, and I am more than happy to oblige. And for those of you living in the greater Denver area, feel free to swing by my store. I promise I'll give the brasier guy a stern talking to should he spit in your meal.
  14. Conner McCloud

    I need your opinion about them dancin' dozers!

    I like the border-free version. Can't really comment on the design itself.
  15. Before I begin, I want to make something clear. In my entire life, only two books have produced actual physical responses in me. One, The Shining terrified me, and resulted in nightmares for several days. The other, The Hitchhiker's Guide To the Galaxy, caused me to literally drop the book because I was laughing so hard. I don't laugh out loud at comedians, and that's their only reason for being. So to me, that says a lot about the quality of the book. That was all my freshman year in college. So about five years ago. I didn't have ready access to the other four books, so I never bothered to read them. In the back of my mind, I always planned to. Enter the movie. I enjoyed the movie. Yes, they cut out a bunch of my favorite lines, but oh well. All in all, I found it thoroughly enjoyable. Shortly thereafter, I went to buy The Bourne Ultimatum at Barnes and Noble, and happened to pass by The Ultimate Hitchhiker's Guide To The Galaxy. "At last in paperback in one complete volume, here are the five classic novels from Douglas Adams's beloved Hitchhiker series." "Includes the bonus story: 'Young Zaphod Plays It Safe.'" Needless to say, I'm still only two books into the life of David Webb, but a full five into the life of Aurthur Dent. Naturally, the best place to start a story is at the begining. So I reread "Hitchhiker's", just to make sure i haven't forgotten anything. I didn't laugh out loud this time, but that wasn't the fault of the book. Quite the contrary, it was every bit as entertaining as the first time. I just knew what was coming, and had time to prepare for the particularly funny bits. So I keep going, plowing headfirst into The Restaurant at the End of the Universe. I didn't laugh out loud this time, either. But I did chuckle a few times. All in all, it was good. Sadly, that's the last time I can say that about the series. I'm trying really really hard to come up with redeaming qualities of the last three books. I am, honest Injin. But it just isn't possible. I mean, sure, there were some high points. The story of the fellows from Krikkit first going into space was quite good. The initial explanation about how one learns to fly will likely find its way into my general vocabulary. But halfway through So Long, and Thanks for All the Fish even that has lost its charm. Before I go on, let me make something perfectly clear. First, yes, I'm an American, raised on American humour. Although, apparently I occasionally spell it "humour." I am completely willing to admit that chances are, I just missed the point. Somewhere, something in my upbringing just made me incompatable with these books. Apparently, there are a lot of people who feel these books are the second coming of Christ. That's cool, I respect that. I'm still going to proceed to explain, in detail, exactly why they're all wrong. I am going to do this knowing full well that it is entirely possible that the fault lies with me. Now, where was I? Oh, right. Part of the problem, I think, is one of plot. In various reviews of the theatrical adaptation, many people complained that the plot was overdeveloped. Adams' work isn't about plot, its about characters and comedy and blah blah blah. I believe Superpig refered to the books as the opposite of sitcoms. At the time, thinking about "Hitchhiker's", this made sense. It was just a bunch of random shit happening for no apparent reason. Then the book ended, and you still didn't have an apparent reason. And that was OK, it worked. Oh, except for all the "Why did I become president and zap my brain and steal this ship" bitching from Zaphod that formed the basis for most of The Restaurant at the End of the Universe. For an author that doesn't care about plot, he sure went out of his way to set himself up for a sequal. But that was still cool. At first. Then, the "lets just do random shit" kicked back in, and we find ourselves with Ford and Arthur stuck on a prehistoric Earth. But that's not the problem. The problem is that there are also a bunch of telephone cleaners and hair dressers stuck on a prehistoric Earth. Apparently, the very presence of these telephone cleaners and hair dressers is sufficient to cause the native inhabitants to die out. This leads to the natural conclusion that Arthur is decendent from these telephone cleaners and hair dressers. All without the knowledge of the pan-dimensional super intelligent mice that were, supposedly, responsible for the development of Earth. At this point, Adams officially crossed the line between "random shit" and "completely retarded." Sure, it was a little funny when you find out that the ultimate question is "What is the product of six and nine" [heh heh, six times nine doesn't equal fourty-two, narf], but the underlying stupidity of the whole thing is just too much. Was it really necessary? Did it serve anything to have this revelation? To a certain extent, yes, it did. It introduced a certain symmetry to the book. You see, for me, the two best parts of The Hitchhiker's Guide to the Galaxy are as follows: (1) The parallels between Arthur's home being demolished and Arthur's planet being demolished. (2) The pan-dimensional super intelligent mice that have been controlling our destines for all of history. So what is the first thing Adams does in The Restaurant at the End of the Universe? Reveal that there are no parallels between Arthur's home being demolished and Arthur's planet being demolished. His home was demolished to make way for a bypass, and his planet was demolished to keep the pan-dimensional super intelligent mice from learning the Ultimate Question to Life, the Universe, and Everything. What is the last thing Adams does in The Restaurant at the End of the Universe? Reveal that there are no pan-dimensional super intelligent mice. Instead, there's just pan-dimensional idiot mice who apparently don't question the overnight evolution of their experiment from primitive "cave men" to telephone cleaners and hair dressers. This brings us to Life, the Universe, and Everything. At this point, any claim that Adams doesn't write plot-driven stories goes right out the window. Even seemingly pointless happenings are immediately put to good use in furthering the story. The single biggest example of this is obviously the story about the computer that wants to blow up the Universe. It starts off as an interesting little aside about how you should never try to solve anything with potatoes, and ends up being the crucial bit of information necessary to save the day. Why did Trillian even watch that segment of history? It has nothing to do with anything. Then there's the fact that Adams is apparently on a crusade to destroy everything that made "Hitchhiker's" entertaining. Many people have speculated that if we knew exactly why the bowl of petunias had thought ["Oh no, not again"] we would know a lot more about the nature of the Universe than we do now. Apparently, many people are wrong. Did the knowledge make that section more entertaining than it already was? No, not at all. Frankly, that was the best section in the book. Did it render one of the more interesting parts of the initial book completely pointless? Yes, absolutely so. That scene also set up the basis for later plot devices, but we're still a solid book away from that. The final gripe with Life, the Universe and Everything is the ending. First, there's the nice climactic "oh shit I'm about to detonate the bomb and destroy the Universe, why oh why did I want to play cricket?" moment. This pissed me off to no end. Completely unneccessary. Then, they throw in this Prak fellow who explains that you can't know both the ultimate question and the ultimate answer at the same time. This didn't bother me all, actually, but I feel like pointing out the implied "Fuck off and stop asking me what the god damn question is" that Adams was classy enough not to actually say. Personally, I felt the revelation in The Restaurant at the End of the Universe about Arthur not knowing the question was sufficient to settle that once and for all, but apparently not. Enter So Long, and Thanks for All the Fish. Now, right off the bat, I recall several people complaining about how Earth was brought back at the end of the movie. If any of these people have read So Long, and Thanks for All the Fish, they are too stupid to breed. Other than that, I don't have much to say about this one. I wasn't impressed, but nothing particularly horrible happens. At least, not at first. You're well into Mostly Harmless before you realize how crappy So Long, and Thanks for All the Fish is. I hope, after all I've written thus far, my reasons for disliking Mostly Harmless should be pretty clear. Think about it for a moment while I check out the Lounge really quick. I've been working on this for three hours now, so I need a break. OK, I'm back. Before I get into Mostly Harmless I should probably mention the bonus story "Young Zaphod Plays It Safe." I don't understand it at all. I suspect something happened, but I don't know what. There were a few cleverly written bits, so I give it high marks. Moving on, the single worst thing about Mostly Harmless is the treatment of the Vogons. Up to this point, you have been treated to Vogons that are completely devoid of imagination and generally incapable of any real thought. Hence their knack for beurocracy. And yet in Mostly Harmless you discover that Captain Prostetnic Vogon Jeltz of the Galactic Hypersace Planning Council manages to mastermind a hostile takeover of the Guide publishers and creates some fancy new Guide that can manipulate space and time. How does that not require imagination and thought? It just doesn't make sense. Sure, there's a certain logic behind Mr Jeltz being obsessed with destroying Earth. But why the overly complex plot? Fortunately, up to that point the book was actually quite superb. You encounter such wonderful scenes as the heartfelt and emotional goodbye when Fenchurch dies. And the shock and awe that Arthur experiences when he discovers that more than just the one copy of Earth survived. Or the way Adams doesn't even remotely forshadow Arthur's death fifty times throughout the book. Personally, I wouldn't have been able to help but remind the reader every couple pages that Arthur can't die until he gets to Stavro Mueller Beta. After all, this fact wasn't menioned at all in So Long, and Thanks for All the Fish, so its possible they might have forgotten. But then, that's why I'm not a world class author. No, I didn't like the fact that Adams felt compelled to not only kill off three of the original characters, but to introduce two more just to kill them off as well. But that's OK, I don't have to like every decision an author makes. However, I do expect a certain quality of execution that I don't feel was present in Mostly Harmless. So there you have it. Conner McCloud's in depth analysis of the complete lunacy of a world in which The Hitchhiker's Guide to the Galaxy trilogy can be heralded as a beaken of literary perfection. Please, feel free to explain to me the various ways in which I missed some point. Perhaps once I'm aware of the point, I will be able to like the series. At the very least, I'd like to look back on the first book with untainted memories, even if I never like the rest. Actually, I'm not quite done. Being a developers journal I'm going to menion my programming goals. I'm currently working on a card game. In my head, its a fairly generic framework that will simplify the creation of multi-player games. More importantly, it will simplify the creation of AI bots to play these multi-player games. I'm learning wxWindows to do the windowing stuff, but there is also going to be the ability to do it all at the command line. Less overhead for training bots, but with the ability to have a handy interface for humans playing the game as well. Hopefully, I'll have an initial release of a Cribbage game by the end of Summer. Plus maybe Blackjack and Texas Hold'em. Once the graphics stuff is done, creating new games should be a breeze. CM
  • Advertisement
×

Important Information

By using GameDev.net, you agree to our community Guidelines, Terms of Use, and Privacy Policy.

Participate in the game development conversation and more when you create an account on GameDev.net!

Sign me up!