Home / C++


C++ Home

WURFL C++ Tools
 C++ Tools
set of C++ classes that allow you to querry the WURFL repository.
 Doxygen docs






SourceForge.net Logo
 

C++ Wurfl tools
by Emmanuel Papirakis

The C++ Wurfl tools are a set of C++ classes that allow you to querry the Wurfl database through an object oriented interface. Although C++ is not a language that is traditionaly viewed as a web oriented language, it is widely used to write server code for various applications that might benefit from the WURFL even though they are not integrated into a web server.

This package has two external dependencies:

Note that if your C++ compiler supports tr1 (check to see if you have a "tr1/memory" header file), you do not need boost. The only boost feature we use is the boost::shared_ptr template.

1- Installation
First, you need to install boost and libxml2. Once you've done that, you should be good to go. Currently, this package has only been tested on Linux and Solaris. There are no project files for other operating systems but the code should be fairly easy to port (contributed project files for other development environments are welcomed).

2- Quick note on reference counted smart pointers
Those of you who follow the evolution of the C++ already know that a new C++ standard is comming sometime before the end of this decade. Currently, a technical report (tr1) is out and some compilers start to add support for the new functionalities in introduces. Among those new functionalities, there is support for reference counted smart pointers.

You also know that tr1 (and soon to be tr2) is heavily based on Boost, an open source initiative originally started by members of the C++ standardization comitee that aims to provide enhanced C++ functionalities for all currently available C++ compilers. If you are familiar with the boost::shared_ptr template (also known as tr1::shared_ptr) I suggest that you skip this section. If not, read along, this section might introduce you to something that might just save hours of work on your next C++ project.

2.1- What is a "smart pointer"?
You probably already know that C++ lets you overload many of its operator. Among the operators that you can overload, you can find operator->(), operator*() and operator!(). Using these operators (and others such as the assignment operator), it is possible to write a class that offers an interface that closely feels like traditional pointers (even though it is impossible to create a class that is 100% interchangeable a pointers). Smart pointers do just that. They can be used to implement sparse matrices, transparent data base accesses, etc.

2.2- What are "reference counted smart pointers"?
Smart pointers, can also be used to create classes that help managing dynamically allocated memory. You probably already know about the standard auto_ptr. Reference counted smart pointers go one step further. They are aware of the number of times they are referenced, and when their reference count goes down to 0, they automatically delete the object they manage.

If your C++ compiler has support for tr1, it comes with support for tr1::shared_ptr which resides in the <tr1::memory> header file. If not, support for reference counted smart pointers is available from Boost in the <boost/shared_ptr.hpp> header file. In this package, most classes export a typedef named Ptr. This type refers to a reference counted smart pointer to the class. See the examples or the Boost documentation for a more elaborate discussion.

3- Examples:

Example 1: Getting an attribute
#include <wurfl/wurfl.h>
#include <iostream>

#define WURFL_PATH "../wurfl.xml"
#define USER_AGENT "Mozilla/5.0"

using namespace std;
using namespace WURFL;

int main(int argc, char **argv)
{
	Wurfl w(WURFL_PATH);
	CapabilityEntry::Ptr cap(w.GetCapability(USER_AGENT, argv[1]));
	
	if (!cap) {
		cerr << "Bad capability: "
		     << argv[1]
		     << endl;
		return -1;
	} else {
		cout << "The value of capability: "
		     << argv[1]
		     << " "
		     << "for user-agent: "
		     << USER_AGENT
		     << " "
		     << "is: "
		     << cap->GetValue()
		     << endl;
		     return 0;
	}
}
This example shows the easiest way to use the C++ Wurfl tools. The first step is to create a "Wurfl" object. The constructor takes a path on the local file system where a wurfl XML file resides. The constructor can fail for three main reasons:
  • The system does not have enough momory to hold all of the entries
  • The XML file is malformed
  • The Wurfl file does not follow the DTD correctly
In any case, an exception is thrown. In case the system goes out of memory, std::bad_alloc is thrown by the C++ environment. In the other cases, WURFL::Exceptions::BadFile is thrown.

In the example, an object of type CapabilityEntry::Ptr is constructed using the GetCapability method. This method takes 2 arguments:
  • The user-agent as advertized by the browser
  • The name of the capability to look for
In the example, the user-agent is found in a macro and the capability is the first argument given to the program. If no argument is given, this method will fail throwing std::logic_error.

It is possible to see if the method found the requested capability by using operator!(). Note that GetCapability will automatically look in the user-agent's fallbacks. If found, it is possible to know from which DeviceEntry the capability came from by issuing the GetDeviceID method. For a complete list of the methods available in CapabilityEntry, see the package's documentation.

Example 2:
If you do not want the system to search in fallbacks automatically, you need to use GetDeviceEntry instead of GetCapability. It will return an object of type DeviceEntry::Ptr. You can then invoke it's GetCapability method and use "false" for the second parameter.
#include <wurfl/wurfl.h>
#include <iostream>

#define WURFL_PATH "../wurfl.xml"
#define USER_AGENT "Mozilla/5.0"

using namespace std;
using namespace WURFL;

int main(int argc, char **argv)
{
	Wurfl w(WURFL_PATH);
	DeviceEntry::Ptr dev(w.GetDeviceEntry(USER_AGENT));
	
	for (;;) {
		CapabilityEntry::Ptr cap(dev->GetCapability(argv[1], false));
		
		if (!cap) {
			if (dev->GetId() == "generic") {
				cout << "No such capability: "
				     << argv[1]
				     << endl;
				break;
			}
			dev = w.GetDeviceEntryByID(dev->GetFallback());
		} else {
			cout << "The value of capability: "
		    	 << argv[1]
		    	 << " "
		    	 << "for user-agent: "
		    	 << USER_AGENT
		    	 << " "
		    	 << "is: "
		    	 << cap->GetValue()
		    	 << endl
		    	 << "The ID of the DeviceEntry which it was found is: "
		    	 << cap->GetDeviceID()
		    	 << endl;
			break;
		}
	}
}
Example 3: Applying a patch to the WURFL

4- Configuration

4.1- Thread safety
If you compile this software as is, it is not thread safe this is due to performance and portability issues. However, making it thread sage is very easy. If you simply want to make it thread safe and do are not interested in any more details, simply open wurfl/include/wurfl/locks.h. In this file, there is a Mutex class. With two empty methods:
class Mutex
{
public:
	void Lock()
	{ }
	void Unlock()
	{ }
};
If you simply implement them (along with a default constructor and a destructor). The calls to those methods are made properly whenever locking is required. This is done using the RAII idiom through the MutexLocker class.

In theory, once a Wurfl object is created, all of its methods are "read only" and const. In practice however, some internal caching is performed. Inserting elements into the internal cache is not a thread safe operation and some locking is required in a multi-thread environment.

Note that the boost::shared_ptr template is thread safe by default, but, the Makefile that comes with this software disables thread safety. So you need to change that as well (comment -DBOOST_DISABLE_THREADS).

Finally, by default, we use the standard std::basic_string template. Some implementations are "reference counted". For instance:
void f()
{
	std::string s1 = "Hello world";
	std::string s2 = s1;
	
	std::cout << &s1.c_str()[0] << " " << &s2.c_str()[0] << endl;
}
This would print the same address twice (but it can print two seperate addresses and still be reference counted). These implementations go to extremes to hide this behavior from the user, and, in a sing threaded environment, they do a good job at it.

Since the C++ standard is silent about thread safety, it is not impossible that modifying s2 and s1 in seperate threads might lead to a nasty synchronization BUG. If you respect "constness" (i.e.: not use const_cast) this should not be an issue.

4.2- Container selection and the STL
It is the author's opinion that the STL is widely available and, if done correctly, code using a given implementation of the STL works great on other implementations of the STL. However, for their own reasons, some organizations took steps to implement their own version of this library (even though many portable and efficient implementations are freely available for download).

This package uses the STL at its core. However, it doesn't use the STL types directly. Instead, all container types are declared in wurfl/include/wurfl/types.h. This file contains a series of template classes that contain only typedefs.

If you have classes that implement the STL in a namespace other than std, you can change those typedefs to point to your own classes. For instance, if you are using QList template instead of the standard, widely available, simple and efficient std::list template, you can.

Simply replace:
template<typename T>
struct List
{
	typedef std::list<T> type;
};
with:

#include <QList>

template<typename T>
struct List
{
	typedef QList<T> type;
};
in wurfl/include/wurfl/types.h. Since the nice people from Trolltech used an API that is consistent with the STL (even though their implementation is not 100% STL compliant), this package should work just fine.

It is also possible to "tweek" performance by changing certain container types found in this file. By default, we only use std::map for three main reasons:
  • It is the only standard associative container as of this writting.
  • It the associative container that requires the least dynamic memory.
  • It performed acceptably well throughout our tests.
If you prefer to sacrifice memory in the name of increasing speed, you can do so by changing some of the typedefs in this file.

To do so, you can change the typedef found in the StringHash template (or specialise the template and provide a different typedef for a specific type. There are commented exemples in this file).

5 - Installation
Click here to download the WURFL Tools. In the package you will find installation instructions (INSTALL file).

6 - Documentation
On-line Doxygen documentation can be found here (It is also included in the package).


7 - Enjoy
If you have any questions, comments, bug reports about this software please do not hesitate to contact:

Emmanuel dot Papirakis at gmail dot com

Copyright © 2007-2009, Luca Passani