Monday, October 29, 2012

File wrapper class in C++ (2)

Last time I showed how to implement a File class. In the solution Brian (pictured on the right) and me used a second class as a wrapper around a standard FILE pointer. The two classes worked together by means of a shared_ptr. The shared_ptr ensured that the file would not be closed until there were no more references to the file wrapper object. Reread the last post to see what I'm talking about.

A reader of this blog pointed out that there is another solution, one that turns out far more idiomatic. The constructor of shared_ptr accepts an instance of a functor, named a Deleter. This functor will be called when the reference count drops to zero, and the object will be destructed. The resulting code looks like this:
#include <cstdio>
#include <tr1/memory>

class File {
public:

    // conversion constructor
    File(FILE *f) : stream_(std::tr1::shared_ptr<FILE>(f,
        FileDeleter() )) { }

    // copy constructor copies the shared_ptr
    File(const File& f) : stream_(f.stream_) { }

private:
    std::tr1::shared_ptr<FILE> stream_;

    // local functor closes the FILE upon destruction   
    class FileDeleter {
    public:
        void operator()(FILE *f) const {
            if (f != NULL)
                std::fclose(f);
        }
    };
};
Right here I inlined the FileDeleter class. You may also choose to put it outside the enclosing class, or it can even be implemented as a struct with operator()().

All in all, this solution is more elegant and more idiomatic than the previous one.

Saturday, October 13, 2012

File wrapper class in C++

Last week my good friend Brian (see picture on the right) and I encountered an interesting problem: wrapping up a file descriptor in a File class. Making class wrappers in C++ generally isn't all that hard, but this particular one was a lot more tricky than we had expected.

What we wanted to have was a File class that would act the following way:
  • based on standard C's FILE*
  • factory function open() creates a File, and opens it
  • destructor of File closes the open file
So that you could write code like this:
    File f = open("filename");
    f.write("hello\n");

    // destructor closes the file
You might code open() like this:
File open(const char *filename) {
    File f;
    f.open(filename);
    return f;
}
Unfortunately, this code doesn't work, as it has a bug. Do you see it already?
The thing is, when you return f, the value f gets copied [by means of the copy constructor] and then the destructor gets called in the original f, because it goes out of scope. Since our destructor closes the open file, the result is that the file is closed once open() returns. Read that last sentence again, argh.

There are two issues at play here:
1. The copy constructor doesn't work because you can't properly copy an open FILE* object.
2. Exiting the function causes the destructor to run, closing the file — despite the fact that we are returning the instance.

Can I have the attention of the class
How to solve this problem, and no ugly hacks, please. A good solution lies in using shared_ptr. You can copy a shared_ptr and safely return it, and its internal reference count will cause the File instance to stick around. The File object will not destructed until it really needs to be.
[More specifically, the shared_ptr keeps a pointer to dynamically allocated memory, which never auto-destructs like instances on the stack do. The copy constructor and operator=() of shared_ptr happily copy the pointer, but while doing so it increments a reference counter that keeps track of how many shared_ptr instances are currently referencing the dynamically allocated object. The destructor of the shared_ptr decrements the reference count, but does not delete the pointer until the refcount drops to zero. So, a shared_ptr can be used to 'keep objects around' for as long as they're needed].

Beware, you can't simply dump a FILE* into a shared_ptr, since the FILE* may be allocated with malloc() rather than with new. And besides, a FILE* opened (or allocated) with fopen() must be deallocated using fclose(). Therefore we wrap the FILE* into another new class named FileStream and use that class with the shared_ptr.
#include <cstdio>
#include <tr1/memory>

class FileStream {
public:
    FileStream(FILE *f) : f_(f) { }

    ~FileStream() {
        if (f_ != NULL)
            std::fclose(f_);
    }

    ...

private:
    FILE *f_;
};

class File {
public:

    // copy constructor copies the shared_ptr
    File(const File& f) : stream_(f.stream_) { }

    ...

private:
    std::tr1::shared_ptr<Filestream> stream_;
};

File open(const char *);
Wrapping up
This is one of the reasons why C++ is so hard. In any other language, you'd just return the basic File object and be done with it. Even simple things like returning a value is hard in C++. You need a degree in Computer Science to even attempt it.
[It makes sense when you realize that upon exiting the subroutine, the stack is cleaned and therefore the destructor is called. But we're returning a value that is on that stack ... so the value needs to be copied to another place, first].

Of course, I should've used the std::fstream class to begin with ... but std::fstream doesn't give me fdopen() nor popen(). It feels so unfinished. The documentation doesn't say whether its destructor closes the stream. I didn't feel like fixing fstream, so I went with FILE*.

C++ is a nice language but sometimes trivial things can get quite complex. Situations like this remind you that coding in C++ is really hard.

Saturday, September 8, 2012

What if Python had a strict mode

Python, our most-beloved byte-code interpreted language, recognizes only a few errors during compile time. Being a dynamic language, it simply doesn't see errors until the interpreter tries to run the code, only to find out that it isn't going to work, and throws an exception. This delayed nature of giving an error can be very frustrating. A Python code may run alright for weeks and then suddenly go BOOM as it runs into an exception. Wouldn't it be nice if Python could catch trivial programming errors at compile time? What if Python had a strict mode?

Perl, ‘that other’ byte-code interpreted language, has a strict mode in which the compiler is, well, more strict about code constructs that may well be programming mistakes. In Python, there is no such thing as a strict mode. But suppose there was, what would it be like? It would surely be nice if the following could be caught at compile-time.
  • syntax errors, including indentation errors;
  • undeclared local variables: this expresses the need for a local keyword that declares local variables. Alternatively, Python could have an operator := that declares and assigns a new local variable, like Go has. (In addition, do away with the global keyword. New variables outside a function should be treated as global in that module or class scope);
  • stricter type checking: reusing a variable as a variable of a different type is an error. This would eliminate the bad coding style of reusing the same variable name for a different purpose;
  • type errors: certain operations can't be done on certain types; e.g, the divide operation does not exist for the string type;
  • format string checking: check the number of arguments for format strings;
  • format string checking: check the types of the arguments for format strings.
Note that these suggestions would change Python from a dynamically typed language to a statically typed one — which is something we would like to avoid. On the other hand, it paves the way to more far reaching changes:
  • strict checking of function parameter types: function parameters should specify type;
  • strict checking of function return types: functions should have a return type.
Python does have typing, but you seldom explicitly state the type of a variable. There are a few cases where you do explicitly specify the type. For example, when creating a new class: class MyType(object) declares a class of type MyType with parent class object. When catching an exception, you specify what type of exception: e.g. except KeyError.

Wrapping up
Python is a dynamically typed language. This is one of its key strengths; you can do things in Python which are not possible in other languages due to their typing restrictions. The dynamic nature of the language allows for quickly throwing some code together. However, particularly for maintaining larger codes this becomes more of a problem. Python does not offer the option of having a strict mode, like there is in Perl. I feel that Python could benefit from having a strict mode.

A lint-like tool for checking your Python code is PyChecker.

Sunday, August 26, 2012

Crash course in ncurses

The UNIX terminal is an archaic text interface to the computer. It evolved from teletype machines, which were essentially keyboards with a line printer attached. Any text I/O would be directly printed on paper. Later, the paper was replaced by a monitor. If you want to create a terminal program that has a text interface anything fancier than just lines of text scrolling up the screen, you are likely to go with ncurses. ncurses is a library for creating text mode user interfaces.

A word of warning, like with all things that are new, learning to use ncurses is very much a step by step process. ncurses is not a magical tool for easily crafting great UIs. It has the same 1970s style interface as the rest of the UNIX that we all love.
In this post I will throw a lot of library function names at you with only a minimal description of what they do. Trust me, this is all you need to get you kickstarted with ncurses, but do check out the man pages afterwards to get more deeply involved.

Start off by including <ncurses.h>. First thing in main(), initialize the library with a call to initscr(). To deinitialize, call endwin(). You should always call endwin() upon program termination to prevent leaving the terminal in a state that the UNIX shell (or rather, the user) doesn't like. By the way, why endwin() isn't named deinitscr(), we may never know.

initscr() initializes a global variable in the library named stdscr. It's a pointer to an ncurses window that represents the terminal screen. It is not really the terminal screen, but think of it as a back buffer; you do your operations on the back buffer, then call refresh() to get output to the screen. Because of this, do not use printf() to display text. Instead, use the wprintw() function to print to a window and call refresh() at an appropriate time afterwards. To print text at a fixed position on the screen or window, use mvwprintw(). You can print text with attributes and in other colors using attron() and attroff(). For example, attron(A_BOLD) enables the bold attribute. Clear the screen with clear(). To get the screen dimensions, call the macro getmaxyx(stdscr, height, width).

The cursor can be freely controlled. You can hide or show it using curs_set(visible), and you can move it around with move(y, x).

You can get input key presses with getch(). But before that, you should put the terminal in the right mode, usually during program initialization. Normally, the terminal is in cooked mode, meaning that a lot of processing has already been done before the pressed key was passed on to the running process. For example, the user might suspend the process by hitting Ctrl-Z.
By default, input is line buffered. This means that the user has to hit return before the key is passed to the process. To keep this from happening, set cbreak mode (break after each character). Setting cbreak mode is easy: call cbreak().
getch() will echo the characters to the screen. If you don't want this, call noecho(). What's really nice, getch() responds to nearly all keys on your keyboard, including the arrow keys, page up, page down, and so on. The symbolic constants for keys are named KEY_xxx. They should be in the man page for curs_getch or do a grep for "KEY_" in /usr/include/ncurses.h.
The function keys are not enabled by default. Set keypad(stdscr, true) to enable them.
getch() will implicitly call refresh() as necessary.

Call raw() to set the terminal in raw mode. In raw mode, interrupt and flow control characters like Ctrl-C, Ctrl-Z, and the like behave like any other key press without producing a signal. My personal preference is to set cbreak, noecho, and keypad, but there are cases in which raw mode is well-suited too.
You may be familiar with the tcsetattr() function that manipulates the terminal mode. Word of advice: don't bother using it—the ncurses functions are easier and more comprehensible.

ncurses allows you to work with windows. A window is a rectangular screen area. Allocate a new window with newwin(), and deallocate it with delwin(). To neatly close a window and erase its content, use wborder() and call wrefresh(), before finally deallocating it with delwin().
Subwindows are created using derwin() [as in ‘derived window’]. I prefer derwin() over subwin() because derwin() uses coordinates relative to its parent window.
Subwindows are windows just like any other ncurses window, they are all of type WINDOW pointer. Again, deallocate with delwin().

Windows are confined to the screen area. To create windows larger than the screen, or even off-screen windows, use pads. A pad window is allocated using newpad(), and deallocated using ... delwin(). Because a pad can only have a small visible portion onscreen, you should use prefresh() rather than wrefresh() to redisplay pad windows.

This is ncurses in a nutshell. It should be just enough to get you started. Don't forget to call refresh()! Frankly, I just couldn't get used to ncurses odd parameter order of "h,w,y,x" so I wrapped everything in my own interface. People say it's better to learn the standard API, but hey. ncurses is not exactly UIKit for terminal based apps. But fair enough, it's fun being able to handle the arrow keys in a UNIX program, and to print text at any screen position without having to write ANSI escape sequences.

For a more elaborate instruction with code examples and everything, please see the NCURSES Programming HOWTO at The Linux Documentation Project.