Friday, September 14, 2007

The Five Ages of a TSM Office Manager

I wrote this last year about TSM. It became relevant again as I heard through the TSM grapevine that a new office manager was once again doing something about that fridge situation

1. The first age dawns on day one of TSM employment. Fresh faced, filled with enthusiasm and vigor, the new office manager arrives at Black Falcon Ave. in clean and pressed professional attire and is carted around to meet all of the employees. Names are forgotten and awkward pleasantries exchanged—at least that’s how it works in the development office. Still, optimism rules the hour and coffee is promptly brewed and setup with pleasant presentation in the kitchen.

2. The second age comes after the office manager has been on the job for a few weeks. Flushed with the success of the coffee and getting to know co-workers, the office manager sets about to make a difference. Accordingly, the refrigerator is soon cleaned.

3. In the third age, the small details that delighted colleagues have become drab. Routine is the by-word of this age and frankly the routine is getting old. The pleasantly presented coffee pots of earlier ages are forgotten, and the brewing of coffee mostly ceases. Coffee drinkers are left to fend for themselves. They do. No one expects coffee to be made for them. By this point, the office manager is starting to realize his or her true purpose: executive assistant.

4. In the fourth age of the office manager, the last age’s foreshadowings have come to rule the day. Coffee and refrigerator be damned, the office manager is solely responsible to the C and Vs. Cream shortages are rampant with the occasional water shortage. Murmurs echo in the hallway as co-workers speculate about the difficulty of keeping the coffee system running smoothly and noticing that all of the water jugs are empty.

5. The fifth and final age begins as the office manager realizes that in spite his or her aptitude for assisting executives, lowly co-workers circumvent his attempts to be a buffer by speaking directly to the executives. They don’t seem to understand the value of a middleman. They also still seem miffed by the lack of cream. Appearance starts to suffer and cigarette intake increases. The age ends with a mishandled airline ticket, one too many over-important interruptions of the CEO, or a simple disinterested resignation leading to early termination. Life remains largely the same and former co-workers of the once-upon-a-time Office Manager look forward to the cycle renewing itself again. The fridge is starting to get a little grimy, after all.

Tuesday, September 11, 2007

Record macro and Repeat

I've been slowly updating my IntelliJ keybindings so that unbound keys are bound in ways that my hands expect from using emacs. It's been giving me a lot of "wow that makes me happy" moments from doing stupid things like splitting windows or closing the current editor without leaving the keyboard. But what I really need now is a command that will let me repeat the next command n times as in emacs C-x [n] [command] syntax. I'm probably missing something and am too lazy to research this just yet, but it seems likes it makes macros much less powerful if you can't record a macro that does something to one line and then have it executed an arbitrary number of times.

Wednesday, August 22, 2007

Zen, C++, IDE reloaded

So I think my initial thought when I started this blog was that within some months of getting started, emacs would have proved itself; I would have gathered helpful hints from around the internet, consolidated them, and turned my emacs work environment into a killer, "have fun developing" environment. Not only that but I would be able to sleep easy at night knowing that I had done it all in emacs. If I was more into being green and less into cars, it would be the same sort of moral superiority that some people derive from owning and operating hybrids... or not. I may just be making random stabs at the hybrid crowd for no good reason, vainly trying to mask my frustration with my own failure to achieve a sense of 'holier-than-thouness'.

Perfect... 1 paragraph and I've insulted a bunch of (oops almost used do-gooders there) people trying to make a difference and help the planet and invented a word... gratuitously.

So I think where I was going with was that there's a lot of functionality in emacs and a lot of people get a lot out of it. I was just hoping to provide a friendly place to pick up some tips on maximizing its use targeted to people with similar (IDE-driven) sensibilities to mine. But, it won't be that way. So, I'll just walk through where I've been from there to now.

First, emacs. Built it for Mac OS X. And started playing. For one thing, it totally kicks ass at one of my basic requirements for work which is to be able to seamlessly develop on a remote machine via ssh. The baseline version TRAMP that ships with emacs 22 allows you to easily load files from a remote machine via ssh just by opening a file with the following syntax: '/ssh:user@machine:/path/to/file.cpp'. What's really nice about TRAMP (given our shady connection to the data center) is that it saves the file in /tmp and all autosaves are performed locally. Only when you make emacs save does it actually go over the network. And if you crash, TRAMP provides a way to restore those backed up files. It also caches directory information enough to make tab complete pretty useable even over a slow connection. The packaged version of TRAMP's only weakness was in building, grepping, and debugging. To do these, you had to use remote versions of the commands that were a bit buggy. However, if you patch to the latest version of TRAMP, the standard commands will 'just work' via ssh. So that's that. TRAMP is totally impressive.

CVS integration is also very good in emacs. It's another feature that seems to just work and is generally pretty good. Diffs and history for files are fairly easy to get at via windowed menus and I don't feel the need to perform those things via the keyboard.

I was also happy with emacs' editing capabilities and general configurability. It's satisfying to envy a simple editing command available in a different editor and just implement it. That said, if the feature you happen to be drooling over is continuous syntax checking, function completion, or god forbid refactoring support, you're in for a ride.

The easiest thing to do for continuous syntax checking seems to be fly-make... a package that finds the Makefile for your current file and runs it with the target check-syntax. With gcc you can set this target up to be something along the lines of: g++ -fsyntax-only $(INCLUDES) $(CPP_FILES) and the module will run in the background highlighting errors. It's pretty useful and really easy to use, but it doesn't always work. Fairly often it would alert me that it was going to crap out... and then it would crap out. At some point, I even got it to take down all of emacs (which incidentally, it how I found out about TRAMP's awesome recovery features).

So that's not great, but then in terms of completion of functions, etc. There's just very little out there. The best thing is the probably CEDET which is collection of .el files to help make emacs into an IDE. Personally, I couldn't get it to even byte-compile the .el files on my mac and even running on the non-compiled version, it didn't do much for me. I've heard similar reviews from friends who have played with it. It may get there. It's an exciting idea. It's opensource, so if you're inclined you can work on it... but it's not there.

Crap... I just checked the length of this in preview and it's a monster. How do people write short blogs?

All of this led me to Eclipse C++. It, too, is opensource, so I figured I could work on problems that I found. And it already supports C++ and better yet I even found a remote plugin for it. But there were some problems. For one, yes it supports C++ but it seems to get tripped up on templates fairly easily and sometimes is just unhappy and won't offer completion hints. It quickly became my opinion that no hints were better than occasional hints. It's just too painful when it frequently doesn't work but you expect it to. I think that ends up being slower than planning to not get help from the IDE. Next, eclipse seems to have been designed with the keyboard forgotten. Everything is mouse centric from navigation to code inspection. Furthermore, I couldn't find any code generation features written (e.g., getters/setters or constructor). Next the code base is way too massive to think about doing anything quickly (as opposed to emacs). Finally, the remote stuff sucked after using TRAMP.

So what now? This is a two part answer, I guess. Answer 1: IntelliJ in textmode. And here's why. It has a lot of the great editing capabilities that I got with emacs including incremental search and macros (although the latter are not as convenient as they are in emacs). It has better file navigation -- I don't think I can live without the cmd-n interface... seriously. It doesn't have remote file editing/building (for C++ obviously) -- but I have handled both of those problems with shell scripts that make it all seamless. It has nicer CVS integration -- it's diffs are great. It has built in syntax highlighting for C++. And it's fast and looks nice.

Part 2 of the answer is that it also supports extension so I'm working on a C++ extension for it. This is a bit of large undertaking since it entails writing a full parser for C++, but at least I can do it in Java, in IntelliJ, be as ghetto about it as I want, and pretty much anything will be better than what I had before.

Thursday, July 26, 2007

Why Continuous Syntax Checking Matters

Today, I ran into a great example of why continuous syntax checking matters. Now, I understand that some of the slowness that I experienced in diagnosing this problem is because I'm still getting used to C++ and Boost and the resulting error messages. but still...

Here's the relevant chunk of code with the error in bold and line 69 pointed out for future reference.


--> Line 69

boost::program_options::options_description config( "Config File Options ");
config.add_options()
( "currentTime", boost::program_options::value()->default_value(defaultDateString), "YYYY-MM-DD HH:MM:SS" )
( "df", boost::program_options::value(), "description" )
( "debugDir", boost::program_options::value(), "location for log file" )
( "workingDir", boost::program_options::value(), "all intermediate files will go here" )
( "script", boost::program_options::value(), "script directory (location of func)" )
( "DBName", boost::program_options::value<std::string()>(), "name of database" )
( "DBUserName", boost::program_options::value< std::string >(), "username for database" )
( "DBPassword", boost::program_options::value< std::string >(), "password for database" )
( "DBSchema", boost::program_options::value(), "schema to be used for database" )
( "ASchema", boost::program_options::value(), "schema to access A tables" )
( "PSchema", boost::program_options::value(), "schema to access P tables" )
( "opsEmail", boost::program_options::value(), "operations email address" )
( "researchEmail", boost::program_options::value(), "research email address" );




And I'll just add the error message afterwards, for your enjoyment. I won't belabor the point by actually writing anything afterwards. I think it will be pretty clear. If I had had some kind of auto-syntax checking active, then this would have been a lot easier to find. Probably would have saved 5 minutes of staring at the code and 15 minutes of entering a blog to bitch about it. That's a 20 point swing!

/usr/include/boost/program_options/detail/value_semantic.hpp: In member function `void boost::program_options::typed_value::notify(const boost::any&) const [with T = std::string ()(), charT = char]':
private/SVCExecutable.cpp:69: instantiated from here
/usr/include/boost/program_options/detail/value_semantic.hpp:28: error: function return type cannot be function
/usr/include/boost/program_options/detail/value_semantic.hpp:28: error: function return type cannot be function
/usr/include/boost/any.hpp:172: error: function return type cannot be function
private/SVCExecutable.cpp:69: instantiated from here
/usr/include/boost/program_options/detail/value_semantic.hpp:30: error: assignment of read-only location
/usr/include/boost/program_options/detail/value_semantic.hpp:30: error: cannot convert `std::string ()()' to `std::string ()()' in assignment
/usr/include/boost/program_options/detail/value_semantic.hpp: In function `void boost::program_options::validate(boost::any&, const std::vector, std::allocator<_T2> >, std::allocator, std::allocator<_T2> > > >&, T*, long int) [with T = std::string ()(), charT = char]':
/usr/include/boost/program_options/detail/value_semantic.hpp:149: instantiated from `void boost::program_options::typed_value::xparse(boost::any&, const std::vector, std::allocator<_T2> >, std::allocator, std::allocator<_T2> > > >&) const [with T = std::string ()(), charT = char]'
private/SVCExecutable.cpp:69: instantiated from here
/usr/include/boost/program_options/detail/value_semantic.hpp:81: error: function return type cannot be function
/usr/include/boost/program_options/detail/value_semantic.hpp:81: error: function return type cannot be function
/usr/include/boost/lexical_cast.hpp:185: error: function return type cannot be function
/usr/include/boost/any.hpp: At global scope:
/usr/include/boost/any.hpp: In instantiation of `boost::any::holder':
/usr/include/boost/any.hpp:159: instantiated from `ValueType* boost::any_cast(boost::any*) [with ValueType = std::string ()()]'
/usr/include/boost/any.hpp:167: instantiated from `const ValueType* boost::any_cast(const boost::any*) [with ValueType = std::string ()()]'
/usr/include/boost/program_options/detail/value_semantic.hpp:28: instantiated from `void boost::program_options::typed_value::notify(const boost::any&) const [with T = std::string ()(), charT = char]'
private/SVCExecutable.cpp:69: instantiated from here
/usr/include/boost/any.hpp:125: error: field `boost::any::holder::held' invalidly declared function type
/usr/include/boost/any.hpp: In function `ValueType* boost::any_cast(boost::any*) [with ValueType = std::string ()()]':
/usr/include/boost/any.hpp:167: instantiated from `const ValueType* boost::any_cast(const boost::any*) [with ValueType = std::string ()()]'
/usr/include/boost/program_options/detail/value_semantic.hpp:28: instantiated from `void boost::program_options::typed_value::notify(const boost::any&) const [with T = std::string ()(), charT = char]'
private/SVCExecutable.cpp:69: instantiated from here
/usr/include/boost/any.hpp:159: error: cannot convert `std::string (**)()' to `std::string (*)()' in return
/usr/include/boost/lexical_cast.hpp: In function `Target boost::lexical_cast(Source) [with Target = std::string ()(), Source = std::basic_string, std::allocator >]':
/usr/include/boost/program_options/detail/value_semantic.hpp:81: instantiated from `void boost::program_options::validate(boost::any&, const std::vector, std::allocator<_T2> >, std::allocator, std::allocator<_T2> > > >&, T*, long int) [with T = std::string ()(), charT = char]'
/usr/include/boost/program_options/detail/value_semantic.hpp:149: instantiated from `void boost::program_options::typed_value::xparse(boost::any&, const std::vector, std::allocator<_T2> >, std::allocator, std::allocator<_T2> > > >&) const [with T = std::string ()(), charT = char]'
private/SVCExecutable.cpp:69: instantiated from here
/usr/include/boost/lexical_cast.hpp:185: error: function return type cannot be function
/usr/include/boost/program_options/detail/value_semantic.hpp:81: instantiated from `void boost::program_options::validate(boost::any&, const std::vector, std::allocator<_T2> >, std::allocator, std::allocator<_T2> > > >&, T*, long int) [with T = std::string ()(), charT = char]'
/usr/include/boost/program_options/detail/value_semantic.hpp:149: instantiated from `void boost::program_options::typed_value::xparse(boost::any&, const std::vector, std::allocator<_T2> >, std::allocator, std::allocator<_T2> > > >&) const [with T = std::string ()(), charT = char]'
private/SVCExecutable.cpp:69: instantiated from here
/usr/include/boost/lexical_cast.hpp:191: error: invalid conversion from `std::string (*)()' to `int'
/usr/include/boost/lexical_cast.hpp:187: warning: address of local variable `result' returned
/usr/lib/gcc/i386-redhat-linux/3.4.5/../../../../include/c++/3.4.5/limits: At global scope:
/usr/lib/gcc/i386-redhat-linux/3.4.5/../../../../include/c++/3.4.5/limits: In instantiation of `std::numeric_limits':
/usr/include/boost/lexical_cast.hpp:131: instantiated from `boost::detail::lexical_stream::lexical_stream() [with Target = std::string ()(), Source = std::basic_string, std::allocator >]'
/usr/include/boost/lexical_cast.hpp:186: instantiated from `Target boost::lexical_cast(Source) [with Target = std::string ()(), Source = std::basic_string, std::allocator >]'
/usr/include/boost/program_options/detail/value_semantic.hpp:81: instantiated from `void boost::program_options::validate(boost::any&, const std::vector, std::allocator<_T2> >, std::allocator, std::allocator<_T2> > > >&, T*, long int) [with T = std::string ()(), charT = char]'
/usr/include/boost/program_options/detail/value_semantic.hpp:149: instantiated from `void boost::program_options::typed_value::xparse(boost::any&, const std::vector, std::allocator<_T2> >, std::allocator, std::allocator<_T2> > > >&) const [with T = std::string ()(), charT = char]'
private/SVCExecutable.cpp:69: instantiated from here
/usr/lib/gcc/i386-redhat-linux/3.4.5/../../../../include/c++/3.4.5/limits:290: error: function return type cannot be function
/usr/lib/gcc/i386-redhat-linux/3.4.5/../../../../include/c++/3.4.5/limits:292: error: function return type cannot be function
/usr/lib/gcc/i386-redhat-linux/3.4.5/../../../../include/c++/3.4.5/limits:295: error: function return type cannot be function
/usr/lib/gcc/i386-redhat-linux/3.4.5/../../../../include/c++/3.4.5/limits:297: error: function return type cannot be function
/usr/lib/gcc/i386-redhat-linux/3.4.5/../../../../include/c++/3.4.5/limits:299: error: function return type cannot be function
/usr/lib/gcc/i386-redhat-linux/3.4.5/../../../../include/c++/3.4.5/limits:301: error: function return type cannot be function
/usr/lib/gcc/i386-redhat-linux/3.4.5/../../../../include/c++/3.4.5/limits:304: error: function return type cannot be function
/usr/lib/gcc/i386-redhat-linux/3.4.5/../../../../include/c++/3.4.5/limits:308: error: function return type cannot be function
/usr/include/boost/lexical_cast.hpp: In member function `bool boost::detail::lexical_stream::operator>>(InputStreamable&) [with InputStreamable = std::string ()(), Target = std::string ()(), Source = std::basic_string, std::allocator >]':
/usr/include/boost/lexical_cast.hpp:189: instantiated from `Target boost::lexical_cast(Source) [with Target = std::string ()(), Source = std::basic_string, std::allocator >]'
/usr/include/boost/program_options/detail/value_semantic.hpp:81: instantiated from `void boost::program_options::validate(boost::any&, const std::vector, std::allocator<_T2> >, std::allocator, std::allocator<_T2> > > >&, T*, long int) [with T = std::string ()(), charT = char]'
/usr/include/boost/program_options/detail/value_semantic.hpp:149: instantiated from `void boost::program_options::typed_value::xparse(boost::any&, const std::vector, std::allocator<_T2> >, std::allocator, std::allocator<_T2> > > >&) const [with T = std::string ()(), charT = char]'
private/SVCExecutable.cpp:69: instantiated from here
/usr/include/boost/lexical_cast.hpp:149: error: ambiguous overload for 'operator>>' in '((boost::detail::lexical_stream, std::allocator > >*)this)->boost::detail::lexical_stream, std::allocator > >::stream >> output'
/usr/lib/gcc/i386-redhat-linux/3.4.5/../../../../include/c++/3.4.5/bits/istream.tcc:87: note: candidates are: std::basic_istream<_CharT, _Traits>& std::basic_istream<_CharT, _Traits>::operator>>(std::basic_istream<_CharT, _Traits>&(*)(std::basic_istream<_CharT, _Traits>&)) [with _CharT = char, _Traits = std::char_traits]
/usr/lib/gcc/i386-redhat-linux/3.4.5/../../../../include/c++/3.4.5/bits/istream.tcc:93: note: std::basic_istream<_CharT, _Traits>& std::basic_istream<_CharT, _Traits>::operator>>(std::basic_ios<_CharT, _Traits>&(*)(std::basic_ios<_CharT, _Traits>&)) [with _CharT = char, _Traits = std::char_traits]
/usr/lib/gcc/i386-redhat-linux/3.4.5/../../../../include/c++/3.4.5/bits/istream.tcc:102: note: std::basic_istream<_CharT, _Traits>& std::basic_istream<_CharT, _Traits>::operator>>(std::ios_base&(*)(std::ios_base&)) [with _CharT = char, _Traits = std::char_traits]
make[1]: *** [debug.obj/SVCExecutable.o] Error 1
make[1]: Leaving directory `/data/aykroyd/cvs/Optimization/five0/PredictionAndBidding/SegmentVolumeCalculator'
make: *** [default] Error 2
make: Leaving directory `/data/aykroyd/cvs/Optimization/five0/PredictionAndBidding/SegmentVolumeCalculator'
exit
Connection to master closed.

exit

Compilation exited abnormally with code 2 at Thu Jul 26 10:53:43

Tuesday, July 24, 2007

emacs and the search for tool zen

NOTE: Originally, this was going to be one post with everything that I've figured out so far about how to use emacs, what tools to extend it with, and what custom extensions to hand-roll based on my current needs. Unfortunately, my tendency towards diarrhea of the mouth being what it is, I've filled more space than I feel comfortable spending on drivel about why I care about this, why tools are important, and why my individual needs are like a unique snow flake that everyone should care about. So, in the interests of optimizing the organization of the information a bit, I'm breaking it up. So this post will contain only introductory and motivation of problem-type content. Future posts will cover what I've actually done, ideas that I like, and hopefully provide some value at least based on the combination of ideas, if not the originality.

So I recently moved from Boston to Northern California. Very shortly thereafter, the startup that I had been at for nearly 2 years was acquired and I was on the job market. I've ended up taking a job with the engineering group in a smallish R&D team that works on optimization problems. The environment is completely different and in many ways I feel like I've stepped back about 20 years into the history of programming. We have to do most of our builds, debugging, etc on remote machines across a connection that is too slow for X forwarding, so most of the coding happens remotely as well. Since I've spent 5 years in Java-based IDEs -- first Eclipse and then IntelliJ, writing C++ ssh'd into a server is unfamiliar ground for me. So I decided to figure out what everyone else was using to do their development. And I was stunned to learn that my new colleagues used vim + grep...

and that was it...

vim and grep ...

To be frank, after having just finished my first warm-up coding assignment during which I was reintroduced to compiler errors that results from misspelled variables and calls to functions that don't exist: m_InitDatabase not m_initDatabase or obj->Run() not obj->Start(), I was hoping for a bit more than that.

So that's the fluffy background... I like tools that make it easier, faster, and more enjoyable to cut code. So I'm in search of the "right" development environment to make me productive with this new set of environmental constraints. I suspect it will be a somewhat lengthy process to configure my environment as well as I'd like and this blog will hopefully reflect that as well other topics that I find interesting.

So why bother?

I have the sense that people who use stock VIM and whatever else is available from bash wonder why I care. The power offered by the standard toolset available on any Unix system is amazing and I think it's important for developers to be comfortable using those tools. Spend any amount of time debugging production issues on servers and your knowledge of UNIX will be rewarded. But I've also been pampered by the amazing development tools offered by IntelliJ and developing without such tools feels needlessly painful. So I'll throw out a random laundry list of things that I love about IntelliJ (not that most of them are exclusive to it. But they are well executed there):
  1. Auto Completion: First, you don't make spelling mistakes and the completion list is a crutch to jog your memory of just what you named a variable or method anyway. Second, it serves as an inline method of looking at simple documentation. Often you can avoid a trip to google to search for an API's documentation just by checking completion options on an object. Both of these uses save a lot of tme.
  2. Fast Keyboard-based File Access: IntelliJ has this feature that I'm in love with. I think more than anything, this is what won me over to it coming from Eclipse. If you press Command-n, and start typing a class name "mysimpl" it will generate a list of all the classes in the project that match that search string. If there are none it will search in your library. Alone, that's pretty cool. But it also has a number of shortcuts. For example, if you type only capital letters, it will let any lower case letter be in between them (e.g., MSS matches MySimpleServer or ManualSarcasmService). This same tool can also be used to search files (excluding java files) and for symbols.
  3. Code Generation: One of the things that I love about really expressive languages is how succinctly you can express yourself. But most of day to day use Java or C++ and with these languages there is a lot of boiler plate code: from class definitions to constructors, getters/setters, to equals and hashcode methods. After using IntelliJ for a while it stopped occurring to me that I should ever have to write these methods unless there was something out of the ordinary. Writing boiler plate code by hand is error-prone and a waste of time. What I like about IntelliJ's execution of this versus Visual Studio was that it's not wizard-based. I define a class member in the .java file and then position the cursor where I want the getter/setter to go, hit Ctrl-n, select getter/setter, and I'm done. No wizards, no mouse clicks, no graphic representations of my class... done.
  4. Finding Uses of Methods/Variables: When debugging a problem, learning a new code base, or thinking about a refactor, it's incredibly useful to be able to jump from a method invocation to its implementation or find all of the uses of a method. Similarly, IntelliJ provides tools to see a graphic representation of an object's hierarchy. By contrast, determining where the method or class is implemented, opening that file, and then moving to the definition is just too slow.
  5. Source Control Integration: I don't mind using svn or cvs command lines for some operations. But generally when I want to diff a file with a previous version or look at its history, I'm actually looking at the file in my editor. So it's handy (I might start to belabor the point... time saving) to perform that operation in the context of my editor.
  6. Integrated Debugging: I'll admit it. I'm a wus. I don't like using command line debuggers. Everything about typing list to see where I am in the file and setting breakpoints by file and line number feels wrong to me. I understand that this is because I'm lacking character. And I'm not hardcore. But I'm not going to change. I like having the option of using a mouse when I debug.
So there's a few of the high impact features that I've come to expect from my development environment. Without these, I find coding and code related activities to be markedly slower and I find myself doing work that would be better completed by a computer. Computers are way more patient than I am.

In the posts that I'll be adding soon I'm going to talk about emacs22, tramp, and a number of extensions that I've been testing.