Wednesday, 26 November 2008

Using Boost Bind and Boost Function with Qt

One very, very, very annoying feature of Qt in C++ is how very difficult it is to create bespoke slots.

With gtkmm, you can more or less get away with using boost::bind so long as the bind results in a 0-ary function (otherwise the signal/slots library for gtkmm is not compatible.)

With Qt, you have to write a new member function, or heaven forbid, a new class. This is too much typing for me. Ideally, what I would like to do is something like:


connect(some_button,SIGNAL(clicked()),
boost::bind(&Foo::bar,something,else,entirely));


Doing this with bare Qt, I'd have to:

  • Create a new slot function: Foo::bar_special_case

  • Add member variables for the variables else,entirely

  • Finally use it as: connect(some_button,SIGNAL(clicked(),something,SLOT(bar_special_case()))



After doing this for the 100 billionth time, I realized that there should be a better way to do this. After months of investigation, I have discovered that there is no better way to do this.

Just kidding.

The idea is to create a slot handler that accepts boost::function types:

struct SignalHandler0 : QObject
{
private:
Q_OBJECT
public:
SignalHandler0(QObject * parent,
boost::function<void(void)> const & f):
QObject(parent), // parent will delete this object when destructed
m_f(f) {}

public slots:
void
handleSignal()
{
try
{
m_f();
}
catch(...)
{
// Cannot throw exceptions from signals.
ASSERT_BUG_HERE
}
}
private:
boost::function<void(void)> m_f;
};


If I were to use this class directly, I would write:

connect(button,SIGNAL(clicked()),
// Note: Not a leak as button will delete the handler when destructed
new SignalHandler0(button,boost::bind(&Foo::bar,something,else,entirely)),
SLOT(handleSignal()));


That's pretty much it. Of course, this is a bit too verbose so I'd write a free function:

bool
connect(QObject * sender, const char * signal,
boost::function const & f)
{
return QObject::connect(sender,signal,
// Note: Not a leak as sender will delete the handler when destructed
new SignalHandler0(sender,f),SLOT(handleSignal()));
}


And use it as:::connect(button,SLOT(clicked()),boost::bind(&Foo::bar,something,else,entirely))

The obvious problem with this approach is if you add any overload of the free function + bind and you are in for some fun. I prototyped a solution for this based on the Boost.FunctionTypes library but at the moment, I don't need this (i.e., I am happy with 0-ary bind)

Another thing... Why does the aforementioned function types library not handle boost function and boost bind?

Odd.

PS: I'm not dead. I just smell funny.
PPS: Sorry for so long in between posts, I've been very busy (the good kind!)

Sunday, 5 October 2008

Everything you ever wanted to know about tr1::function but were too afraid to actually know

Working title for a talk I'm giving October 16 at the Vancouver C++ Users Group.

I find three patterns of usage around Boost Function/Bind:


  • The right way

  • The wrong way

  • Running away



My goal for this talk: attendees should no longer be in the dark about how Boost Function/Bind (and by extension, tr1 function/bind) work. If you are reading this blog, coming to this talk, not the author of this post and you have some things you'd like to hear covered, please feel free to leave some comments. I'm pretty sure the intersection of all those groups of people is the empty set but it never hurts to ask!

Heckling must be reserved for the end.

PS: Regular posting will resume some point soon!

Thursday, 31 July 2008

Qt/Lisp: more progress

I've replaced the unsightly (make-instance 'qstring :ch "Foo") with a reader macro: #q"Foo". Ideally we'd use cl:string and qstring interchangeably but right now its too much of a PITA to make that work.

I've also made it possible to use closures for slots (see example below.) Frickin sweet.

Anyway, I started getting into the Dock Widgets example but lost interest after seeing all the code. Here is the code so far. Not very concise yet but will get there. Once I get into make-it-less-ugly mode, I will probably copy what Paul did with EQL as he calls it now.

Also the :foreign-pointer thingy will go away when I get around to adding cffi type translations.

Monday, 28 July 2008

svn-bisect

Well it was only a matter of time... svn-bisect.py lives!

Greg Noel of SCons fame (is that really fame?) has committed a modification of a script I created whose purpose was to binary search the introduction of a bug in a SVN repository.

Example usage of the script is here.

Enjoy.

Why complaining works..

My email to an online store:


Hey guys, I ordered product XYZ from your site yesterday. According to your shipping estimates, it should be here in 2-4 days. However, I see that it is not shipped yet. I have had one bad experience with your service before and would not like it to repeat. Please see that this is so.

Literally 5 minutes later:

Your order has been shipped.

Now if I can only get them to do it before I complain.

Tuesday, 22 July 2008

Simple CFFI - Qt4 integration attempt

Update: Paul has sent me his code. He calls it "EQL" for ECL and Qt. Or something like that anyway! I have put it up here.

The author of the Simple CFFI - Qt4 integration attempt has contacted me to let me know that a modification of the above linked software is in commercial use. I have prodded him to release his modified version. He says he'll do it Real Soon Now (TM).

I don't know if he is reading this blog but maybe a little pressure will help. Just kidding!

What I like most about his method is the use of the introspection capabilities of Qt. Compared to the duct tape and glue I used, his is a lot more elegant. Currently, you cannot extend Qt in Lisp (via inheritance) but all the other goodies are still there. Indeed he tells me that for his application, he extended Qt from within C++ and used these extensions from his Lisp.

I look forward to the update and to using the library.

Friday, 18 July 2008

Qt/Lisp: looking for feedback

Update: You can download the generated code from here.

The tubes were down this morning, so I managed to get up to tutorial 6 in my Qt/Lisp API. It covers about 95% of the API by design so most of the non-extension tutorials/examples should work.



I would like to hear your complaints, suggestions, feedback if you have any.

Note that this API is a first draft and I thought it better to have something out there than wait until everything is perfect.

To start with, my current complaints:

  • (in-package :qt) -> Nothing is actually exported from the package yet :-)

  • (make-instance 'qstring ...) -> A really verbose way to pass a string! Should convert lisp strings to qstrings automagically.

  • Naming: instead of qpush-button, q:push-button

  • No way to extend Qt from within Lisp yet (tutorial 7 won't work)

  • No way to attach arbitrary functions as slots

  • Not Lispy enough.



I expect to address most of these at some point though the "Not Lispy enough" complaint is a bit subjective.

Wednesday, 9 July 2008

Sohail, it doesn't work anymore

Everyone has a story about when they were younger. For example, Joel Spolsky talks about his time in an army bakery (or something like that.) Sometimes, the stories are quite pathetic like Al Bundy and his high school football team. I am neither as exciting as Spolsky, nor as pathetic as Al Bundy (though I'm sure the latter is up for debate.)

This story is about something that happened to me when I was younger that made me who I am today.

I started programming quite late by geek standards. I was either 11 or 14 depending on whether you count batch files as programming. But I loved it. And when I got tired of programming, I'd program some more in FastTracker 2!

For a while, I had a job as a pharmacist's assistant. This particular pharmacy had the option for delivery of prescriptions and other general items. The deliveries were done in the evening and were done by two very friendly guys named Krish and Sam.

Part of my job here (besides preparing the methadone!) was to add up all the deliveries that these two guys did within some period of time and summarize for the boss. It used to take me hours in a busy month as it required use of some large Excel spreadsheet. So I resolved to solve that problem with my programming chops. I got the owner to agree to pay me hourly (wow, $10/hour!) and he told me not to spend "too much time" on it. So I guesstimated 40 hours until I would be able to deploy the application. He was fine with that.

I was mostly right but near the end of it, while I was testing with the users (me), it turned out some other data collection was necessary. After discussing with the boss, he said it was important but not to spend "too much time" on it. By then, I had come to realize that "too much time" means "don't charge me more than I want to pay you." So I added it quickly, and it was done.

I deployed the application in December of that year to rave reviews from the boss. I'm not sure if the other employees cared that much but I know that it made my life easier and the count of deliveries more accurate so I was happy.

Due to other responsibilities (I think it was school?), I had to reduce my hours at the pharmacy. That was around June. I had trained the other people to use the application and it was becoming quite necessary for them to use if they wanted to retain their sanity.

Fast-forward to November. I get a call. "Sohail, it's not working anymore." "Crap", I thought. What did I miss? I went into the pharmacy and fudged around in the flat files to see what could be wrong.

It turns out that one of the data files was being named incorrectly. Can you guess which data file? Yes, it was the one I had added last just before I completed the project.

I think I spent about 10 unpaid hours debugging the problem at home while studying for exams/finishing projects. It came down to this (in C++, I don't really recall the language):


// data1.cpp
const char * months[] = {"Jan","Feb","Mar","Apr","May","Jun","Jul","Aug","Sep","Oct","Nov","Dec"};


// data2.cpp
const char * months[] = {"Jan","Feb","Mar","Apr","May","Jun","Jul","Aug","Sep","Oct","Nov ","Dec"};

Can you see the problem? In the second instance, I added a space after "Nov". So while data2.cpp was saving "Data2Nov Year.file", data1.cpp was saving "Data1NovYear.file" and the code that summarized the data was trying to open "Data<N>NovYear.file".

Oops.

I fixed this bug and apologized to the owner. I explained why it happened and that I fixed the code duplication. The boss said that he thought I should get an award for this software (a big ego boost!) and wondered if he could sell it to other people. Apparently he had been raving about it to his other pharmacist buddies. I don't remember what I said to him about that (something about me not having enough time to modify it to be generic enough as Sam and Krish were hardcoded!) but I really learned something that month.

Fortunately, in this case, things turned out alright. So I swore to avoid similar problems in the future in case future clients were not as forgiving. How did I do this? Quite simply:

  • Thorough testing

  • No copy-and-waste


My business cards have always had some variation of this. The current incarnation has something like "Do it once, do it right." Corny, but conveys my philosophy quite well. I will work very hard to reduce code duplication now. I try my best to write a test for each function/class that I write. Yes it is hard work. Yes it takes time. Quite frankly, software is hard. But in the end, I get very few calls saying "Sohail, it doesn't work anymore."

And no, you are not allowed to put a comment on this post saying "Sohail, it doesn't work anymore" :-)

Tuesday, 24 June 2008

Automatically rescale a Gtk::Image (and preserve aspect ratio)

Don't really have enough time to explain it all, but hopefully the comments help. Here are a couple of screenshots:





The image used in the examples above is here.

And the code:


/*
Problem:

Using GTK, display an image in a window and rescale when the window
resizes preserving the aspect ratio.

Analysis:

Gtk provides Gtk::Image which works on a Gdk::Pixbuf (pixel buffer)
which allows rescaling but without preserving aspect ratio.

Summary of solution:

Use a Gtk::Image embedded in a Gtk::AspectFrame and resize image
using dimensions determined by Gtk::AspectFrame. The AspectFrame
knows how to maintain a ratio so it does the messy work for us.

*/


#include <gtkmm.h>
#include <iostream>

/// This Widget only scales the image but does not preserve
/// aspect ratios.
struct ScalingImage : public Gtk::Image
{
explicit
ScalingImage(Glib::RefPtr<Gdk::Pixbuf> pixbuf,
Gdk::InterpType interp = Gdk::INTERP_BILINEAR):
Gtk::Image(pixbuf),
m_original(pixbuf),
m_interp(interp),
m_sized(false)
{
}

protected:
virtual
void
on_size_allocate(Gtk::Allocation & r)
{
// This event is fired on all Widgets when their rectangle gets
// resized. So we rescale our image. But rescaling our image fires
// another resize event, and another, and another... You see the
// problem!
//
// The most straightforward solution is to not rescale the image
// the second time the event is fired. Hence the m_size toggle.
//
// Ideally I'd not like to do this but this was the best way I
// found!
if(!m_sized)
{
Glib::RefPtr<Gdk::Pixbuf> scaled =
m_original->scale_simple(r.get_width(),
r.get_height(),
m_interp);
// Reset the image to be the new scaled image which
// will cause the second size-allocate signal
Gtk::Image::set(scaled);

// Make sure we don't treat it as a rescale request
// next time
m_sized = true;
}
else
{
// Reaction to set above. Call the base class's on_size_allocate
// function. Shouldn't I have to call this above too? Maybe it
// isn't necessary since it gets called anyway.
Gtk::Image::on_size_allocate(r);

// Treat next resize event as a rescale
m_sized = false;
}
}

private:
Glib::RefPtr<Gdk::Pixbuf> m_original;
Gdk::InterpType m_interp;
bool m_sized;
};

/// This widget automatically scales the image but preserves the
/// aspect ratio of the original image. This is accomplished via use
/// of the AspectFrame class
///
/// Ideally would like this to be a Gtk::Image subclass.
struct AspectPreservingScalingImage : public Gtk::AspectFrame
{
explicit
AspectPreservingScalingImage(Glib::RefPtr<Gdk::Pixbuf> const & pixbuf,
Gdk::InterpType interp = Gdk::INTERP_BILINEAR):
m_img(pixbuf,interp)
{
set(Gtk::ALIGN_CENTER, Gtk::ALIGN_CENTER,
// Aspect ratio of frame
pixbuf->get_width()/float(pixbuf->get_height()));
// This makes it appear as if there is no frame
set_shadow_type(Gtk::SHADOW_NONE);
// This allows a minimum size
set_size_request(pixbuf->get_width()/2,pixbuf->get_height()/2);
// Finally, add the image to be managed
add(m_img);
}
private:
ScalingImage m_img;
};

int main(int argc, char* argv[])
{
if(argc != 2)
{
std::cout << "Usage: " << argv[0] << " <image-file>" << std::endl;
return 1;
}

Gtk::Main kit(argc,argv);

Gtk::Window win;
Gtk::VBox box;
win.add(box);

Gtk::LinkButton lb("http://www.utahskies.org/image_library/deepsky/constellations/orionwa.jpg");
AspectPreservingScalingImage apsi(Gdk::Pixbuf::create_from_file(argv[1]));

box.pack_start(lb,Gtk::PACK_SHRINK);
box.pack_start(apsi);

struct launch_browser
{
static
void
doit(Glib::ustring const & addr)
{
std::string command("firefox ");
command += addr.c_str();
std::system(command.c_str());
}
};

lb.signal_clicked().connect
(
sigc::bind(
sigc::ptr_fun(launch_browser::doit),
lb.get_uri()
)
);


win.show_all();

Gtk::Main::run(win);
}

Thursday, 5 June 2008

C++ Standard GUI


#include <gtkmm.h>

namespace std
{
namespace gui = Gtk;
}


I love gtkmm.

Monday, 2 June 2008

Linux printing: Brother MFC-465CN

I've been using Linux for a long time and as has anyone who has done so, printers, scanners and basically anything else that does not have a USB storage interface is a risky proposition.

Not so with the Brother MFC-465CN.

My setup process (on Ubuntu Hardy/8.04):


  • Set up hardware as suggested in manual

  • Skip software installation and connect printer to network via ethernet

  • sudo apt-get install brother-lpr-drivers-extra brother-cups-wrapper-extra

  • System->Administration->Printing->Test page

  • sudo apt-get install xsane sane

  • Get brsane2 driver (read the instructions!)

  • Launch xsane and test scanner


I don't even want to remember what it used to be LIKE before. Brother has GPLed their printer driver which is probably part of the reason it was so easy to set up. Great job Brother and many thanks to the Open Source fairy.

Thursday, 29 May 2008

YUI and Parenscript

Parenscript is a nice little mini-language that lets you compile Lisp to JavaScript. Sort of like Google does with GWT and the language to end all languages: Java.

Parenscript code looks like this:


(ps:ps
(defun say-hi ()
(alert "Hi!"))
(say-hi))


The macro ps:ps compiles the Lisp code to a JS string which looks like this:

function sayHi() {
alert('Hi!');
};
sayHi();

Notice the conversion from say-hi to sayHi. I love the Common Lisp naming conventions. They make so much sense and I miss them everywhere else. Obviously the writers of Parenscript agreed with me so they convert each symbol they see into a JS symbol which keeps things looking mostly neat.

The major conventions are (I don't know that there are more):

  • *foo* becomes FOO

  • foo-bar becomes fooBar

  • foo.*bar becomes foo.Bar



Anyhoo, YUI is a largish but nice JavaScript library that one can use for creating quite interactive web applications.

The problem is that YUI has names like YAHOO.util.Event.onDOMReady which is a handful to type even in JavaScript. If you try converting this symbol via ParenScript you will be in for a surprise (try it!) Anyway, there are a couple of ways to write this in ParenScript and keep the casing intact without losing your hair:


  • *yahoo*.util.*event.on-d-o-m-ready

  • |:YAHOO.util.:Event.:onDOMReady|



This works because the bars prevent the reader from downcasing or upcasing the symbol and ParenScript promises that it will leave symbols that begin with a colon alone.

Even so, this is still a PITA to type. What I do is I have a wrapper script that gets compiled once with short names for all of these. So for the above example, using Hunchentoot:

(defin-easy-handler (my-yui :uri "/ps/my-yui.ps") ()
(ps:ps
(defun yui-on-dom-ready (fn)
(|:YAHOO.util.:Event.:onDOMReady| fn))
(defvar yui-some-other-funky-name |:YAHOO.util.:FunkyChickens|)))


Yay.

Monday, 26 May 2008

30 day product challenge: update

The idea: Develop or prototype a commercial application in 30 days. Then decide what to do with it.

Initially I had thought this Wiki would be the central point for a uISV community but I should have known better. It appears that people are a lot more comfortable with using their own blogs to update progress.

With that in mind, Patrick McKenzie (a successful uISV owner, whom I greatly respect) has decided to jump on the bandwagon in his own way

I am joining up with him and will be starting in mid-June while I complete some other work.

I will be aggregating all the 30 day participants I can find at this feed

Best of luck and happy coding!

Friday, 23 May 2008

Very cool interview with a compiler developer

Its sad that this is interesting for me but when you are waiting for a large C++ project to compile (gtkmm), you listen to these things in the background:

The route to C++ code optimization

The C++ compiler is the best MS product that exists. Well the only good product... Or maybe I just have a soft spot for C++ compiler developers. Either way, this man (also) uses Emacs (the illegal version) so maybe there is more to the story.

Channel 9 people: You need to have a whole series on the C++ compiler from top to bottom.

Wednesday, 21 May 2008

Public service announcement: urxvt (rxvt-unicode)

One good habit I have picked up over the years is to have a separate environment for each project that I am working on. For example, I might want to work on project foo but to do that, I need /opt/foo/bin to be in the path. So what I would do in the bad old days is:


$ cd /path/to/project/foo
$ export PATH=`pwd`/bin:$PATH
$ export OTHER_VARIABLE=OTHER_VALUE


Obviously, this can get annoying fast so I would put it in a script and then do the following:

$ cd /path/to/project/foo
$ source setup_environment.sh


The problem with the above is that it involves two more manual steps. Grr... Fortunately, most terminal emulators have the option to execute a script when a certain "profile" of the shell starts.

I have been using gnome-terminal as a terminal emulator for a long time and it has an option to execute a shell script as described above. Unfortunately, when setting the shell script to run in Ubuntu 8.04 through some pointy-clicky magic, it crashes *all* my windows. I don't even know how its managing to do that. On top of that, setting the script to run is quite unintuitive to me. So with these two things together, I finally got fed up and installed rxvt-unicode.

This program is blazing fast. The only thing I'm missing is tabs but I hear that is possible as well. urxvt uses the Xdefaults file which you can use to set system-wide or application specific options for X programs. The relevant settings for me look like this:

URxvt.background: black
URxvt.foreground: green
URxvt.scrollBar: false
URxvt.font: xft:Bitstream Vera Sans Mono:pixelsize=13

There is a daemon that you can run in the background (call like urxvtd -q -o -f) which you would run when you log in. This daemon starts all urxvt terminals within the same process which can save memory but has the disadvantage of taking all your terminals down if one crashes (which I presume doesn't happen too often!) To request a new terminal window, use the program urxvtc.

Back to my original point (I have one?), given that your shell starting script looks like this:

#!/bin/bash
# Usage: start_foo_shell.sh

cd /path/to/project/foo/
pushd $1
source setup_environment.sh
bash


You can set up shortcuts to start in that shell by using the command:

# Start a shell for feature_foo
urxvtc -e bash /home/sohail/src/foo/start_shell.sh branches/feature_foo
# start a shell for trunk
urxvtc -e bash /home/sohail/src/foo/start_shell.sh trunk

This ends the public service announcement. You will be returned to your regularly scheduled feed reeding.

Monday, 19 May 2008

Boost.TR1 package

For those of you interested in using only the TR1 portion of the Boost C++ libraries, you can download a package I created for this purpose. It is about as lightly tested as things can get. I just used bcp to extract everything and copied the minimal amount of stuff needed to run the TR1 tests.

For those of you who don't know what TR1 is, see the Wikipedia page on TR1.

Anyway, you can download the package here (OS indicates line endings):



Steps to running TR1 tests:

  • Download one of above and extract

  • cd boost_1_35_tr1

  • cd tools/jam/src

  • ./build.sh or build.bat

  • export PATH=`pwd`/bin.*:$PATH or set PATH=%CD%\bin.ntx86

  • cd ../../../

  • cd libs/tr1/test

  • bjam


The above will only run the TR1 tests. I just noticed that the regex tests are missing. If anyone cares, leave a comment and I'll see what I can do.

To use in your own code, read the Boost.TR1 documentation.

Also, this is in no way official, so please don't bug the Boost guys if you have a problem. Bug me instead :-)

Sunday, 18 May 2008

30 day product challenge: taking registrations!

I originally made a post here outlining that I think a 30 day product challenge for developers would be an interesting experiment. Others seemed to echo this sentiment.

The intent is for this to put you front and center so that if you fail, you look really bad ;-) Hey, it works for me!

So, in the spirit of something every two days, I have started a wiki here:

http://30dayproductchallenge.com/30day/FrontPage

You may register for the challenge simply by creating an account (go to the bottom of the page for the link!) Please be sure to subscribe to the FrontPage after you register so that you can be notified when the challenge starts.

Thanks to Phillip Flores for motivating me to get things set up.

It is quite probable that the site will buckle under the load of more than 5 people simultaneously accessing it ;-) If this happens to you, please keep trying!

Wednesday, 14 May 2008

Lisp compiler for .NET

I've always wanted to write a compiler for Lisp and .NET. And tonight I did both! Ok I lie. It only handles strings right now but my plan is to add some type inference soon and see how things go. This would not be any sort of faithful lisp implementation as it would be a bearable skin on .NET. To me, that would be worth the work.

Input:


(import "mscorlib")
(assembly "hello")

(defun print-it ()
(print "Hello! How are you?")
(print "I am fine!"))

(defun main ()
(.d entrypoint)
(print-it))


MSIL output:


.assembly extern mscorlib {}
.assembly hello {}
.method static public void PRINT_IT () cil managed
{
.maxstack 1
ldstr "Hello! How are you?"
call void [mscorlib]System.Console::WriteLine(class System.String)
ldstr "I am fine!"
call void [mscorlib]System.Console::WriteLine(class System.String)
ret
}
.method static public void MAIN () cil managed
{
.maxstack 1
.entrypoint
call void PRINT_IT
ret
}


Program output:

sohail@dev-ubuntu-wks:/tmp$ mono foo.exe
Hello! How are you?
I am fine!


Now I can go to sleep!

Wednesday, 7 May 2008

BoostCon '08

I gave an all day workshop on Boost Serialization yesterday. I was a bit nervous as I hadn't really tested the material elsewhere and I think the BoostCon organizers were also nervous as they had never seen me work my magic. Fortunately, I had a few gems in there and my section on forward compatible file formats with the serialization library was my favourite part. For my money, I think people got the most out of it because there were a few practical activities in the session (though we didn't have time for all of them.)

Anyway, at the end of it, one of the attendees said that it was likely going to be the highlight of their week which is a great review for me! For what its worth, I don't think that there is any way the session could have gone as well as it did without the amount of dedication the attendees had so I am very grateful to them. So many of them stayed throughout the breaks to work on their activities! That was awesome of them and made me want to make sure that they "got it" even more!

Now I am heading to the keynote by Bjarne (who seems to be walking around in a hat to remain anonymous?) That will be a highlight for me, but really, giving that presentation with those attendees is the ultimate highlight.

PS: If you ever want to live on the wild side, try and get people to compile Boost in your presentation. That was "fun" but paid off once people had things working.

PPS: In my opinion, the *first* session for everyone in BoostCon should be geared towards getting people compiling and running Boost.

Thursday, 24 April 2008

Mandelbrot generator in Common Lisp

A couple of months back I had a bad flu and couldn't really do much so of course I wrote a Mandelbrot generator. This took me between .5 hr to 1 hr so go easy on me. Just dug it up today b/c of a discussion I was having so thought I'd post it here. It uses cl-opengl.


(in-package #:cl-glut-examples)

(progn (setq glut::*argcp* (cffi:null-pointer) glut::*argv* (cffi:null-pointer)))(glut:init)

(defclass mandelbrot-window (glut:window)
((image :initform nil
:accessor image)
(width :initarg :width
:accessor width)
(height :initarg :height
:accessor height)
(need-recalc :initform t
:accessor need-recalc)
(real-extents :initarg :real-extents
:accessor real-extents)
(imag-extents :initarg :imag-extents
:accessor imag-extents))
(:default-initargs :pos-x 100 :pos-y 100
:mode '(:single :rgb) :title "mandelbrot.lisp"))

(defmethod glut:display-window :before ((w mandelbrot-window))
;; Select clearing color.
(gl:clear-color 0 0 0 0)
;; Initialize viewing values.
(gl:matrix-mode :projection)
(gl:load-identity)
(gl:ortho 0 1 0 1 -1 1))

(defmethod glut:display ((w mandelbrot-window))
(gl:clear :color-buffer)
(gl:raster-pos 0 0)
(when (need-recalc w)
(format t "Need to recalc...~%")
(format t "Real: ~A Imag: ~A~%" (real-extents w) (imag-extents w))
(setf (image w) (calculate-mandelbrot-image (width w) (height w) :real (real-extents w) :imag (imag-extents w) :max-iterations 256))
(setf (need-recalc w) nil)
(format t "Done...~%"))
(gl:draw-pixels (image-width (image w)) (image-height (image w)) :rgb :unsigned-byte (image-1darray (image w)))
(gl:flush))

(defun rb-mandelbrot (width height &key (max-iterations 1000) (real '(-1.5 1.5)) (imag '(-1.5 1.5)))
(glut:display-window
(make-instance 'mandelbrot-window
:width width :height height
:real-extents real :imag-extents imag)))
; :image (calculate-mandelbrot-image width height :real real :imag imag :max-iterations max-iterations))))

(defun midpoint (lst)
(/ (+ (first lst) (second lst))
2))

(defmethod glut:keyboard ((w mandelbrot-window) key x y)
(with-slots (real-extents imag-extents) w
(let ((imag-delta (* 4 (/ (- (second imag-extents) (first imag-extents)) (height w))))
(real-delta (* 4 (/ (- (second real-extents) (first real-extents)) (width w))))
(imag-midpoint (midpoint imag-extents))
(real-midpoint (midpoint real-extents)))
(case key
(#\a
(incf (first imag-extents) imag-delta)
(incf (second imag-extents) imag-delta))
(#\A
(incf (first imag-extents) (* 10 imag-delta))
(incf (second imag-extents) (* 10 imag-delta)))
(#\d
(incf (first imag-extents) (- imag-delta))
(incf (second imag-extents) (- imag-delta)))
(#\D
(incf (first imag-extents) (* 10 (- imag-delta)))
(incf (second imag-extents) (* 10 (- imag-delta))))
(#\w
(incf (first real-extents) (- real-delta))
(incf (second real-extents) (- real-delta)))
(#\s
(incf (first real-extents) real-delta)
(incf (second real-extents) real-delta))
(#\S
(let ((imag-midpoint-distance (- imag-midpoint (first imag-extents)))
(real-midpoint-distance (- real-midpoint (first real-extents))))
(setf (first imag-extents) (- (first imag-extents) imag-midpoint-distance))
(setf (second imag-extents) (+ (second imag-extents) imag-midpoint-distance))
(setf (first real-extents) (- (first real-extents) real-midpoint-distance))
(setf (second real-extents) (+ (second real-extents) real-midpoint-distance))))
(#\W
(setf (first imag-extents) (midpoint (list (first imag-extents)
imag-midpoint)))
(setf (second imag-extents) (midpoint (list imag-midpoint
(second imag-extents))))
(setf (first real-extents) (midpoint (list (first real-extents)
real-midpoint)))
(setf (second real-extents) (midpoint (list real-midpoint
(second real-extents))))))
(case key
((#\w #\s #\a #\d #\W #\D #\A #\S)
(setf (need-recalc w) t)
(setf (real-extents w) real-extents)
(setf (imag-extents w) imag-extents)))))
(glut:post-redisplay))

(defun make-image (w h)
(let* ((underlying-array (make-array (* w h 3) :element-type '(unsigned-byte 8)
:initial-element 0))
(displaced-array (make-array (list w h 3) :element-type '(unsigned-byte 8)
:displaced-to underlying-array)))

(list w h underlying-array displaced-array)))

(defun image-width (image)
(first image))

(defun image-height (image)
(second image))

(defun image-1darray (image)
(third image))

(defun image-3darray (image)
(fourth image))

(defun set-colour (image x y iterations-taken max-iterations)
(let (depth)
(if (= iterations-taken max-iterations)
(setf depth 0)
(progn
(setf depth (truncate (* 255 (/ iterations-taken 1000))))))
(let ((r (min 180 (* 20 depth)))
(g (min 180 (* 5 depth)))
(b (min 180 (* 1 depth)))
(data (image-3darray image)))
(setf (aref data x y 0) r)
(setf (aref data x y 1) g)
(setf (aref data x y 2) b))))

(defun within-radius (z)
(let ((r (realpart z))
(i (imagpart z)))
(<= (+ (* r r) (* i i))
4)))

(defun calculate-mandelbrot-image (width height &key (max-iterations 1000)
(real '(-1.5 1.5))
(imag '(-1.5 1.5)))
(let* ((image (make-image width height))
(low-real (first real))
(high-real (second real))
(low-imag (first imag))
(high-imag (second imag))
(real-length (- high-real low-real))
(imag-length (- high-imag low-imag))
(real-by (/ real-length width))
(imag-by (/ imag-length height)))
(flet ((do-it (start-x end-x start-cr)
(loop
for x from start-x to end-x
for cr from start-cr by real-by
do
(loop
for y below height
for ci from low-imag by imag-by
do
(let* ((c (complex cr ci))
(iterations-taken
(loop
for z = c then (+ (* z z) c)
for iteration from 0 below max-iterations
while (within-radius z)
count iteration)))
(set-colour image (truncate x) (truncate y) iterations-taken max-iterations))))))
(let* ((end-x (truncate (/ width 2)))
(threads (list (sb-thread:make-thread (lambda ()
(do-it 0 end-x low-real)))
(sb-thread:make-thread (lambda ()
(do-it (1+ end-x) (1- width) (+ (* end-x real-by) low-real)))))))
(loop for thread in threads
do (sb-thread:join-thread thread))))
image))

Monday, 21 April 2008

The secret to making money online

The secret to making money online.

I know I haven't posted on it much, but this guy says it all.

Sunday, 13 April 2008

Weblocks: Presentations

When Slava set out to write Weblocks, he claims to have a goal to never write HTML again. The way he proposed to accomplish this was through the use of presentations. You can see an example of his UI-DSL. I don't claim to understand the UI-DSL but I do understand how presentations work and I like them!

I set out to add support for money for something I am working on. So first, I wanted to be able to validate/display a number. Simple enough. First thing I did was define a new type so I can change it later into something that handles currencies properly:

(deftype money () 
'number)

There are basically two things you need to be able to do (depending on context): parse and display. Obviously, presentations are responsible for the display while parsers are responsible for ... parsing!
(defun parse-money (string)
(read-from-string string))

(defun format-money-display (amount)
;; Note the extra "$"
(format nil "$~4$" amount))

(defun format-money-input (amount)
(format nil "~d" amount))


The reason there are two format functions is that you need to format the value for display or for editing. The latter is named format-money-input.

The protocol calls the generic functions weblocks:typespec->view-field-presentation and weblocks:typespec->form-view-field-parser depending on what the scaffold is rendering. The purpose of these functions is to take a typespec and create the corresponding presentation or parser.

You can have form scaffolds in addition to the regular scaffold. You would use the form scaffold when the method of display would be different. For example, for a boolean input you would use a checkbox for form input, whereas when simply rendering for display, you could use "true" or "false" or "yes" or "no".

In this case, I don't need anything this drastic, so I defined the functions as follows:
(defmethod weblocks:typespec->view-field-presentation (scaffold
(typespec (eql 'money)) args)
(values t (make-instance 'money-presentation)))

(defmethod weblocks:typespec->form-view-field-parser ((scaffold form-scaffold)
(typespec (eql 'money)) args)
(values t (make-instance 'money-parser)))


In this case, the money-presentation and money-parser types are just tags to make the generic function machinery work. The types are defined as follows:

(defclass money-presentation (weblocks:text-presentation weblocks:input-presentation)
())

(defclass money-parser (parser)
()
(:default-initargs :error-message "a money value (for example, 100.51 or 4.52)")
(:documentation "A parser designed to parse strings into
money."
))


Using :default-initargs you can define an error message that will be seen when parsing fails (for whatever reason).

When Weblocks has your parser and presentation types, it then calls the following generic functions depending on the rendering context:

  • weblocks:print-view-field-value

  • weblocks:parse-view-field-value

  • weblocks:render-view-field-value


You won't believe it, but there is barely any HTML involved. I love it!

(defmethod weblocks:print-view-field-value (value
(presentation money-presentation)
field view widget obj &rest args)
(declare (ignore args))
(format-money-display value))

(defmethod weblocks:parse-view-field-value ((parser money-parser)
value obj
(view form-view) (field form-view-field)
&rest args)
(declare (ignore args))
(declare (optimize safety))
(ignore-errors
(let* ((presentp (text-input-present-p value))
(money-value (when presentp
(parse-money value))))
(values t presentp money-value))))

(defmethod weblocks:render-view-field-value (value (presentation money-presentation)
(field form-view-field)
(view form-view)
widget obj &rest args)
(declare (ignore args))
(render-text-input (view-field-slot-name field)
(format-money-input value)
:maxlength 50))


An example of usage would be:
(defclass transaction ()
((id :accessor transaction-id)
(date :initform (get-universal-time)
:accessor transaction-date
:initarg :date)
(amount :initform *default-money-value*
:accessor transaction-amount
:initarg :amount
:type money)))


And that is all you need to add your own reusable type to Weblocks. Not bad. Haven't added a date type yet, but will soon.

Thursday, 27 March 2008

BoostCon '08

I figure it's about time to announce that I will be giving a hands-on tutorial regarding the Boost.Serialization library this year at BoostCon.

I went last year as a spectator and thoroughly enjoyed myself (I know, sad!) This year has a lot of new and interesting sessions. The keynote speaker is Bjarne Stroustrup. How cool is that?!

Anyway, If you sign up before April 1st, you get the early bird special. If you are planning to go, might as well sign up and get the savings. Not to mention the beautiful surroundings (Aspen!) It is gorgeous. This time I am taking my camera.

If you are coming, please leave a note so we can meet!

Monday, 24 March 2008

A lame implementation of dynamic scoping in C++

This is very lame. I have nothing to say for myself except I'm very sorry.

#include <vector>
#include <iostream>
#include <boost/thread/tss.hpp>

#include <boost/thread/thread.hpp>
#include <boost/bind.hpp>
#include <boost/thread/barrier.hpp>

/**
Dynamic scoping is a form of scoping where the bindings associated
with variables are determined at runtime.

C++ has lexical scoping which is a form of scoping where the
bindings associated with are known at compile-time.

This piece of code is a half-assed implementation of thread-safe
dynamic variables in C++. See the main function in this file for a
"test" of the thread-safety

If I cared enough, I would make the variables get the global
bindings when new threads were created but as of now, they only get
the default value.

Alas, I don't care enough.

Usage:

// at global scope
dynamic_variable<int> dv_foo(5); // default value
dynamic_variable<double> dv_bar; // defaults to 0

void print()
{
std::cout << "FOO: " << dv_foo() << " BAR: " << dv_bar() << std::endl;
}

int main(int argc, char * argv[])
{
{
dynamic_bind<int> foo(dv_foo,10);
dynamic_bind<double> bar(dv_bar,10);
print();
}
{
dynamic_bind<int> foo(dv_foo,20);
dynamic_bind<double> bar(dv_bar,20);
print();
}
print();
}

output:
FOO: 10 BAR: 10
FOO: 20 BAR: 20
FOO: 5 BAR: 0
**/


template
<typename T> struct dynamic_bind;

template
<typename T>

struct
dynamic_variable
{

dynamic_variable(T const & t)
:
initial_value_(t)
{


init();
}

dynamic_variable()
:
initial_value_(T())
{

init();
}


T & operator()()
{

init();
return
bindings_.get()->back();
}


private
:
friend class
dynamic_bind<T>;

void
init()
{


if
(!bindings_.get())
{

bindings_.reset(new std::vector<T>());

bind(initial_value_);
}
}


void
bind(T const & t)
{


init();
bindings_.get()->push_back(t);
}

void
unbind()
{


init();
bindings_.get()->pop_back();
}


boost::thread_specific_ptr<std::vector<T> > bindings_;

T initial_value_;
};


template
<typename T>
struct
dynamic_bind
{

dynamic_bind(dynamic_variable<T> & var,

T const & t):
var_(var)
{

var_.bind(t);
}
~
dynamic_bind()
{


var_.unbind();
}

private
:
dynamic_variable<T> & var_;
};



dynamic_variable<int> dv_thread_id(0);
dynamic_variable<std::string> dv_x;

dynamic_variable<std::string> dv_y("global y");

void
reader()
{

std::cout << "In thread " << dv_thread_id()
<<
", x = " << dv_x()
<<
", y = " << dv_y()
<<
std::endl;
}


void
setter()
{

boost::barrier wait_for(2);

dynamic_bind<std::string> x(dv_x,"setter::x1");
{


// yield to allow for interleaving of threads
boost::thread::yield();
dynamic_bind<std::string> x(dv_x,"setter::x2");

// first time this function is called, y has not
// been dynamically rebound, so y will have its
// default value
reader();
}

dynamic_bind<std::string> y(dv_y,"setter::y1");
{


boost::thread::yield();
reader();
}
}


void
threadfunc(int threadid)
{


dynamic_bind<int> thread_id(dv_thread_id,threadid);
setter();
}


int
main()
{


std::cout << "In main..." << std::endl;
boost::thread t1(boost::bind(threadfunc,1));

boost::thread t2(boost::bind(threadfunc,2));
t1.join();

t2.join();
}


Friday, 14 March 2008

Piracy surcharge? Wtf? Think of the kittens!

Totally off-topic.

Music industry proposes a piracy surcharge on ISPs.

You know, you'd think they would realize that being able to charge $5/month/user results in big $$$. Big enough so that you *don't* need to call it piracy anymore. I think everyone I know would gladly pay $X/month to have access to the world's music collection in high quality. But if you call it a piracy surcharge, I ain't paying. How about calling it a "music access surcharge" or something that is more indicative of its purpose.

Mmmmkay, lets do the math:


  1. Let's say $1.50 per broadband user per month (I'm sorry, $5 for you not providing any infrastructure is too much.)

  2. Let's say oh 33.9 million *subscribers* in America alone



Per-month, that works out to: 50.85 million dollars in America for doing nothing special except promising not to sue anyone.

Per-year, that works out to $610 million dollars in America alone, again, for doing nothing special. You could probably get double that if you get the European countries and Canada on board.

I'd *love* to own that business. No distribution costs? That is ridiculous! This is the new world order people.

Wednesday, 5 March 2008

Update: Serializing Boost.Tuple using Boost.Serialization

So as I often do while sleeping, I was thinking about the code I posted yesterday and I realized that it was way more complicated than it needed to be. I started out by writing:

template<typename Archive, typename T1>

void
serialize(Archive & ar,
boost::tuple<T1> & t,

const
unsigned int)
{

ar & t.get<0>();
}


But for some reason, when generalizing for more than one element, I starting using the head and tail members of the tuple. What I should have focused on was generating:
template<typename Archive, typename T1>

void
serialize(Archive & ar,
boost::tuple<T1> & t,

const
unsigned int)
{

ar & t.get<0>();
}


template
<typename Archive, typename T1, T2>
void
serialize(Archive & ar,

boost::tuple<T1,T2> & t,

const
unsigned int)
{


ar & t.get<0>();
ar & t.get<1>();
}
...

And in fact, this is quite easy to do using just the Boost Preprocessor. So simple that I smacked myself when I thought of it:

#include <boost/tuple/tuple.hpp>
#include <boost/serialization/nvp.hpp>
#include <boost/preprocessor/repetition.hpp>

namespace boost { namespace serialization {

#define GENERATE_ELEMENT_SERIALIZE(z,which,unused) \
ar & boost::serialization::make_nvp("element",t.get< which >());

#define GENERATE_TUPLE_SERIALIZE(z,nargs,unused) \
template< typename Archive, BOOST_PP_ENUM_PARAMS(nargs,typename T) > \
void serialize(Archive & ar, \
boost::tuple< BOOST_PP_ENUM_PARAMS(nargs,T) > & t, \
const unsigned int version) \
{ \
BOOST_PP_REPEAT_FROM_TO(0,nargs,GENERATE_ELEMENT_SERIALIZE,~); \
}


BOOST_PP_REPEAT_FROM_TO(1,6,GENERATE_TUPLE_SERIALIZE,~);

}}

Tuesday, 4 March 2008

Serializing Boost.Tuple using Boost.Serialization

Note: there is an update here. This post is entertainment only!

If you are using Boost Serialization, at some point, you will have to serialize instances of a class that you cannot alter. Perhaps it is from a third-party library like Boost. One example of a class you might want to serialize is Boost Tuple.

The way one normally serializes instances of a class using Boost Serialization is by inserting a template member function:

#include <boost/archive/text_oarchive.hpp>
#include <iostream>

struct
foo

{

foo(int bar):bar(bar){}
template
<typename Archive>

void
serialize(Archive & ar, const unsigned int)
{

ar & bar;
}


private
:
int
bar;
};


int
main()
{

foo f(5);;

boost::archive::text_oarchive ar(std::cout);
ar & f;
}



But if you want to serialize something like Boost Tuple, you can't really go and modify the code to add the serialize member. Technically, you could but it would be a maintenance headache. Instead, you would use non-intrusive serialization which looks something like this:
#include <boost/tuple/tuple.hpp>
#include <boost/archive/text_oarchive.hpp>
#include <boost/archive/text_iarchive.hpp>

#include <sstream>
#include <iostream>
#include <boost/tuple/tuple_comparison.hpp>
#include <boost/tuple/tuple_io.hpp>

namespace
boost { namespace serialization {

template
<typename Archive, typename T1>
void
serialize(Archive & ar, boost::tuple<T1> & t, const unsigned int)
{


ar & t.get<0>();
}
}}


template
<typename TupleType>
void
test(TupleType t)
{


std::ostringstream os;
{

boost::archive::text_oarchive ar(os);
ar & boost::serialization::make_nvp("tuple",t);
}


TupleType t2;
{

std::istringstream is(os.str());
boost::archive::text_iarchive ar(is);

ar & boost::serialization::make_nvp("tuple",t2);
}

std::cout << t << " == " << t2 << " ? " << std::boolalpha << (t==t2) << std::endl;
}


int
main()
{

test(boost::make_tuple(1.0));
}


The above code will work just fine for a tuple with only one element but will fail for any tuples with more than one element.

Under the hood, boost::tuple is a template class with a fixed number of template parameters all defaulting to boost::tuples::null_type. So one way to serialize all tuples with at most five elements is to write the above free serialize function five times over. That is really as much fun as it sounds. So lets make it a bit more fun!

In a language like Lisp, you would generate the code by writing a macro. However, C++ macros are nowhere near as easy-to-use as Lisp macros. But the good/insane guys at Boost have pushed the limits and have developed a set of preprocessor macros that add the control flow statements you have come to love like looping and if statements. Thats right, looping!

So to make it more fun, we must abuse the C++ preprocessor and have it generate the code we need.

There are multiple ways to iterate using Boost.Preprocessor. The one I will use (for no particular reason) is BOOST_PP_REPEAT_FROM_TO(first,last,macro,data). This macro causes the argument passed in for the macro argument to be evaluated last-first times.

The expected signature for this macro is macro(z,iteration,data). z is simply the next available repetition dimension and data is any other stuff you want to pass in. For the vast majority of stuff, I think you ignore these two parameters. iteration will take on the values first, first+1,...,last-1.

In our case, we would like to call BOOST_PP_REPEAT_FROM_TO(0,6,GENERATE_TUPLE_SERIALIZE,~). Even though we do not make any use of the last parameter, we must still provide a value. Now the rest is fairly mechanical. We need to write GENERATE_TUPLE_SERIALIZE. From the above, we can see the signature should look something like #define GENERATE_TUPLE_SERIALIZE(z,n,unused). So lets take a stab at it:
#define GENERATE_TUPLE_SERIALIZE(z,nargs,unused)                            \
template< typename Archive, BOOST_PP_ENUM_PARAMS(nargs,typename T) > \
void serialize(Archive & ar, \
boost::tuple< BOOST_PP_ENUM_PARAMS(nargs,T) > & t, \
const unsigned int version) \
{ \
ar & boost::serialization::make_nvp("head",t.head); \
ar & boost::serialization::make_nvp("tail",t.tail); \
}


Too easy :-)

Peeking under the hood (but still public API) for Boost Tuple, we see that each tuple is really a cons, albeit a compile-time one. This is done through inheritance. So for example, boost::tuple<int> is really boost::tuples::cons<int,boost::tuples::null_type>.

When we access t.tail in the above definition of serialize, we are accessing one of these cons structures. So if we compiled the code as is, that is:
#include <boost/tuple/tuple.hpp>
#include <boost/archive/text_oarchive.hpp>
#include <boost/archive/text_iarchive.hpp>

#include <boost/preprocessor/repetition.hpp>

#include <sstream>
#include <iostream>
#include <boost/tuple/tuple_comparison.hpp>
#include <boost/tuple/tuple_io.hpp>


namespace
boost { namespace serialization {

#define GENERATE_TUPLE_SERIALIZE(z,nargs,unused) \
template< typename Archive, BOOST_PP_ENUM_PARAMS(nargs,typename T) > \
void serialize(Archive & ar, \
boost::tuple< BOOST_PP_ENUM_PARAMS(nargs,T) > & t, \
const unsigned int version) \
{ \
ar & boost::serialization::make_nvp("head",t.head); \
ar & boost::serialization::make_nvp("tail",t.tail); \
}


BOOST_PP_REPEAT_FROM_TO(1,6,GENERATE_TUPLE_SERIALIZE,~);

}}


template
<typename TupleType>

void
test(TupleType t)
{

std::ostringstream os;
{

boost::archive::text_oarchive ar(os);

ar & boost::serialization::make_nvp("tuple",t);
}

TupleType t2;
{


std::istringstream is(os.str());
boost::archive::text_iarchive ar(is);

ar & boost::serialization::make_nvp("tuple",t2);
}

std::cout << t << " == " << t2 << " ? " << std::boolalpha << (t==t2) << std::endl;
}


int
main()
{

test(boost::make_tuple(1.0));
}



We would get a compile error:

serialize3.cpp:23: error: ‘class boost::tuples::tuple<double, boost::tuples::null_type, boost::tuples::null_type, boost::tuples::null_type, boost::tuples::null_type, boost::tuples::null_type, boost::tuples::null_type, boost::tuples::null_type, boost::tuples::null_type, boost::tuples::null_type>’ has no member named ‘tail’

The reason for this is that a one element tuple has no tail. There are multiple ways to solve this problem, but I think the neatest is to treat 1-element tuples (ignore 0-element tuples) as special cases and treat the rest as generic. The new serialization code looks like:

namespace boost { namespace serialization {

template
<typename Archive, typename T1>
void
serialize(Archive & ar,

boost::tuple<T1> & t,
const
unsigned int)
{

ar & boost::serialization::make_nvp("head",t.head);
}


#define GENERATE_TUPLE_SERIALIZE(z,nargs,unused) \
template< typename Archive, BOOST_PP_ENUM_PARAMS(nargs,typename T) > \
void serialize(Archive & ar, \
boost::tuple< BOOST_PP_ENUM_PARAMS(nargs,T) > & t, \
const unsigned int version) \
{ \
ar & boost::serialization::make_nvp("head",t.head); \
ar & boost::serialization::make_nvp("tail",t.tail); \
}


BOOST_PP_REPEAT_FROM_TO(2,6,GENERATE_TUPLE_SERIALIZE,~);

}}


If we compile this, we get yet another error:
/usr/include/boost/serialization/access.hpp:109: 

error:struct boost::tuples::cons<int, boost::tuples::cons<char,

boost::tuples::cons<std::basic_string<char, std::char_traits<char>,

std::allocator<char> >, boost::tuples::null_type> > >’ has no member
named ‘serialize’

Almost home free! The reason we get this error is that when we reference the tail member of the tuple, that is a cons object, not a tuple. So we need two more functions:
    template<typename Archive, typename T1>

void
serialize(Archive & ar,
boost::tuples::cons<T1,boost::tuples::null_type> & t,

const
unsigned int)
{

ar & boost::serialization::make_nvp("head",t.head);
}


template
<typename Archive, typename T1, typename T2>

void
serialize(Archive & ar,
boost::tuples::cons<T1,T2> & t,

const
unsigned int)
{

ar & boost::serialization::make_nvp("head",t.head);

ar & boost::serialization::make_nvp("tail",t.tail);
}



With that, the final code looks like:
#include <boost/tuple/tuple.hpp>
#include <boost/archive/text_oarchive.hpp>
#include <boost/archive/text_iarchive.hpp>

#include <boost/preprocessor/repetition.hpp>

#include <sstream>
#include <iostream>
#include <boost/tuple/tuple_comparison.hpp>
#include <boost/tuple/tuple_io.hpp>


namespace
boost { namespace serialization {

template
<typename Archive, typename T1>

void
serialize(Archive & ar,
boost::tuples::cons<T1,boost::tuples::null_type> & t,

const
unsigned int)
{

ar & boost::serialization::make_nvp("head",t.head);
}


template
<typename Archive, typename T1, typename T2>

void
serialize(Archive & ar,
boost::tuples::cons<T1,T2> & t,

const
unsigned int)
{

ar & boost::serialization::make_nvp("head",t.head);

ar & boost::serialization::make_nvp("tail",t.tail);
}


template
<typename Archive, typename T1>
void
serialize(Archive & ar,

boost::tuple<T1> & t,
const
unsigned int)
{

ar & boost::serialization::make_nvp("head",t.head);
}


#define GENERATE_TUPLE_SERIALIZE(z,nargs,unused) \
template< typename Archive, BOOST_PP_ENUM_PARAMS(nargs,typename T) > \
void serialize(Archive & ar, \
boost::tuple< BOOST_PP_ENUM_PARAMS(nargs,T) > & t, \
const unsigned int version) \
{ \
ar & boost::serialization::make_nvp("head",t.head); \
ar & boost::serialization::make_nvp("tail",t.tail); \
}


BOOST_PP_REPEAT_FROM_TO(2,6,GENERATE_TUPLE_SERIALIZE,~);

}}


template
<typename TupleType>

void
test(TupleType t)
{

std::ostringstream os;
{

boost::archive::text_oarchive ar(os);

ar & boost::serialization::make_nvp("tuple",t);
}

TupleType t2;
{


std::istringstream is(os.str());
boost::archive::text_iarchive ar(is);

ar & boost::serialization::make_nvp("tuple",t2);
}

std::cout << t << " == " << t2 << " ? " << std::boolalpha << (t==t2) << std::endl;
}


int
main()
{

test(boost::make_tuple(1.0));
test(boost::make_tuple(1.0,1));

test(boost::make_tuple(1.0,1,'c'));
test(boost::make_tuple(1.0,1,'c',std::string("FOO!")));
}



Compiling and running the code, we get the expected output:

/tmp$ g++ serialize6.cpp -lboost_serialization && ./a.out
(1) == (1) ? true
(1 1) == (1 1) ? true
(1 1 c) == (1 1 c) ? true
(1 1 c FOO!) == (1 1 c FOO!) ? true


Why did I do this? Someone asked and I was waiting for a process to finish running. That and I tend to do bursts of random C++ code when I am sick (which I am today!)