|
DesignThe library is designed by using trait patterns, similar to the standard I/O. That means, that the classes that the users see are not the classes that do all the work. Instead, the real work is deligated to static utility classes - the traits. All the classes that the user see are in fact templates. As a (fictive) example of this, consider the following dummy trait:template <typename Foo> class foo_traits { public: static void init(Foo&) {} static void destroy(Foo&) {} static void doit(Foo&) {} }; The dummy trait above will be specialised to work with the specific underlying library, as given an example of here: template <> class foo_traits<foo_t> { public: static void init(foo_t& foo) { foo_init(&foo); } static void destroy(foo_t& foo) { foo_destroy(&foo); } static void doit(foo_t& foo) { foo_doit(&foo); } }; foo_init , foo_destroy , and foo_doit via this trait.Now the dummy trait and the specialised trait are purely static, so they are not of much use to the user. We'd like to be able to create mutex, thread, etc. objects, and use those. Also, we'd might like to derive from some class that makes these things avaliable, so we can do special stuff. For this to happen, the library defines basic classes of the concepts that the user would like to have. Such a class may look like: template <typename Foo, typename Trait=foo_traits<Foo> > class basic_foo { public: typedef Foo foo_type; typedef Trait traits_type; private: foo_type _foo; public: basic_foo() { traits_type::init(_foo); } virtual ~basic_foo() { traits_type::destroy(_foo); } void doit() { traits_type::doit(_foo); } };
The user can now use the basic_foo<foo_t> my_foo; foo_t depends on the underlying library, and that there'll only be one valid Foo parameters for a given library, the header files does a typedef to make the interface even easier to deal with: #ifndef THREAD_foo #define THREAD_foo #include <threadmm/basic/foo.hh> // defined basic_foo<Foo> #ifdef HAVE_my_h #include <threadmm/my/foo.hh> // Defined foo_traits<foo_t> typedef basic_foo<foo_t> foo; #endif foo my_foo;
If you substitute Note, that this design means that there's no inheritance, and hence no expensive virtual member function calls - the class hierarchy is completly flat. It does mean that the compiler has to generated more code, as it has to make specialised code for all the implecit and explcit template specialisations used in the code. However, as one will only use one thread back-end at a time, this is not really a problem. Also, who gives a ... if your program takes up 100kB or 200kB of RAM - most people will have at least 64MB in this day and age (fall 2002). There's one class that's notably different - that's the class threadmm::thread_specific. That's because it's not possible to do the typedef trick with partially specialised template classes i.e., what we'd like to do is something like template <typename Data, typename Mutex, typename Key, typename Trait=thread_specific_traits<Mutex, Key> > class basic_thread_specific { ... public: Data* operator->() { ... } } template<> class thread_specific_traits<posix_mutex, pthread_key_t> { ... }; template <typename Data> typedef thread_specific<Data,posix_mutex,pthread_t> thread_specific<Data>; Last update Tue Nov 9 12:40:50 2004 Christian Holm Created by DoxyGen 1.3.9.1 |