Workaround MSVC behaviour regarding template initialization.
[grantlee:grantlee.git] / dox / generictypes.dox
1 namespace Grantlee
2 {
3
4 /**
5
6   @page generic_types_and_templates Generic type and template support
7
8   @section generic_types Generic type support
9
10   %Grantlee offers powerful support for using any type or container in a QVariant as part of the Context. Although
11   QObject based introspection based on Q_PROPERTY is the most convenient way to access properties on objects in the
12   context, sometimes it is necessary to use classes which do not derive from QObject (and therefore can't have Q_PROPERTY macros)
13   and where it would not be practical to create QObject wrappers around all related classes.
14
15   In such cases the metatype can be registered with %Grantlee and an introspection method can be written.
16
17   @code
18     // Non-QObject
19     class Person
20     {
21     public:
22       Person() :age(0) {}
23       Person(const QString &name, int age)
24         : m_name(name), m_age(age)
25       {
26       }
27
28       QString name() const
29       {
30         return m_name;
31       }
32
33       int age() const
34       {
35         return m_age;
36       }
37
38     private:
39       QString m_name;
40       int m_age;
41     };
42
43     // Make it possible to put Person in a QVariant.
44     Q_DECLARE_METATYPE(Person)
45     // Make it possible to put QList<Person> in a QVariant.
46     Q_DECLARE_METATYPE(QList<Person>)
47
48     // Read-only introspection of Person object.
49     GRANTLEE_BEGIN_LOOKUP(Person)
50       if ( property == "name" )
51         return object.name();
52       else if ( property == "age" )
53         return object.age();
54     GRANTLEE_END_LOOKUP
55
56     void someInitializer()
57     {
58       GRANTLEE_METATYPE_INITIALIZE
59       // Register the Person type with the %Grantlee introspection system.
60       Grantlee::registerMetaType<Person>();
61     }
62
63     Grantlee::Context getContext()
64     {
65       Grantlee::Context c;
66       Person leader("The Leader", 19);
67
68       QList<Person> followers;
69       for (int i = 0; i < 3; ++i)
70       {
71         Person follower("Follower" + QString::number(i), 18);
72         people.append(follower);
73       }
74
75       c.insert("leader", QVariant::fromValue(leader));
76       c.insert("followers", QVariant::fromValue(followers));
77       return c;
78     }
79
80     QString createOutput()
81     {
82       Template t = m_engine->newTemplate(
83         "<h1>{{ leader.name }}</h1>"
84         "<ul>"
85         "{% for person in followers %}"
86           "<li>{{ person.name }}, {{ person.age }}</li>"
87         "{% endfor %}"
88         "</ul>"
89         );
90
91       Context c = getContext();
92
93       return t->render(&c);
94     }
95   @endcode
96
97   There are several necessary steps and consequences.
98   @li The type must be registered as a QMetaType with Q_DECLARE_METATYPE
99   @li If you want to use a Container of the type it too must be registered with Q_DECLARE_METATYPE.
100   @li All containers are supported. (See @ref generic_containers)
101   @li The GRANTLEE_BEGIN_LOOKUP and GRANTLEE_END_LOOKUP macros help to define the introspection of the type. Between them is the definition of a method with the signature QVariant getProperty(const Type &object, const QString &property).
102   @li Grantlee::registerMetaType must be called at some point in the program before attempting to use the type in a Context.
103   @li If no custom type is registered with registerMetaType, then GRANTLEE_METATYPE_INITIALIZE must be used at some point to use the metatype system.
104   @li The Context is created and used as normal.
105
106   @section generic_containers Generic container support
107
108   %Grantlee supports most Qt and stl containers by default if they are registered with the QMetaType system as shown in @ref generic_types.
109   Where a container does not have built in support it can easily be added (See @ref third_party_containers).
110
111   The following containers have built in support:
112
113   @li QList&lt;T&gt;
114   @li QVector&lt;T&gt;
115   @li QSet&lt;T&gt;
116   @li QLinkedList&lt;T&gt;
117   @li QStack&lt;T&gt;
118   @li QQueue&lt;T&gt;
119   @li std::vector&lt;T&gt;
120   @li std::deque&lt;T&gt;
121   @li std::list&lt;T&gt;
122   @li QHash&lt;QString, T&gt;
123   @li QHash&lt;qint16, T&gt;
124   @li QHash&lt;qint32, T&gt;
125   @li QHash&lt;qint64, T&gt;
126   @li QHash&lt;quint16, T&gt;
127   @li QHash&lt;quint32, T&gt;
128   @li QHash&lt;quint64, T&gt;
129   @li QMap&lt;QString, T&gt;
130   @li QMap&lt;qint16, T&gt;
131   @li QMap&lt;qint32, T&gt;
132   @li QMap&lt;qint64, T&gt;
133   @li QMap&lt;quint16, T&gt;
134   @li QMap&lt;quint32, T&gt;
135   @li QMap&lt;quint64, T&gt;
136   @li std::map&lt;QString, T&gt;
137   @li std::map&lt;qint16, T&gt;
138   @li std::map&lt;qint32, T&gt;
139   @li std::map&lt;qint64, T&gt;
140   @li std::map&lt;quint16, T&gt;
141   @li std::map&lt;quint32, T&gt;
142   @li std::map&lt;quint64, T&gt;
143
144   where T is one of
145
146   @li bool
147   @li qint16
148   @li qint32
149   @li qint64
150   @li quint16
151   @li quint32
152   @li quint64
153   @li float
154   @li double
155   @li QVariant
156   @li QString
157   @li QDateTime
158   @li A pointer to a type which inherits QObject
159   @li Any type registered with Grantlee::registerMetaType
160   @li Any supported container
161
162   Note that QSet&lt;T&gt; is an exception and will only work with types for which qHash(T) is defined. This means that QSet&lt;QVariant&gt;
163   is not possible for example.
164
165   Note also that containers of pointers to QObject derived types can be stored in containers of QObject* (that is QVector&lt;QObject*&gt;
166   instead of QVector&lt;PersonObject*&gt;), and they do not need to be explicitly registered with %Grantlee. Where the class has sufficient
167   Q_PROPERTYs defined, the introspection method described above with GRANTLEE_BEGIN_LOOKUP and GRANTLEE_END_LOOKUP is also not
168   necessary. Note also that any type or container can be used through a Q_PROPERTY.
169
170   @code
171     class PersonObject : public QObject
172     {
173       Q_OBJECT
174       Q_PROPERTY(QString name READ name)
175       Q_PROPERTY(int age READ age)
176     public:
177       PersonObject(const QString &name, int age, QObject *parent = 0);
178
179       QString name() const;
180       int age() const;
181     };
182
183     // Make it possible to put the containers in a QVariant
184     Q_DECLARE_METATYPE<QSet<QObject*>)
185     Q_DECLARE_METATYPE<std::vector<QObject*>)
186
187     class SportsClub : public QObject
188     {
189       Q_OBJECT
190       Q_PROPERTY(QString name READ name)
191       Q_PROPERTY(QString sport READ sport)
192       Q_PROPERTY(std::vector<QObject*> members READ members)
193     public:
194       SportsClub(const QString &name, const QString &sport, QObject *parent = 0);
195
196       QString name() const;
197       QString sport() const;
198       std::vector<QObject*> members() const;
199       void setMembers(std::vector<QObject*> members);
200     };
201
202     void someInitializer()
203     {
204       GRANTLEE_METATYPE_INITIALIZE
205       // QObject* already has built in support. No need to register the types
206       // with Grantlee::registerMetaType
207     }
208
209     Grantlee::Context SomeClass::getContext()
210     {
211       Grantlee::Context c;
212
213       QSet<QObject*> clubs;
214       {
215         SportsClub *club = new SportsClub("Smithfield Tennis Club", "Tennis", this);
216
217         std::vector<QObject*> members;
218         {
219           QObject *member = new PersonObject("Alice", 21, this);
220           members.push_back(member);
221         }
222         {
223           QObject *member = new PersonObject("Bob", 22, this);
224           members.push_back(member);
225         }
226         club.setMembers(members);
227       }
228
229       // ... specify other clubs and members
230
231       c.insert("sportsClubs", QVariant::fromValue(clubs));
232       return c;
233     }
234
235     QString createOutput()
236     {
237       Template t = m_engine->newTemplate(
238         "{% regroup sportsClubs by sport as groupedSports %}"
239         "{% for groupedClub in groupedSports %}"
240           "<h1>{{ groupedClub.grouper }}</h1>"
241           "{% for club in groupedClub.list %}"
242             "<h2>{{ club.name }}</h2>"
243             "<ul>"
244             "{% for member in club.members %}"
245               "<li>{{ member.name, }}, {{ member.age }}"
246             "{% endfor %}"
247           "</ul>"
248           "{% endfor %}"
249         "{% endfor %}"
250         );
251
252       Context c = getContext();
253
254       return t->render(&c);
255     }
256   @endcode
257
258   @see <a href="http://docs.djangoproject.com/en/1.1/ref/templates/builtins/#regroup">The regroup tag</a>
259
260   The output would be something like
261
262   @verbatim
263     <h1>Tennis</h1>
264       <h2>Smithfield Tennis Club</h2>
265       <ul>
266         <li>Alice, 21</li>
267         <li>Bob, 22</li>
268       </ul>
269       <h2>Greenore Lawn Tennis and Fitness Club</h2>
270       <ul>
271         <li>Charlie, 23</li>
272         <li>David, 24</li>
273         <li>Elaine, 25</li>
274         <li>Frank, 26</li>
275       </ul>
276     <h1>Basketball</h1>
277       <h2>Sandyford Basketball Club</h2>
278       <ul>
279         <li>Gilian, 27</li>
280         <li>Henry, 28</li>
281       </ul>
282   @endverbatim
283
284   Of course, it is possible to use containers of pointers to concrete QObject subclasses, such as QSet&lt;PersonObject*&gt; and std::vector&lt;SportsClub*&gt;
285   but then it is necessary to register them with %Grantlee using Grantlee::registerMetaType&lt;PersonObject*&gt;()
286   and Grantlee::registerMetaType&lt;SportsClub*&gt;(). The use of GRANTLEE_BEGIN_LOOKUP and GRANTLEE_END_LOOKUP is still not required in
287   that case.
288
289   Because any supported container can also be used as a contained type, nested containers are also supported.
290
291   Note that if a type is registered with Grantlee::registerMetaType, built in containers of that type do not also need to be registered.
292   They will be registered automatically by %Grantlee. Third party containers do need to be registered however (See @ref third_party_containers)
293
294   @code
295     Q_DECLARE_METATYPE(Person)
296     Q_DECLARE_METATYPE(QVector<Person>)
297     Q_DECLARE_METATYPE(QList<QVector<Person> >)
298     Q_DECLARE_METATYPE(QStack<QList<QVector<Person> > >)
299
300     void someInitializer()
301     {
302       Grantlee::registerMetaType<Person>();
303
304       // Now any of the nested containers can be put in a Context and used in a Template.
305     }
306   @endcode
307
308   @section third_party_containers Third party containers
309
310   To support another, non-built in container it is necessary to use some macros to register it with %Grantlee.
311
312   For a sequential container, GRANTLEE_REGISTER_SEQUENTIAL_CONTAINER,
313   GRANTLEE_SEQUENTIAL_TYPE_CONTAINER_ACCESSOR and GRANTLEE_REGISTER_SEQUENTIAL_CONTAINER_IF are needed.
314
315   @code
316     #include <boost/circular_buffer>
317
318     // Enable looping over the contents of the container
319     GRANTLEE_REGISTER_SEQUENTIAL_CONTAINER       (boost::circular_buffer)
320     // Enable operator[] index lookup in the container.
321     GRANTLEE_SEQUENTIAL_TYPE_CONTAINER_ACCESSOR  (boost::circular_buffer)
322
323     Q_DECLARE_METATYPE(boost::circular_buffer<Person>)
324     Q_DECLARE_METATYPE(boost::circular_buffer<SportsClub>)
325
326     void someInitializer()
327     {
328       Grantlee::registerMetaType<Person>();
329       // Make it possible to use circular_buffer<Person> and Container<circular_buffer<Person> >
330       // for any built in Container.
331       GRANTLEE_REGISTER_SEQUENTIAL_CONTAINER_IF(boost::circular_buffer, Person)
332
333       // Make it possible to use circular_buffer<SportsClub> and Container<circular_buffer<SportsClub> >
334       // for any built in container.
335       GRANTLEE_REGISTER_SEQUENTIAL_CONTAINER_IF(boost::circular_buffer, SportsClub)
336     }
337
338     Grantlee::Context getContext()
339     {
340       Grantlee::Context c;
341
342       boost::circular_buffer<Person> buffer(5);
343       // loop
344       {
345         Person p("Grant", 21);
346         b.push_back(p);
347       }
348       c.insert("people", QVariant::fromValue(buffer));
349     }
350   @endcode
351
352   Note that if the container does not allow operator[] lookup it is necessary to use the GRANTLEE_DISABLE_RANDOM_ACCESS macro instead of
353   GRANTLEE_SEQUENTIAL_TYPE_CONTAINER_ACCESSOR
354
355   @code
356     #include <ext/slist>
357
358     // Enable looping over the contents of the container
359     GRANTLEE_REGISTER_SEQUENTIAL_CONTAINER       (std::slist)
360     // Disable operator[] index lookup in the container.
361     GRANTLEE_DISABLE_RANDOM_ACCESS               (std::slist)
362   @endcode
363
364   This means that template constructs such as <tt>{{ someList.4 }}</tt> will work for containers with operator[], such as QList and
365   QVector, but will not work with containers like QLinkedList, QSet, std::list.
366
367   @see @ref lookups
368
369   For associative containers GRANTLEE_REGISTER_ASSOCIATIVE_CONTAINER,
370   GRANTLEE_ASSOCIATIVE_TYPE_CONTAINER_ACCESSOR and GRANTLEE_REGISTER_ASSOCIATIVE_CONTAINER_IF are needed.
371
372   It might also be necessary, depending on the API of the container to define additional accessors for keys and
373   values from a %const_iterator. By default, %Grantlee uses iter.key() and iter.value() (The Qt default) to
374   access them. Grantlee::KeyGetter and Grantlee::MappedValueGetter can be specialized for custom access.
375   Note that they must be defined in the Grantlee namespace
376
377   @code
378     namespace {
379     // Make it possible to put Qt types in a std::hash_map
380     template <typename Key>
381     struct QtHasher
382     {
383       size_t operator()(Key k) const
384       { return qHash(k); }
385     };
386
387     // std::hash_map capable of storing Qt types.
388     template<typename Key, typename Value>
389     struct QtHashMap : public std::hash_map<Key, Value, QtHasher<Key> >
390     {
391
392     };
393
394     }
395
396     // Register the container with Grantlee
397     GRANTLEE_REGISTER_ASSOCIATIVE_CONTAINER       (QtHashMap)
398     GRANTLEE_ASSOCIATIVE_TYPE_CONTAINER_ACCESSOR  (QtHashMap)
399
400     namespace Grantlee {
401
402     // Specialize for getting the key from the iterator
403     template<typename T, typename U>
404     struct KeyGetter<QtHashMap<T, U> > : public Getter<QtHashMap<T, U> >
405     {
406       static T get(typename QtHashMap<T, U>::const_iterator it)
407       {
408         return it->first;
409       }
410     };
411
412     // Specialize for getting the value from the iterator
413     template<typename T, typename U>
414     struct MappedValueGetter<QtHashMap<T, U> > : public Getter<QtHashMap<T, U> >
415     {
416       static U get(typename QtHashMap<T, U>::const_iterator it)
417       {
418         return it->second;
419       }
420     };
421     }
422
423   @endcode
424
425   @section smart_pointers Smart Pointers
426
427   Shared pointer types containing a custom type should be introspected as normal using GRANTLEE_BEGIN_LOOKUP and GRANTLEE_END_LOOKUP
428
429   @code
430     Q_DECLARE_METATYPE(QSharedPointer<Person>)
431
432     void someInitializer()
433     {
434       Grantlee::registerMetaType<QSharedPointer<Person> >();
435     }
436
437     GRANTLEE_BEGIN_LOOKUP(QSharedPointer<Person>)
438       if (property == "name")
439         return object->name();
440     GRANTLEE_END_LOOKUP
441   @endcode
442
443   Note that if only shared pointers to the type is in your introspectable API you only need to define the property access for the
444   shared pointer. In the case above, there is no need to use Q_DECLARE_METATYPE or GRANTLEE_BEGIN_LOOKUP with Person or Person*.
445
446   This is of course true of any smart pointer:
447
448   @code
449     Q_DECLARE_METATYPE(boost::shared_ptr<Person>)
450
451     GRANTLEE_BEGIN_LOOKUP(boost::shared_ptr<Person>)
452       if (property == "name")
453         return object->name();
454     GRANTLEE_END_LOOKUP
455   @endcode
456
457   QSharedPointers containing QObject derived types get special treatment.
458
459   QObjects are automatically introspected for their Q_PROPERTYs (See @ref custom_objects).
460
461   If you have QSharedPointer&lt;PersonObject&gt; where PersonObject is derived from QObject it will be automatically
462   introspected just like a QObject* is without requiring the GRANTLEE_BEGIN_LOOKUP and GRANTLEE_END_LOOKUP macros.
463
464   @code
465     Q_DECLARE_METATYPE(QSharedPointer<PersonObject>)
466
467     void someInitializer()
468     {
469       Grantlee::registerMetaType<QSharedPointer<PersonObject> >();
470     }
471
472     void getContext()
473     {
474       Grantlee::Context c;
475       QSharedPointer<PersonObject> p( new PersonObject );
476       c.insert("person", QVariant::fromValue(p));
477       return c;
478     }
479
480     QString createOutput()
481     {
482       // Uses Q_PROPERTYs defined on PersonObject for name and age
483       Template t = m_engine->newTemplate( "{{ person.name }}, {{ person.age }}" );
484
485       Context c = getContext();
486
487       return t->render(&c);
488     }
489   @endcode
490
491   Note that Grantlee::registerMetaType must be used for the specialized shared pointer type.
492
493   The same technique can be used to support QObject derived types in third party shared pointers, but that requires
494   an extra macro, GRANTLEE_SMART_PTR_ACCESSOR.
495
496   @code
497     Q_DECLARE_METATYPE(boost::shared_ptr<PersonObject>)
498     GRANTLEE_SMART_PTR_ACCESSOR(boost::shared_ptr)
499
500     void someInitializer()
501     {
502       Grantlee::registerMetaType<boost::shared_ptr<PersonObject> >();
503     }
504
505     void getContext()
506     {
507       Grantlee::Context c;
508       boost::shared_ptr<PersonObject> p( new PersonObject );
509       c.insert("person", QVariant::fromValue(p));
510       return c;
511     }
512   @endcode
513
514 */
515
516 }