Welcome to the TnFOX homepage

by . Last updated . This page has been accessed 441 times since the 14th September 2011.

 

View this page in: flag English Any language:
flag Chinese flag French flag German flag Japanese flag Portuguese flag Russian flag Spanish

Translation to non-English languages provided by Google Language

You are connecting to the IPv4 version of this website from the IP address 107.20.104.110. You can try the IPv6-only version if you want.

 

 

I wasn't able to use TnFOX for around a year (the 2008/2009 academic year) as I have been too busy working, both at earning money and the full time Masters in Business Information Systems which sadly required me to do web programming rather than the real stuff. However, coming back to TnFOX after such a long time away has reminded me of why I like this portability toolkit more than any other out there and I thought I should write down the really neat things about TnFOX in my extremely biased opinion in a public sphere.

Why? Narcissm? Possibly, but equally I find myself searching around the web for various things from time to time and I find lots of people asking "how do I do X?" or "what is the best way of implementing Y?". The truth is that TnFOX has a nearly perfect implementation of many state-of-the-art features, yet no one knows nor can find the source code for study, so everyone just goes round and round and no one gets anywhere which isn't the reason I wrote TnFOX in the first place. I thought that maybe this page might provide the search engines with a better reference point with which to index the code, not least that they refuse to index most of the class documentation.

So, here is my list of the neatest things about TnFOX. Hope you find it useful!

1. TnFOX is my own code library

Yeah, yeah I know that is such an egotistical statement, however I taught myself computer programming back in the eighties. And back in the eighties, any programmer worth a damn had their own utility library full of all sorts of useful routines and natty functions which greatly eased the programming experience. My friend back in the old RISC-OS days Justin Fletcher had a particularly complete utility library written in BBC Basic, and I had a half arsed job which was the beginnings of Tn by around 1995.

One of the most fundamental theses of Tn is that computer power is for the most part wasted by crappy software design. And one of the things which I really hate about most portability toolkits is that they are dog slow and consume vast quantities of memory: Qt, wxWindows, .NET and definitely anything Java related all suffer from this curse. I developed Tornado, the predecessor of Tn, using Qt v3 and I was often stunned by two major shortcomings in particular:

  1. Qt v3.x was not quick nor frugal, in fact it was very much the opposite. I found myself rewriting Qt routines which were killing Tornado performance and submitting them as patches because they either used an O(N^4) algorithm or leaked memory. In fairness, Trolltech did a massive amount of long overdue profiling and tuning for Qt v4 and they also incorporated almost all of the new feature suggestions I made in my job interview with them.
  2. Like most older C++ libraries, error handling meant printing a message to stderr and carrying on. All older C++ libraries suffer severely from failing to think of "what happens if this breaks here?". That's passable in a GUI context where it's not the end of the world if some window resource fails, but it is extremely important for server apps, internet facing services and other mission critical code. Qt simply isn't suitable as a mission critical portability library, and indeed neither are most C++ libraries (Java and .NET are somewhat better in error handling, but not in other areas - see below).

After losing the Trolltech job interview, I no longer could afford the Qt license fee and so in 2003 I had the choice of one of two options: (i) use an existing library and submit patches to it or (ii) fork an existing library and make it my own. I did a lot of library reviewing and eventually settled on a fork of FOX, an almost known GUI toolkit, mainly because I particularly liked its orthogonal GUI class design. Jeroen its author writes in a minimalist way and that made his code very amenable to being patched in an upgradeable way. He is also very conservative and likes to wait an age before adopting a new feature, so unicode string support took forever but has the big advantage of being an especially frugal and orthogonal implementation. I do wish he'd hurry up with the theming support though :)

2. I think that TnFOX is a beautiful library

Anyway, one of the best things about using a third party library which has been almost entirely written by a few guru level skilled programmers is the sheer beauty of the thing: just how its structure, function and form meld together into a symphony of perfect execution. When you're writing code on top of it, its structure facilitates good programming style, it has helper functions in all the right areas, it does things in a way which really aids customisation and code reuse and it has outstanding debug facilities i.e. it is deliberately written and designed for when code goes wrong, not for when code goes right. I personally find that TnFOX does just this, and of course I would think that because I designed and wrote it. Nevertheless, it's extremely rare that I ever think "what muppet designed this API?" and I have applied TnFOX to all sorts of diverse solution scenarios.

Needless to say, there are not many libraries like this in the world - far too often we must write code at a much lower level than our capability because mediocre programmers must also understand what we contribute, and indeed in very large projects (such as the GNU Compiler Collection) there is a deliberate style policy of fairly low-level implementational programming (i.e. the code structure and algorithms may be extremely complex and require years of study, but their implementation is extremely mundane).

Some of the better libraries I have worked with are Boost.Python (whose only main failings are a very poor choice in class naming - I find the naming choice more than confusing - and it definitely suffers from a lack of documentation) and OpenSSL (which despite being a C-only library has all its bits in the right place in a really flexible structure - however once again its documentation sucks). The internal portability library used in Colton Fireworkz was gorgeous despite being C-only - though nothing else would be expected from my friend and old boss Stuart Swales.

One very mixed experience library is the STL - at times it's brilliant, at other times it's appalling. I know this is due to the STL being defined as the C++ language was being defined, and indeed the STL is much better than it might have been. Nevertheless, it feels like it was designed by committee which indeed it was, and it's a shame. Some of the Boost libraries are very good, others suffer from STLitus because they replicate the committee-designed STL style a bit too faithfully. I in particular did my own version of the metaprogramming library because Boost.MPL is too suffused with compiler-specific hacks and I therefore find using it an exercise in legacy issues. For example, TnFOX lets you assemble jump tables via metaprogramming which is currently beyond Boost.MPL:

	typedef Generic::TL::create<int, double, char>::value typelist;
	template<typename type> struct Impl {
		static void Do(int n) {
			fxmessage("This is %d=%s!\n", n, Generic::typeInfo<type>().name().text());
		}
	}
	Generic::TL::dynamicAt<typelist, Impl>(1, 1);

This assembles a jump table of typelist entries with each entry pointing to an instantiation of Impl<type>::Do() and the dynamicAt() simply vectors through that according to index. Why is that useful? It lets you choose the execution of type-dependent code at runtime which otherwise would need a switch statement. I put it to great use in my python bindings where it allows an arbitrary python function to pretend to be a C API sort function, something which is often claimed to be impossible without a hand-written wrapper on the Boost.Python mailing lists.

Anyway, ultimately one man's beauty is another man's ugliness, and my choice of TnFOX structure and design is extremely unpopular with just about everyone because no one else uses TnFOX for lots of varying reasons. Some greatly dislike my choice of threading model (I take the view that multithreading is good as that's the way I naturally write). Many have severe issue with the "extra" work they must do when allocating memory and such because TnFOX forces them to do so in the name of correctness should an error occur.

However, I personally think that the main reason that no one else uses it is: (a) few know it exists and (b) if you adopt a library then you constrain who else can work on it. Adopting Qt reduces the available programmer pool badly enough let alone adopting TnFOX which has been deliberately written to use techniques few C++ programmers have mastered. Using TnFOX guarantees few others being able to modify your code, and that's fatal in this modern day and age.

3. I really like how I've implemented error handling in TnFOX

I've already said this, but I really like the TnFOX pervasive error handling system. I really like it when TnFOX throws some weird and unexpected exception or assertion failure when I wasn't expecting it - TnFOX is particularly good at catching memory corruption, and moreover telling you where and when the memory corruption happened.

TnFOX is one of those very few libraries which correctly handles out of memory situations, or free disc space exhaustion, or even a pipe or network socket dying unexpectedly. Far too often I have had to deal with some stupidly written third party library which only correctly handles when things are going perfectly, and should some unexpected error occur then the library which do something especially retarded which makes the problem worse. For example, it will hide or mask the error, make it pop out somewhere it's not supposed to, corrupt memory randomly, cause a slow or insidious gradual failure or best of all, hang not just the process but also the GUI and screen. I have particularly bad memories of the National Instruments products in this regard, though Qt had similar problems, as does the Win32 API some of whose error handling design decisions are really retarded (see my list of stupid Win32 bugs). While the Win32 API is for the most part not bad, the Unix API is rarely ever retarded except perhaps in an efficiency context - POSIX, excepting the spawn of Satan that is the pthreads API, tends to be pretty well thought through.

I also really like how TnFOX can handle an error being thrown while handling the throw of another error - almost every third party library pukes in this situation, even Java and .NET libraries which should know better. Admittedly TnFOX will abort the process in the default case because end-user testing very rarely tests for cascading errors, so while it may have cleaned up properly we assume it may not. Also, the next C++ standard may eat this language capability so I'm not going to rely on it.

One of the main mechanisms used by TnFOX to handle errors so well is my transaction rollback implementation of which I am particularly proud. This lets you denote a sequence of "undo" steps as you perform some operation which might break half way through or simple because you then keep the deallocation code next to the allocation code which makes for much easier maintenance (and much less chance of resource leakage):

	FXERRHWIN(SEC_E_OK==AcquireCredentialsHandle(0, ntlmptr,
		SECPKG_CRED_OUTBOUND, NULL, &swai, NULL, NULL,
		&clientcred, &expiry));
	FXRBOp unclient=FXRBFunc(FreeCredentialsHandle, &clientcred);
	FXERRHWIN(SEC_E_OK==AcquireCredentialsHandle(0, ntlmptr,
		SECPKG_CRED_INBOUND, NULL, NULL, NULL, NULL,
		&servercred, &expiry));
	FXRBOp unserver=FXRBFunc(FreeCredentialsHandle, &servercred);

Lastly, and it's a silly thing, but TnFOX includes a stack backtrace in its exception object. Strangely, no other C++ toolkit seems to do that which is ridiculous as there is support available for it on most platforms, and it very usefully indicates from where an exception originated.

4. I really like how I've implemented the SQL interface in TnFOX

So many people think that "type reflection" for SQL binding is a Java or C# only feature, or one requiring a runtime executed language rather than statically compiled - it certainly is not, yet I know of no C++ library which implements SQL type reflection binding, except of course for TnFOX. This is a shame, because every SQL interface I have ever used in almost any language seems to have been deliberately designed to be as retarded and inconvenient as possible - even LINQ in .NET ain't plain gravy. I fear this has something to do with how programmers look at SQL - as something properly belonging to the 1970s and only to be touched because one is stuck with legacy systems.

It need not be like this - in fact, SQL can be incorporated very cleanly into C++ thanks to the magic of C++ metaprogramming of course which assembles the appropriate code to do the binding on the basis of inferred type associations. The TnFOX docs have all the gory details, but I'll repeat my code from the front of the TnFOX page:

TnFXSQLDBStatementRef st=db->prepare("INSERT INTO 'results'('testname', \
	'started', 'ended', 'platform', 'svnrev', 'returncode', \
	'output') VALUES(:testname, :started, :ended, :platform, :svnrev, :returncode, :output);");
st->bind(":testname", testname);
st->bind(":started", started);
st->bind(":ended", ended);
st->bind(":platform", myPlatformId);
st->bind(":svnrev", version.svnrev);
st->bind(":returncode", returncode);

// Write the output bz2 compressed to save (lots of) space
QBuffer buff;
{
	QBZip2Device bz2(&buff);
	bz2.open(IO_WriteOnly);
	FXStream s(&bz2);
	s << output;
}
st->bind(":output", buff.buffer());
st->execute();

This inserts a row containing a BZ2 compressed file as a BLOB object along with various other parameters such as time, return codes etc. Do you see any type specifiers e.g. TYPE_INTEGER, TYPE_BLOB? No, because the types are reflected through metaprogramming. One merely declares the appropriate template specialisations and TnFOX takes care of the rest. Note also that the above code is entirely exception aware - no leaks, failures to release locks or anything else bad can happen because once again, TnFOX properly throws exceptions and cleans itself up on error. Indeed, I even have a RAAI C++ class for SQL transactions which can be combined with C++ rollback transactions.

SQL in C++ can be easy and fun to work with. I find it a shame that other C++ libraries make it so much harder than it needs to be.

5.

blog comments powered by Disqus

Contact the webmaster: Niall Douglas @ webmaster2<at symbol>nedprod.com (Last updated: 08 July 2012 15:15:52 -0400)