Returning a type from a C++ function

In C++, you can easily “pass a type” to a function by using templates, as if it was an extra parameter for the function. For example you can write this:

tableLayout.addColumn<int>("id")

We add a column of type int and whose name is "id" to a table’s layout.

But “returning a type” is more complicated when the functions parameters are not known at compile-time. I would like to be able to write tableLayout.getColumnType("id") and have the function somehow return int.

Well, you can do it by using a callback function. This is not something new, but C++14′s generic lambdas make it far easier to do. First we need a small helping type:

template<typename T>
struct typetag { typedef T type; };

(note: it would be really great if this small type was added to the C++ standard at some point. We already have std::integral_constant, so why not this?)

And now we can write this:

tableLayout.getColumnType("id", [](auto tag) {
    typedef typename std::decay<decltype(tag)>::type::type
        type;    // that's our type!

    // "type" is "int" in this example
    static_assert(std::is_same<type, int>::value, "");
});

The tableLayout object must pass a typetag to the callback, and thanks to C++14′s generic lambdas you can easily retrieve it and obtain the associated type. Unfortunately we can’t write decltype(tag)::type because decltye(tag) is an lvalue reference, so we need to decay it first.

The implementation on tableLayout‘s side looks like this:

template<typename TCallback>
void getColumnType(const std::string& name, const TCallback& callback)
{
    // getColumnTypeString returns one of "int", "float", "string", etc.
    const auto columnTypeString = getColumnTypeString(name);

    if (columnType == "int")
        callback(typetag<int>{});
    else if (columnType == "float")
        callback(typetag<float>{});
    else if (columnType == "string")
        callback(typetag<std::string>{});
    ...
}

The list of possible types in the implementation, however, must be known in advance. As long as a C++ project is not considered as a whole by the compiler, there will be now way of having a truly generic implementation.

C++ standard library containers iterators quick cheatsheet

Here is a small cheat sheet for standard containers if you need to know when iterators and pointers stay valid and when you can no longer use them. Everything below is taken from the standard. I’m not aware of a compiler which would violate any of these rules.

The swap function of containers never invalidates pointers or iterators to elements. The clear function obviously invalidates everything, but may also invalidates the past-to-end iterator (ie. the iterator returned by the end function)

Note that a call to erase (or any function that deletes an element) obviously always invalidates the pointer and iterator to the deleted element.

std::vector, std::priority_queue

The reserve function invalidates all pointers and all iterators. An insertion invalidates all pointers and iterators only if size() >= capacity(). A deletion invalidates all pointers and references to all elements after the deleted element.

std::deque, std::stack, std::queue

An insertion always invalidates all iterators. An insertion in the middle of the container (ie. not at the front or the back) also invalidates all pointers.

A deletion of the last element invalidates the past-to-end iterator (ie. the iterator returned by the end function). A deletion in the middle of the container invalidates all pointers and all iterators, including the past-to-end iterator. A deletion of the first element of a container doesn’t invalidate anything (except the past-to-end iterator if the first element is also the last element).

std::array, std::list, std::forward_list

Pointers and iterators are never invalidated.

std::map, std::multimap, std::set, std::multiset

Pointers and iterators are never invalidated.

std::unordered_map, std::unordered_multimap, std::unordered_set, std::unordered_multiset

An insertion may sometimes invalidate all iterators, but pointers always remain valid.

C++11 nested exceptions

A little known but very exciting feature of C++11 are nested exceptions.

One of the common problems of C++ is that you can’t have a backtrace of the exceptions that you catch, which means that you won’t be able to know where and in which context they were thrown. For example, if you try to access a non-existing element in an unordered_map, you will catch an std::out_of_range exception with a message saying “invalid unordered_map<K, T> key”. But you will have no idea where in your program this was triggered.

There exist platform-specific functions that allow you to read the current stack, but there are two problems with this. The first one is that you need to compile your program in debug mode in order to read the actual functions’ names. Having a backtrace without any function name or anything that provides any information about where you are, is not very useful. The second problem is that you need to be at the top of the stack when you read it. This means that you need to read the stack at the location where you throw the exception, and not at the location where you catch it. This is impossible in many cases, especially for exceptions thrown by the standard library or in third-party libraries.

But C++11 provides a kind of backtrace-like feature, which are nested exceptions. The idea is simple: inside a function, whenever you detect a propagating exception, you intercept it, and you add a small message to it. When the exception arrives at its final destination, it will contain all the messages that were added in its path.

For example:

void foo() {
    throw std::runtime_error("Exception thrown by foo");
}

void bar() {
    try {
        foo();

    } catch(...) {
        std::throw_with_nested(std::logic_error("Exception detected inside bar"));
    }
}

int main() {
    try {
        bar();
        
    } catch(...) {
        print_backtrace(std::current_exception());
    }

    return 0;
}

In this code, main() calls bar(), which calls foo(). Then foo throws an exception of type std::runtime_error. This exception is intercepted by bar, which throws a nested exception instead. The throw_with_nested function will throw an object that is both a derivate of std::logic_error and of std::nested_exception. But what is interesting is that the nested_exception part of this object will store the std::runtime_error that was thrown by foo.

The exception is then caught by the main function. The main type of the exception caught by main is std::logic_error (that was thrown by bar). But the exception will also contain the std::runtime_error thrown by foo.

The main function then calls print_backtrace, whose role will be to unwind the nested exceptions in order to print the backtrace. If you are not familiar with std::current_exception, this function builds a copy of the exception currently being processed, and stores it into a smart pointer of type exception_ptr. The object that was thrown is kept forever, as long as the exception_ptr is alive. This is very useful, as it allows to pass any exception to a function, even an exception of unknown type.

Here is the print_backtrace function:

void print_backtrace(std::exception_ptr exception, std::string prefix = "") {
	try {
		std::rethrow_exception(exception);

	} catch(const std::exception& e) {
		std::cerr << prefix + "[exception] Exception of type \"" + std::string(typeid(e).name()) + "\"";
		std::cerr << prefix + "[exception] Message: " + std::string(e.what());

	} catch(const std::nested_exception&) {
		// handled below
	} catch(...) {
		std::cerr << prefix + "[exception] Exception of unknown type";
	}

	try {
		std::rethrow_exception(exception);

	} catch(const std::nested_exception& e) {
		print_backtrace(e.nested_ptr(), prefix + "    ");
	} catch (...) {
	}
}

First, we throw again the main exception (for reminder, it is a derivate of both std::logic_error and std::nested_exception in this example). If it is a derivate of std::exception (which should be the case for all exceptions), then we print a small message. The same happens if it is of an unknown type but nested_exception.

In the second part of the function, we rethrow the esame xception again, but this time we only process it if it is a nested_exception. If it is the case, we then extract the pointer to the nested exception (of type std::runtime_error in this example), and call ourself recursively. This way, even if there are multiple nesting levels, they will all be processed.

Conclusion

All you have to do if you want backtraces, is to add try-catch blocks around "strategic" functions and throw nested exceptions.

// before:
void foo() {
    // code
}

// after: 
void foo() {
    try {
        // code

    } catch(...) {
        std::throw_with_nested(std::exception("Exception thrown while doing foo stuff"));
    }
}

Not only will your final exception contain the latest message (which is usually the most useful), but also all the nested exceptions.

Unfortunately, Visual C++ 2012 and 2013 don't support nested exceptions yet. Here is a small code that adds the missing classes and functions.

#ifndef INCLUDE_EXCEPTION_HPP
#define INCLUDE_EXCEPTION_HPP

#include <exception>
#include <type_traits>

#ifdef _MSC_VER
#define EXCEPTION_HPP_NORETURN_MACRO __declspec(noreturn)
#else
#define EXCEPTION_HPP_NORETURN_MACRO [[noreturn]]
#endif

namespace std {
	class nested_exception {
	public:
		nested_exception() : nested(current_exception()) {}
		virtual ~nested_exception() {}

		EXCEPTION_HPP_NORETURN_MACRO
		void rethrow_nested() const 		{ std::rethrow_exception(nested); }

		exception_ptr nested_ptr() const		{ return nested; }


	private:
		exception_ptr nested;
	};

	template<typename T>
	EXCEPTION_HPP_NORETURN_MACRO
	void throw_with_nested(T&& t)
	{
		typedef remove_reference<T>::type RealT;

		struct ThrowWithNestedExcept : nested_exception, RealT
		{
			ThrowWithNestedExcept(T&& t) : RealT(std::forward<T>(t)) {}
		};

		if (is_base_of<nested_exception,RealT>::value)
			throw std::forward<T>(t);
		else
			throw ThrowWithNestedExcept(std::forward<T>(t));
	}

	template<typename E>
	void rethrow_if_nested(const E& e)
	{
		const auto ptr = dynamic_cast<const std::nested_exception*>(&e);
		if (ptr) ptr->rethrow_nested();
	}
}

#endif

Thanks for reading, and enjoy your backtraces!

unordered_map and const char*

Imagine that you want to write something like this:

std::unordered_map<std::string,int> list;
list["foo"] = 2;
list["bar"] = 5;

This works perfectly but is very inefficient. Every time you use the [] operator, a std::string is created, some memory is allocated on the heap, and the content of the C string is copied into this buffer.

Instead we would like to directly read from the const char*. You can’t just write std::unordered_map<const char*,int> because it would compare pointers instead of their content.

I quickly wrote this CStringUnorderedMap which does this:

#include <unordered_map>

namespace CStringUnorderedMapImpl_ {
	struct Hash {
		size_t operator()(const char* ptr) {
			size_t hash = 0;
			for (; *ptr; ++ptr)
				hash = (hash << 2) + *ptr;
			return hash;
		}
	};

	struct Comparaison {
		bool operator()(const char* str1, const char* str2) {
			for (; *str1 && *str1 == *str2; ++str1, ++str2) {}
			return (*str1 == *str2);
		}
	};
}

template<typename T>
using CStringUnorderedMap = std::unordered_map<const char*, T, CStringUnorderedMapImpl_::Hash, CStringUnorderedMapImpl_::Comparaison>;

#endif

If your compiler doesn't support the new using syntax (Visual C++ for example), you can replace the last line by this one:

template<typename T>
struct CStringUnorderedMap : public std::unordered_map<const char*, T, CStringUnorderedMapImpl_::Hash, CStringUnorderedMapImpl_::Comparaison> {};

DirectX-compliant smart pointer

If you are using the DirectX library, you have probably understood that you can’t use regular smart pointers with DirectX objects because of the way Microsoft designed their API.
Here is a small smart pointer class I created to handle this problem.

#include <stdexcept>

/**
 * Pointer to any IUnknown* object
 * T must be a derivate of IUnknown
 */
template<typename T>
struct DXObjectPointer {
	DXObjectPointer() : pointer(nullptr) {}
	DXObjectPointer(const DXObjectPointer& ptr) : pointer(ptr.pointer) { pointer->AddRef(); }
	DXObjectPointer(DXObjectPointer&& ptr) : pointer(nullptr) { std::swap(pointer, ptr.pointer); }
	~DXObjectPointer() { reset(); }

	DXObjectPointer&	operator=(const DXObjectPointer& ptr)		{ reset(); pointer = ptr.pointer; pointer->AddRef(); return *this; }
	DXObjectPointer&	operator=(DXObjectPointer&& ptr)		{ std::swap(pointer, ptr.pointer); return *this; }

	T**		operator&()				{ if (pointer) throw std::logic_error("Trying to assign into a non-empty DXObjectPointer"); return &pointer; }
	T*		operator->() const			{ return pointer; }
	T&		operator*() const			{ return *pointer; }

	T*		get() const				{ return pointer; }

	void		reset()					{ if (pointer) pointer->Release(); pointer = nullptr; }
		

private:
	T*		pointer;
};

Example usage: (without error handling)

DXObjectPointer<IDXGIFactory> factory;
CreateDXGIFactory(__uuidof(IDXGIFactory), reinterpret_cast<void**>(&factory));

{
	DXObjectPointer<IDXGIFactory> foo = factory;     // calls IDXGIFactory->AddRef();

	DXObjectPointer<IDXGIAdapter> adapter;
	foo->EnumAdapters(0, &adapter);
    	
	// use get() to obtain a raw IDXGIAdapter* that you can pass to functions
	D3D10CreateDeviceAndSwapChain(adapter.get(), D3D10_DRIVER_TYPE_HARDWARE, NULL, flags, D3D10_SDK_VERSION, &swapChainDesc, &swapChain, &device);

}  // calls IDXGIFactory->Release() when the foo variable is destroyed

Random tricks when using AngularJS

If you don’t know AngularJS, it is a very good Javascript library that allows you to easily build web applications. Here are some random useful tricks that I found for AngularJS users.

Ternary operator in expressions

When you write an expression, like {{ id }}, you can’t use the ternary operator ?:. But you can use the && and || operators. These two lines are identical provided that b always equals true:

a ? b : c
a && b || c

For example:

<p>This feature is {{ feature.isActivated && "activated" || "not activated" }}</p>

This reason why it works is that "a && b" is treated like "if (a) return b; return a;" and "a || b" is treated like "if (!a) return b; return a;".

Using arrays or objects inside expressions

You can use arrays or objects directly from inside AngularJS expressions. For example, you can write the list of days of the week like this:

<ul>
  <li ng-repeat="day in [1,2,3,4,5,6,7]">Name of the day: {{ ['Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat', 'Sun'][day-1] }}</li>
</ul>

This will output:

<ul>
  <li>Name of the day: Mon</li>
  <li>Name of the day: Tue</li>
  <li>Name of the day: Wed</li>
  <li>Name of the day: Thu</li>
  <li>Name of the day: Fri</li>
  <li>Name of the day: Sat</li>
  <li>Name of the day: Sun</li>
</ul>

Watching two variables are once

AngularJS allows you to watch a variable and will call a callback whenever its content is modified. But what if you need to watch two variables at once? You can write this:

function callback() {
  ...
}

$scope.$watch('a', callback);
$scope.$watch('b', callback);

But you can also write this, which is more elegant:

$scope.$watch('a + b', function() {
  ...
});

The value of "a + b" will change whenever either a or b changes. Therefore you watch both variable at once.

Syntax of directives

The tutorial of AngularJS writes directives like this: "ng-model". But you can also write: "x-ng-model", "ng:model", "ng_model", "data-ng-model". They are all equivalent and work the same. I like to use the syntax with "data-" because it is standard-compliant.

Adding a loading screen on AJAX request

This module will add a loading screen over the whole page whenever an AJAX request is made using AngularJS's $http or $resource. This will prevent the user from clicking anywhere, which is a good solution if you don't want to handle the consequences of clicks during requests. Just don't use this if you load several megabytes of data.

angular
    .module('loadingOnAJAX', [])
    .config(function($httpProvider) {
        var numLoadings = 0;
        var loadingScreen = $('<div style="position:fixed;top:0;left:0;right:0;bottom:0;z-index:10000;background-color:gray;background-color:rgba(70,70,70,0.2);"><img style="position:absolute;top:50%;left:50%;" alt="" src="data:image/gif;base64,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" /></div>')
            .appendTo($('body')).hide();
        $httpProvider.responseInterceptors.push(function() {
            return function(promise) {
                numLoadings++;
                loadingScreen.show();
                var hide = function(r) { if (!(--numLoadings)) loadingScreen.hide(); return r; };
                return promise.then(hide, hide);
            };
        });
    });

Global errors handler

In web applications you usually handle AJAX errors globally. If the server returns an error, an error message will be displayed, and it's always at the same location in the application no matter the location of the error. This module allows you to define the location where they will be displayed.

angular
    .module('globalErrors', [])
    .config(function($provide, $httpProvider, $compileProvider) {
        var elementsList = $();

        var showMessage = function(content, cl, time) {
            $('<div/>')
                .addClass('message')
                .addClass(cl)
                .hide()
                .fadeIn('fast')
                .delay(time)
                .fadeOut('fast', function() { $(this).remove(); })
                .appendTo(elementsList)
                .text(content);
        };
        
        $httpProvider.responseInterceptors.push(function($timeout, $q) {
            return function(promise) {
                return promise.then(function(successResponse) {
                    if (successResponse.config.method.toUpperCase() != 'GET')
                        showMessage('Success', 'successMessage', 5000);
                    return successResponse;

                }, function(errorResponse) {
                    switch (errorResponse.status) {
                        case 401:
                            showMessage('Wrong usename or password', 'errorMessage', 20000);
                            break;
                        case 403:
                            showMessage('You don\'t have the right to do this', 'errorMessage', 20000);
                            break;
                        case 500:
                            showMessage('Server internal error: ' + errorResponse.data, 'errorMessage', 20000);
                            break;
                        default:
                            showMessage('Error ' + errorResponse.status + ': ' + errorResponse.data, 'errorMessage', 20000);
                    }
                    return $q.reject(errorResponse);
                });
            };
        });

        $compileProvider.directive('appMessages', function() {
            var directiveDefinitionObject = {
                link: function(scope, element, attrs) { elementsList.push($(element)); }
            };
            return directiveDefinitionObject;
        });
    });

Usage :

<div class="messagesList" app-messages></div>

Whenever an HTTP request (made using AngularJS's $http or $resource object) returns an error code, or when a non-GET request succeeds, a message will be displayed inside this container for a few seconds. Thanks to this, you almost don't need to worry about errors anymore.

Using HTTP basic auth

Some web applications use HTTP basic authentication (through HTTPS). You can also use this with AngularJS by setting the value of the "Authorization" header for the $http service.

$http.defaults.headers.common['Authorization'] = 'Basic ' + Base64.encode($scope.login.login + ':' + $scope.login.password);

Note that Base64.encode comes from this website. IE9 and below don't support atob and bota.

Here is an example code for a login form:

var LoginController = function($scope, $http) {
    $scope.login = {};
    $scope.login.user = null;

    $scope.login.connect = function() {
        $http.get('/users/me').success(function(data, status) {
            if (status < 200 || status >= 300)
                return;
            $scope.login.user = data;
        });
    };
    
    $scope.login.disconnect = function() {
        $scope.login.user = null;
    };

    $scope.$watch('login.login + login.password', function() {
        $http.defaults.headers.common['Authorization'] = 'Basic ' + Base64.encode($scope.login.login + ':' + $scope.login.password);
    });
};
<div ng-controller="LoginController">
  <div ng-hide="login.user">
    <form action="" ng-submit="login.connect()">
      <fieldset>
        <legend>Login</legend>
        <p><input ng-model="login.login" name="email" type="text" placeholder="Login" required /></p>
        <p><input ng-model="login.password" name="password" type="password" placeholder="Password" required /></p>
        <p><button type="submit">Login</button></p>
      </fieldset>
    </form>
  </div>

  <div ng-show="login.user">
    <p>Welcome, {{login.user.userName}}!</p>
    <p><button ng-click="login.disconnect()">Logout</button></p>
  </div>
</div>

What if the user enters a wrong username or password? The global errors handler (see above) will show an error message.

Note: this is out of topic, but if your server returns a 401 code, the web browser will display a "login dialog", asking the login/password of the user. You don't have any control over this dialog. The solution to prevent this is not to return 401 but another status code (418 I'm a Teapot is a good candidate). A clean way to do so is to add a header to tell the server, like this:

$http.defaults.headers.common['X-StatusOnLoginFail'] = '418';

Two-way binding for HTML

If you want to write some HTML that you loaded using AJAX, you can use ng-bind-html-unsafe. But this is a one-directional binding. If the content of the element is modified (thanks to contenteditable for example), the content of the variable is not updated.

This module allows two-way HTML binding between a variable and an element.

angular
	.module('twoWayHTMLBinding', [])
	.directive('adminBindHtml', function($timeout) {
		return {
			link: function compile(scope, tElement, tAttrs) {
				var refresh = function() {
					scope.$apply(tAttrs.adminBindHtml + ' = "' + tElement.html().replace(/"/g, '\\"') + '"');
					$timeout(refresh, 200);
				};
				scope.$watch(tAttrs.adminBindHtml, function(val, oldVal) {
					if (val != oldVal && tElement.html() != val)
						tElement.html(scope.$eval(tAttrs.adminBindHtml));
				});
				$timeout(refresh, 200);
			}
		};
	});

Example:

<div ng-init="htmlData = '&lt;p&gt;Hello!&lt;/p&gt;'">
  <div admin-bind-html="htmlData" contenteditable="true"></div>
</div>

Note: this may seem obvious, but don't allow non-admins to modify HTML code that can be displayed to anyone, or if you do you must add a server-side filter.

Logging exceptions in C++11

It’s not a very well-known feature of C++11, but you are now able to get a pointer to the exception currently being processed.

void foo() {
	try {
		throw anything;
	} catch(...) {
		auto exceptionPtr = std::current_exception();
	}
}

In this example, exceptionPtr is of type std::exception_ptr. This type is kind of a smart pointer, which means that as long as the exceptionPtr variable or one of its copies exists, the thrown object still exists too.

So what can you do with a std::exception_ptr ? Nothing, except rethrow it later.

void foo() {
	std::exception_ptr exceptionPtr;
	try {
		throw std::exception("Error!");
	} catch(...) {
		exceptionPtr = std::current_exception();
	}

	... code here ...

	try {
		std::rethrow_exception(exceptionPtr);
	} catch(const std::exception& e) {
		std::cout << e.what();     // will print "Error!"
	}
}

Note that there exist a std::make_exception_ptr function that can build a std::exception_ptr without having to go through a try-catch block. The code just above can be rewritten like this:

void foo() {
	auto exceptionPtr = std::make_exception_ptr(std::exception("Error!"));

	... code here ...

	try {
		std::rethrow_exception(exceptionPtr);
	} catch(const std::exception& e) {
		std::cout << e.what();     // will print "Error!"
	}
}

These features are mainly here to support the new thread and future functions. But you can also use them to log exceptions:

void logException(std::exception_ptr exception) {
	try {
		std::rethrow_exception(exception);

	} catch(const std::system_error& e) {
		log("std::system_error with error code: " + e.code().message());
		log("Message: " + std::string(e.what()));

	} catch(const std::exception& e) {
		log("std::exception of type " + std::string(typeid(e).name()));
		log("Message: " + std::string(e.what()));

	} catch(...) {
		log("Exception of unknown type");
	}
}

int main() {
	try {
		... anything ...
		return 0;
	} catch(...) {
		logException(std::current_exception());
		return 1;
	}
}

These features are supported in both GCC 4.4 and Visual C++ 2012.

[C++] Converting GUID to string and vice-versa

Here are two small functions to convert between a GUID and a string.

#ifdef _MSC_VER
#	define snprintf	_snprintf
#endif

std::string guidToString(GUID guid) {
	std::array<char,40> output;
	snprintf(output.data(), output.size(), "{%08X-%04hX-%04hX-%02X%02X-%02X%02X%02X%02X%02X%02X}", guid.Data1, guid.Data2, guid.Data3, guid.Data4[0], guid.Data4[1], guid.Data4[2], guid.Data4[3], guid.Data4[4], guid.Data4[5], guid.Data4[6], guid.Data4[7]);
	return std::string(output.data());
}

GUID stringToGUID(const std::string& guid) {
	GUID output;
	const auto ret = sscanf(guid.c_str(), "{%8X-%4hX-%4hX-%2hX%2hX-%2hX%2hX%2hX%2hX%2hX%2hX}", &output.Data1, &output.Data2, &output.Data3, &output.Data4[0], &output.Data4[1], &output.Data4[2], &output.Data4[3], &output.Data4[4], &output.Data4[5], &output.Data4[6], &output.Data4[7]);
	if (ret != 11)
		throw std::logic_error("Unvalid GUID, format should be {00000000-0000-0000-0000-000000000000}");
	return output;
}

AJAX application: adding a handy loading screen to your page

If you are developing an AJAX application, here is a small jQuery code to add a loading screen to your page whenever an AJAX request is made. No external dependency, should work everywhere.

$(function() {
    $('<div style="position:fixed;top:0;left:0;right:0;bottom:0;z-index:10000;background-color:gray;background-color:rgba(70,70,70,0.2);"><img style="position:absolute;top:50%;left:50%;" alt="" src="data:image/gif;base64,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" /></div>')
        .appendTo($('body')).hide()
        .ajaxStart(function() { $(this).show(); }).ajaxStop(function() { $(this).hide(); });
});

PHP: wrapper around XMLWriter

In PHP, when you want to write some XML data, the “correct” way is to use XMLWriter. However it can really be annoying to use for large documents.

Take this simple XHTML page for example:

<?xml version="1.0" encoding="UTF-8"?>
<html lang="fr" xmlns="http://www.w3.org/1999/xhtml">
 <head>
  <title>Test</title>
 </head>
 <body>
  <p>Hello world</p>
 </body>
</html>

If you want to write this using XMLWriter, you get this:

$xmlWriter = new XMLWriter();
$xmlWriter->openURI('php://output');
$xmlWriter->setIndentString(' ');
$xmlWriter->setIndent(true);
$xmlWriter->startDocument('1.0', 'utf-8');

$xmlWriter->startElementNS(null, 'html', 'http://www.w3.org/1999/xhtml');
$xmlWriter->writeAttribute('lang', 'fr');

$xmlWriter->startElement('head');
$xmlWriter->writeElement('title', 'Test');
$xmlWriter->endElement();

$xmlWriter->startElement('body');
$xmlWriter->writeElement('p', 'Hello world');
$xmlWriter->endElement();

$xmlWriter->endElement();
$xmlWriter->endDocument();
$xmlWriter->flush();

You can easily understand why it’s annoying to use! And for larger documents, keeping track of startElement/endElement is also difficult.

So I coded this little wrapper, which you can use if you wish:

function writeXML($xml) {
	$writeNode = function($xmlWriter, $node) use (&$writeNode) {
		if (!isset($node[0]))
			return;

		if (is_array($node[0])) {
			foreach ($node as $elem)
				$writeNode($xmlWriter, $elem);

		} else if (is_string($node[0])) {
			if ($node[0] == '#comment') {
				$xmlWriter->writeComment($node[1]);

			} else if ($node[0] == '#cdata') {
				$xmlWriter->writeCData($node[1]);

			} else {
				$xmlWriter->startElement($node[0]);
				foreach ($node as $key => $value) {
					if (!is_string($key))
						continue;
					if (strlen($key) > 0)
						$xmlWriter->writeAttribute($key, $value);
				}
				foreach ($node as $key => $value) {
					if ($key === 0)
						continue;

					if (is_array($value)) {
						$writeNode($xmlWriter, $value);
					} else if (!is_string($key)) {
						$xmlWriter->text($value);
					}
				}
				$xmlWriter->endElement();
			}
		}
	};

	$writer = new XMLWriter();
	$writer->openMemory();
	$writer->setIndentString(' ');
	$writer->setIndent(true);
	$writer->startDocument('1.0', 'utf-8');
	$writeNode($writer, $xml);
	$writer->endDocument();
	return $writer->outputMemory();
}

Writing this same document is now much more intuitive:

echo writeXML(array('html',
	'lang' => 'fr',
	'xmlns' => 'http://www.w3.org/1999/xhtml',

	array('head',
		array('title', 'Test')
	),

	array('body',
		array('p', 'Hello world'),
		'test'
	)
));

And with PHP 5.4, you can even write this:

echo writeXML(['html',
	'lang' => 'fr',
	'xmlns' => 'http://www.w3.org/1999/xhtml',

	['head',
		['title', 'Test']
	],

	['body',
		['p', 'Hello world'],
		'test'
	]
]);

Even better, you can split your document writing code:

$head = ['head', 'title' => ['Test']];
$body = ['body', 'p' => ['Hello world']];

echo writeXML(['html',
	'lang' => 'fr',
	'xmlns' => 'http://www.w3.org/1999/xhtml',

	$head,
	$body
]);

For me, XML has now become a lot easier to write!