Callback Functions

Unless you've skipped ahead to the WvStreamList section, this section will seem even more useless than the one on select(). But trust us, when you have lots of different streams talking to each other later on, you'll be really glad to have callback functions around.

Here's a quick example, using one of the predefined stream callbacks called autoforward().

	  
/*
 * A WvStream example.
 *
 * Some text about this example...
 */

#include <wvstream.h>

int main()
{
    wvcon->autoforward(*wvcon);

    while (wvcon->isok())
    {
	if (wvcon->select(-1))
	    wvcon->callback();
    }
}

 	 

The above program does almost exactly what the previous ones did, except that instead of manually reading and writing strings, we tell the stream that its "default operation" is to auto-forward its data to wvcon... which is, in this case, itself. The result is that we forward stdin to stdout as before, only without the extra text surrounding it.

Almost all WvStreams programs end up using callbacks for most of their work. That's because with callbacks, each stream knows what to do with its own data, and you don't have to say it in the main program. That's what object-oriented programming is all about, after all.

In fact, the main loop in most WvStreams programs ends up looking a lot like the main loop in the program above: wait until the stream is ready, then run its callback function.

What does autoforward() do, exactly? Let's write a callback function ourselves and demonstrate one possibility.

	  
/*
 * A WvStream example
 *
 * Some text about this example...
 */

#include <wvstream.h>

void mycallback(WvStream &s, void *userdata)
{
    WvStream *outstream = (WvStream *)userdata;
    
    char *str = s.getline();
    if (str)
	outstream->print("You said: %s\n", str);
}

int main()
{
    wvcon->setcallback(mycallback, wvcon);

    while (wvcon->isok())
    {
	if (wvcon->select(-1))
	    wvcon->callback();
    }
}

 	 

Note that mycallback() calls getline() with a (0) delay parameter; you almost never want a callback function to wait for something (although it certainly can, if you really want). Instead, if getline can't find a whole line of text to read, we simply return from the callback and wait until next time.

This brings up an interesting point about select() and getline(): select() might say yes, the stream is ready for reading, but getline() might still return NULL. Why? Because getline() looks for a terminating newline. If the only input is the letters "abcdefg" (without a trailing newline character) then the stream really is ready for reading, but getline hasn't retrieved a whole line of input. Don't worry, though, running getline() once clears the input-ready condition, so the callback will only get called again when more data is received.

But whatever you do, don't be fooled into thinking that just because you're in the callback function, that data will always be returned on a read() or getline(). Always, _always_ check the return values first.