Lumiera  0.pre.03
»edit your freedom«
visitingtool-concept.cpp
Go to the documentation of this file.
1 /*
2  VisitingTool(Concept) - concept draft of a Visitor library implementation
3 
4  Copyright (C) Lumiera.org
5  2008, Hermann Vosseler <Ichthyostega@web.de>
6 
7  This program is free software; you can redistribute it and/or
8  modify it under the terms of the GNU General Public License as
9  published by the Free Software Foundation; either version 2 of
10  the License, or (at your option) any later version.
11 
12  This program is distributed in the hope that it will be useful,
13  but WITHOUT ANY WARRANTY; without even the implied warranty of
14  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15  GNU General Public License for more details.
16 
17  You should have received a copy of the GNU General Public License
18  along with this program; if not, write to the Free Software
19  Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
20 
21 * *****************************************************/
22 
23 
56 #include "lib/test/run.hpp"
57 #include "lib/format-cout.hpp"
58 #include "lib/format-string.hpp"
59 #include "lib/depend.hpp"
60 
61 #include <vector>
62 
63 using util::_Fmt;
64 using std::string;
65 
66 
67 namespace lumiera {
68  namespace visitor_concept_draft {
69 
70  // ================================================================== Library ====
71 
72 
73 
74  template<class TOOL> class Tag;
75 
76 
77  template<class TOOL, class TOOLImpl>
79  {
80  static Tag<TOOL> tag;
81  };
82 
83 
84  template<class TOOL>
85  class Tag
86  {
87  size_t tagID;
88  static size_t lastRegisteredID;
89 
90  public:
91  Tag() : tagID(0) { }
92  operator size_t() const { return tagID; }
93 
94 
95  template<class TOOLImpl>
96  static Tag&
97  get (TOOLImpl* const =0) // param used to pass type info
98  {
99  // we have a race condition here...
101  if (!t)
102  t.tagID = ++lastRegisteredID;
103  return t;
104  }
105 
106  };
107 
108 
109 
111  template<class TOOL, class TOOLImpl>
113 
114  template<class TOOL>
115  size_t Tag<TOOL>::lastRegisteredID (0);
116 
117 
118 
119 
120 
121 
123  template<typename RET>
124  class Tool
125  {
126  public:
127  typedef RET ReturnType;
128  typedef Tool<RET> ToolBase;
129 
130  virtual ~Tool() { };
131 
134  virtual Tag<ToolBase> getTag() =0;
135  };
136 
137 
139  template<class TOOLImpl, class BASE =Tool<void>>
140  class ToolType
141  : public BASE
142  {
143  typedef typename BASE::ToolBase ToolBase;
144 
145  public:
146  virtual Tag<ToolBase>
147  getTag()
148  {
149  TOOLImpl* typeKey = 0;
150  return Tag<ToolBase>::get (typeKey);
151  }
152  };
153 
154 
155 
156 
162  template<class TAR, class TOOL>
164  {
165  typedef typename TOOL::ReturnType ReturnType;
166 
172  template<class TOOLImpl>
173  static ReturnType
174  callTrampoline (TAR& obj, TOOL& tool)
175  {
176  // cast down to real implementation type
177  CHECK (INSTANCEOF (TOOLImpl, &tool));
178  TOOLImpl& toolObj = static_cast<TOOLImpl&> (tool);
179 
180  // trigger (compile time) overload resolution
181  // based on concrete type, then dispatch the call.
182  // Note this may cause obj to be upcasted.
183  return toolObj.treat (obj);
184  }
185 
186  typedef ReturnType (*Trampoline) (TAR&, TOOL& );
187 
188 
190  std::vector<Trampoline> table_;
191 
192 
193  inline bool
194  is_known (size_t id)
195  {
196  return id<=table_.size() && table_[id-1];
197  }
198 
199  inline void
200  storePtr (size_t id, Trampoline func)
201  {
202  // lacks error- and concurrency handling....
203  if (id>table_.size())
204  table_.resize (id);
205  table_[id-1] = func;
206  }
207 
208  inline Trampoline
209  storedTrampoline (size_t id)
210  {
211  if (id<=table_.size() && table_[id-1])
212  return table_[id-1];
213  else
214  return &errorHandler;
215  }
216 
217  static ReturnType
218  errorHandler (TAR&, TOOL&)
219  {
220  cout << "Error Handler: unregistered combination of (Tool, TargetObject) invoked!\n";
221  }
222 
223 
224  public:
226 
227  inline ReturnType
228  forwardCall (TAR& target, TOOL& tool)
229  {
230  // get concrete type via tool's VTable
231  Tag<TOOL> index = tool.getTag();
232  return (*storedTrampoline(index)) (target, tool);
233  }
234 
235  template<class TOOLImpl>
236  inline void
237  enrol (TOOLImpl* typeKey)
238  {
239  Tag<TOOL>& index = Tag<TOOL>::get (typeKey);
240  if (is_known (index))
241  return;
242  else
243  {
244  Trampoline func = &callTrampoline<TOOLImpl>;
245  storePtr (index, func);
246  }
247 
248  }
249  };
250 
252  template<class TAR, class TOOL>
254 
255 
256 
257 
258 
264  template<class TAR, class TOOLImpl, class BASE=Tool<void> >
266  {
267  typedef typename BASE::ReturnType Ret;
268  typedef typename BASE::ToolBase ToolBase;
269 
270  protected:
271  Applicable()
272  {
273  TOOLImpl* typeKey = 0;
274  Dispatcher<TAR,ToolBase>::instance().enrol (typeKey);
275  }
276 
277  virtual ~Applicable () {}
278 
279  public:
280  // we could enforce the definition of treat()-functions by:
281  //
282  // virtual Ret treat (TAR& target) = 0;
283 
284  };
285 
286 
287 
288 
289 
292  template
293  < class TOOL = Tool<void>
294  >
295  class Visitable
296  {
297  protected:
298  virtual ~Visitable() { };
299 
301  typedef typename TOOL::ToolBase ToolBase;
302  typedef typename TOOL::ReturnType ReturnType;
303 
309  template <class TAR>
310  static inline ReturnType
311  dispatchOp (TAR& target, TOOL& tool)
312  {
313  return Dispatcher<TAR,ToolBase>::instance().forwardCall (target,tool);
314  }
315 
316  public:
319  virtual ReturnType apply (TOOL&) = 0;
320  };
321 
322 
327 #define DEFINE_PROCESSABLE_BY(TOOL) \
328  virtual ReturnType apply (TOOL& tool) \
329  { return dispatchOp (*this, tool); }
330 
331 
332  // =============================================================(End) Library ====
333 
334 
335 
336 
337 
338  namespace test {
339 
340  typedef Tool<void> VisitingTool;
341 
342  class HomoSapiens : public Visitable<>
343  {
344  public:
345  DEFINE_PROCESSABLE_BY (VisitingTool);
346  };
347 
348  class Boss : public HomoSapiens
349  {
350  public:
351  DEFINE_PROCESSABLE_BY (VisitingTool);
352  };
353 
354  class BigBoss : public Boss
355  {
356  public:
357  DEFINE_PROCESSABLE_BY (VisitingTool);
358  };
359 
360  class Leader : public Boss
361  {
362  };
363 
364  class Visionary : public Leader
365  {
366  };
367 
368 
370  : public VisitingTool
371  {
372  protected:
373  void talk_to (string guy)
374  {
375  cout << _Fmt{"Hello %s, nice to meet you...\n"} % guy;
376  }
377  };
378 
379 
380  class Babbler
381  : public Applicable<Boss,Babbler>,
382  public Applicable<BigBoss,Babbler>,
383  public Applicable<Visionary,Babbler>,
384  public ToolType<Babbler, VerboseVisitor>
385  {
386  public:
387  void treat (Boss&) { talk_to("Boss"); }
388  void treat (BigBoss&) { talk_to("Big Boss"); }
389 
390  };
391 
392 
393 
394 
395 
396 
397 
398  /*********************************************************************/
409  class VisitingTool_concept : public Test
410  {
411  virtual void run(Arg)
412  {
413  known_visitor_known_class();
414  visitor_not_visiting_some_class();
415  }
416 
417  void known_visitor_known_class()
418  {
419  Boss x1;
420  BigBoss x2;
421 
422  // masquerade as HomoSapiens...
423  HomoSapiens& homo1 (x1);
424  HomoSapiens& homo2 (x2);
425 
426  cout << "=== Babbler meets Boss and BigBoss ===\n";
427  Babbler bab;
428  VisitingTool& vista (bab);
429  homo1.apply (vista);
430  homo2.apply (vista);
431  }
432 
433  void visitor_not_visiting_some_class()
434  {
435  HomoSapiens x1;
436  Visionary x2;
437 
438  HomoSapiens& homo1 (x1);
439  HomoSapiens& homo2 (x2);
440 
441  cout << "=== Babbler meets HomoSapiens and Visionary ===\n";
442  Babbler bab;
443  VisitingTool& vista (bab);
444  homo1.apply (vista); // error handler (not Applicable to HomoSapiens)
445  homo2.apply (vista); // treats Visionary as Boss
446  }
447 
448  };
449 
450 
452  LAUNCHER (VisitingTool_concept, "unit common");
453 
454 
455 
456 }}} // namespace lumiera::visitor_concept_draft::test
Automatically use custom string conversion in C++ stream output.
any concrete visiting tool implementation has to inherit from this class for each kind of calls it wa...
Definition: run.hpp:49
virtual ReturnType apply(TOOL &)=0
to be defined by the DEFINE_PROCESSABLE_BY macro in all classes wanting to be treated by some tool ...
#define INSTANCEOF(CLASS, EXPR)
shortcut for subclass test, intended for assertions only.
Definition: util.hpp:447
static Tag< TOOL > tag
storage for the Tag registry for each concrete tool
Front-end for printf-style string template interpolation.
A front-end for using printf-style formatting.
Mixin for attaching a type tag to the concrete tool implementation.
Access point to singletons and other kinds of dependencies designated by type.
Definition: depend.hpp:289
static ReturnType callTrampoline(TAR &obj, TOOL &tool)
generator for Trampoline functions, used to dispatch calls down to the right "treat"-Function on the ...
Marker interface "visiting tool".
std::vector< Trampoline > table_
custom VTable for storing the Trampoline pointers
static lib::Depend< Dispatcher< TAR, TOOL > > instance
storage for the dispatcher table(s)
Simple test class runner.
Tool< RET > ToolBase
for templating the Tag and Dispatcher
For each possible call entry point via some subclass of the visitable hierarchy, we maintain a dispat...
Singleton services and Dependency Injection.
Lumiera public interface.
Definition: advice.cpp:113
Marker interface "visitable object".
static ReturnType dispatchOp(TAR &target, TOOL &tool)
#define DEFINE_PROCESSABLE_BY(TOOL)
mark a Visitable subclass as actually treatable by some "visiting tool".