Wednesday, November 17, 2010

Emacs: Copy Environment Variable from Shell

Sometimes I get annoyed when my emacs session has different environment variable settings than some shell buffer I have running in the session. The most painful is when a Makefile depends on environment settings that I don't have in my .profile. Command-name completion in a shell buffer can also be painful if you have changed your path. And of course I'd like gdb to start up with the correct environment every time -- you can set the variables inside gdb, but that gets old on those days when gdb itself crashes again and again.

I kept pasting export FOO=bar into the *scratch* buffer and editing it into (setenv "FOO" "bar") and eval'ing that. After the 1000th time of doing that, I decided to automate it. Turns out emacs already has an interactive function for copying an environment variable from the shell, but you have to type in the variable name. I decided to write a little function to look for the last export, or the last echo $FOO, and copy that variable:

(defun engisneering-shell-copy-env-var ()
  (interactive)
  (let* ((expat "\\(export +\\([^=\n]+\\)=\\(.+\\)\\)")
         (echpat "\\(echo +\\$\\(.+\\)\n\\(.+\\)\\)")
         (cshpat "\\(setenv +\\([^ \n]+\\) +\\(.+\\)\\)")
         (patt (concat shell-prompt-pattern
                       "\\(" expat "\\|" echpat "\\|" cshpat "\\)")))
    (save-excursion
      (if (re-search-backward patt)
          (let* ((m (or (and (match-beginning 2) 3)
                        (and (match-beginning 5) 6) 9))
                 (var (buffer-substring (match-beginning m) (match-end m)))
                 (oldval (or (getenv var) " ")))
            (shell-copy-environment-variable var)
            (setq val (getenv var))
            (message "Old %s=%s; New %s=%s" var oldval var val))))))

Run this function inside your shell buffer, and it will search backwards for the last environment variable action and bring that variable setting into the emacs session. It's also nice because it gives you a message in the minibuffer showing the change. Add a local-set-key -- I like C-c C-v -- in your shell-mode-hook, and you're good to go.

Wednesday, September 22, 2010

Let's Allocate Stuff!

Nobody's perfect, of course, but when there are so many things wrong with a small piece of C++ code, it's hard to be placid. How about:

  std::vector v = new std::vector;
  // ...
    v->push_back(new std::string("..."));
  // ...
  std::ostream *str = get_me_a_stream(...);
  for (int i = 0; i < v->size(); i++) {
    *str << (*v)[i]->c_str();
  }
  delete str;
  // loop to delete v and its elements.

Ow, my head is spinning from all of these needless allocations. Let's not wear out new and delete.

Well, if you first learn C and then learn C++, your head might still be in Pointer Land. But the unnecessary call to c_str() makes it hard for me to get any work done.

Saturday, August 14, 2010

gcc: error: expected `)' before '&' token

Duplicate post here just to get two related error messages into page titles. That's as much SEO as I know how to do.

If you get this error from g++, and everything looks correct to you, maybe you are defining a constructor without qualifying it with the class name. See the example on this post.

gcc: error: expected unqualified-id before ')' token

I was scratching my head for a few minutes over the g++ error message in the title. Google turned up a couple of red herrings, like this one at Stack Overflow where a clueless noob #defined the name of his class.

My mistake had been to define a constructor without putting the Class:: in front of it:

    Class() // WRONG!!!
    : Super(), _duper()
    {
    }

instead of

    Class::Class()
    : Super(), _duper()
    {
    }

It wasn't as stupid as it sounds, I was cutting and pasting inlined functions into the .cpp file.

Wednesday, July 14, 2010

Can't Locate Object Method via Package

I recently got a confusing Perl error message for a line of code that I thought was a simple assignment to a hash member:

Can't locate object method "patterns" via package "objdir(/[^ ]*)?" (perhaps you forgot to load "objdir(/[^ ]*)?"?) at ./filter.pl line 24.

A Google search turned up many forum entries asking about the message, but they all had to do with open-source projects where there was indeed a package method call that had gone wrong.

After I rubbed my eyes long enough, it was obvious what I had done: left the '$' off the front of the hash reference. I had typed:

    patterns{"objdir(/[^ ]*)?"} = "dirs/objs"; # WRONG!

instead of:

    $patterns{"objdir(/[^ ]*)?"} = "dirs/objs";

I even had use strict; use warnings; on, but only got the error message. Anyway, since Google didn't help me out, let's see if this page with the error text in the title floats to the top and helps someone else out someday.

Friday, June 18, 2010

Blessed Mother of Commented-out Code

It's the little things in life that give pleasure, isn't it? Like the lovely comment below, which has adorned a source file at my company for nearly sixteen (16) years now (meaning the file is at least 5 years older than the company).

/*****************************#if (0) //[
// Marked code has been deleted   -- XXXX (18 August, 1994)
// No need to check whether the value is X/X/X
// Only the size is to be checked which has been done
[[[ DELETED

      char  val;

      val=tolower(*((char*) var)->getValue());
      if ((val != 'X') && (val != 'X') && (val != 'X'))
          return 0;
]]]
   #endif //]
*************************************/

There are many facets to this beautiful gem. First you'll notice that there is a nice C++-style // comment -- complete with the date and the signature of Mr. XXXX -- itself commented out by a C-style /* comment. Too bad the dead code isn't commented with //, so that my grep for tolower would have stood out as unnecessary.

But look, really the /* is there to comment out an #if 0. The guy must have wanted emacs to colorize the dead code as a comment -- I don't think emacs had font-lock back then, but there was the hilit19 package.

Still, you can't be too careful, so let's also wrap that code up in a [[[DELETED ... ]]], in case there is some human that can't read the C comments and the if-0, but who will understand what "triple-bracket DELETED" means.

Finally, it appears the conditional was originally written as #if (0) [ ... ] -- was that ever legal C? -- but when that wouldn't compile, the brackets had to be commented out, because every character in this file is too precious to ever delete. This must be the bad code afterlife. You can check out anytime you like, but you can never leave.

How did all this happen? Is it an accumulation of different commenting-out strategies that occurred over time? Or is it just an unfortunate snapshot of one man's frenzied efforts to remove 3 lines of code without actually deleting anything? Sadly, we can only speculate on what happened, because these lines entered our repository in exactly this condition over 10 years ago.

I love deleting dead code, but I can't touch this one. It's an antique. And so ugly that it's beautiful.

Thursday, June 10, 2010

Csh Skips Last Line in Script

Oh, joy.  I've found another thing to hate about csh.  If the last line of your script doesn't have a newline after it, that line won't be executed.  It will be silently ignored.  That isn't very useful.

If emacs knows you are editing a shell script, it will automatically put a newline at the end if you didn't do it yourself.  But if you have an ordinary text file that you will run with source, it better have a newline at the end.  You can get emacs to either ensure that or warn you about it by adding one of the following lines to your .emacs file:

;; Quietly add a newline if missing.
  (setq require-final-newline t)

  ;; Ask if you want a newline at end of file.
  (setq require-final-newline 'ask)

Googling for this problem, I found another nice csh rant.

Wednesday, April 28, 2010

Tcl Case Analysis Lunacy

This goes way back to the roots of this blog. Complaining about Tcl, and complaining about code that breaks down into unnecessary and confusing case analysis -- those topics were the first three posts here.

Unsurprisingly, the toy language that is Tcl is serving me up some ridiculous case analysis. It has to do with the C++-side of a Tcl integration. When you look at the internals of the language objects created by the Tcl interpreter, some things that are conceptually the same have different internal representations. It's bad enough that I have to succumb to case analysis to figure out what's what: but on the other side of that wall, Tcl had to do some case analysis to put them all in different representations! Bad, naughty Tcl.

Here's what's bugging me. When you look at a Tcl_Obj over in the C world, it has a typePtr member to distinguish different types of things:

  • A simple name like A has a NULL typePtr.
  • So does a list of things in braces, like { A B }.
  • A name with some special characters like A[0] has type "string".
  • So does a list of things in braces which extends over a few lines, like:
    { A \
          B }
    
  • An explicit list like [list A] has type "list".
  • A quoted string like "A B" has NULL. At least I think so. I lost track.
So obviously there are a bunch of if statements in the back-end tangling stuff up like this. But it's nearly impossible to untangle it on the C side.

Stupid Tcl. Stupid case analysis.

Wednesday, March 10, 2010

Don't Give Variables Negative Names

Sometimes programmers get caught up in the absence of something. Don't let that leak over into the name of a variable or data member:
bool Job::acceptable(const JobCandidate &applicant) const
{
  bool noFelonyConvictions = findFelonyConvictions(applicant);
  return noFelonyConvictions && qualified(applicant);
}
The noFelonyConvictions variable documents the code in a readable way, but it gets silly if the code ever changes so that you have to initialize it, or if someone ever wants to know if there are felony convictions. Things become less readable:
  bool noFelonyConvictions = true;
  if (applicant.findRecords(FELONY)) {
    noFelonyConvictions = false;
  }
  return applicant.qualified(!noFelonyConvictions);
It all makes more sense if you name the variable felonyConvictions, which initializes naturally enough to false, can be set to true if one is found, and can be negated to show absence. Also, if you later become interested in the number of convictions, there will be fewer code changes if the variable changes from a bool to an int.

Whenever you notice yourself putting a "no" into a variable name, get rid of it.

Thursday, February 4, 2010

How to Undeclare a bash Function

It took me a few minutes to figure this out, so I thought I might as well pass it along.

If you've declared a bash function, and then decided you don't really want it, either because you went ahead and implemented it as a script somewhere, or you gave it a bad name or something, you undeclare it like this:

  $ unset -f myfunc

Monday, February 1, 2010

Nice Code Structure

Here's a brilliant one I just ran across at work (variables changed to protect the... to protect someone):

  for (i = 0; i < lim; i++) {
    if (someCondition(n[i])) {
      warn("you can't do that");
      continue;
    }
    switch (n[i].type()) {
      default:
        nn = 0;
        warn("we're ignoring this");
        break;
    }
    orig->doSomethingWith(nn);
    delete nn;
  }

How do you like that switch with only a default branch? Pretty cool, huh? Almost is nice is the delete that only ever sees a NULL. Then when I realized doSomethingWith() is a no-op if it gets a NULL, I felt even luckier.

Look, if you're making such a big change to some code that you take out all the branches of a switch, isn't it worth a couple minutes to clean this up and show what is really happening? After all, the code really does this:

  for (i = 0; i < lim; i++) {
    if (someCondition(n[i])) {
      warn("you can't do that");
    }
    else {
      warn("we're ignoring this");
    }
  }
It probably makes sense to only give the second warning, since who cares if you can't do it, when really it would only be ignored anyway. And at that point you might wonder why you need to give the same warning lim times.

Ugly code kills. Kill ugly code.