Fawkes API  Fawkes Development Version
tolua_generator.cpp
1 
2 /***************************************************************************
3  * tolua_generator.cpp - ToLua++ Interface generator
4  *
5  * Created: Tue Mar 11 15:33:26 2006
6  * Copyright 2006-2008 Tim Niemueller [www.niemueller.de]
7  *
8  ****************************************************************************/
9 
10 /* This program is free software; you can redistribute it and/or modify
11  * it under the terms of the GNU General Public License as published by
12  * the Free Software Foundation; either version 2 of the License, or
13  * (at your option) any later version.
14  *
15  * This program is distributed in the hope that it will be useful,
16  * but WITHOUT ANY WARRANTY; without even the implied warranty of
17  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
18  * GNU Library General Public License for more details.
19  *
20  * Read the full text in the LICENSE.GPL file in the doc directory.
21  */
22 
23 #include "tolua_generator.h"
24 #include "exceptions.h"
25 
26 #include <utils/misc/string_conversions.h>
27 
28 #include <algorithm>
29 #include <iostream>
30 #include <vector>
31 #include <time.h>
32 #include <fstream>
33 
34 using namespace std;
35 
36 
37 /** @class ToLuaInterfaceGenerator <interfaces/generator/tolua_generator.h>
38  * Generator that transforms input from the InterfaceParser into valid
39  * ToLua++ package file.
40  * @author Tim Niemueller
41  */
42 
43 /** Constructor.
44  * @param directory Directory where to create the files
45  * @param interface_name name of the interface, should end with Interface
46  * @param config_basename basename of the config without suffix
47  * @param author author of interface
48  * @param year year of copyright
49  * @param creation_date user-supplied creation date of interface
50  * @param data_comment comment in data block.
51  * @param hash MD5 hash of the config file that was used to generate the interface
52  * @param hash_size size in bytes of hash
53  * @param constants constants
54  * @param enum_constants constants defined as an enum
55  * @param data_fields data fields of the interface
56  * @param pseudo_maps pseudo maps of the interface
57  * @param messages messages defined in the interface
58  */
59 ToLuaInterfaceGenerator::ToLuaInterfaceGenerator(std::string directory, std::string interface_name,
60  std::string config_basename, std::string author,
61  std::string year, std::string creation_date,
62  std::string data_comment,
63  const unsigned char *hash, size_t hash_size,
64  const std::vector<InterfaceConstant> &constants,
65  const std::vector<InterfaceEnumConstant> &enum_constants,
66  const std::vector<InterfaceField> &data_fields,
67  const std::vector<InterfacePseudoMap> &pseudo_maps,
68  const std::vector<InterfaceMessage> &messages
69  )
70 {
71  this->dir = directory;
72  if ( dir.find_last_of("/") != (dir.length() - 1) ) {
73  dir += "/";
74  }
75  this->author = author;
76  this->year = year;
77  this->creation_date = creation_date;
78  this->data_comment = data_comment;
79  this->hash = hash;
80  this->hash_size = hash_size;
81  this->constants = constants;
82  this->enum_constants = enum_constants;
83  this->data_fields = data_fields;
84  this->pseudo_maps = pseudo_maps;
85  this->messages = messages;
86 
87  filename_tolua = config_basename + ".tolua";
88  filename_h = config_basename + ".h";
89 
90  if ( interface_name.find("Interface", 0) == string::npos ) {
91  // append Interface
92  class_name = interface_name + "Interface";
93  } else {
94  class_name = interface_name;
95  }
96 }
97 
98 
99 /** Destructor */
101 {
102 }
103 
104 
105 /** Convert C type to Lua type.
106  * tolua++ does not deal well with stdint types, therefore we convert them
107  * to "traditional" types.
108  * @param c_type C type to convert
109  * @return constant string of the Lua compatible type
110  */
111 const char *
113 {
114  if (c_type == "uint8_t") {
115  return "unsigned char";
116  } else if (c_type == "uint16_t") {
117  return "unsigned short";
118  } else if (c_type == "uint32_t") {
119  return "unsigned int";
120  } else if (c_type == "uint64_t") {
121 #if __WORDSIZE == 64 || defined(__x86_64__)
122  return "unsigned long";
123 #else
124  return "unsigned long long";
125 #endif
126  } else if (c_type == "int8_t") {
127  return "char";
128  } else if (c_type == "int16_t") {
129  return "short";
130  } else if (c_type == "int32_t") {
131  return "int";
132  } else if (c_type == "int64_t") {
133 #if __WORDSIZE == 64 || defined(__x86_64__)
134  return "long";
135 #else
136  return "long long";
137 #endif
138  } else if (c_type == "uint8_t *") {
139  return "unsigned char *";
140  } else if (c_type == "uint16_t *") {
141  return "unsigned short *";
142  } else if (c_type == "uint32_t *") {
143  return "unsigned int *";
144  } else if (c_type == "uint64_t *") {
145 #if __WORDSIZE == 64 || defined(__x86_64__)
146  return "unsigned long *";
147 #else
148  return "unsigned long long *";
149 #endif
150  } else if (c_type == "int8_t *") {
151  return "char *";
152  } else if (c_type == "int16_t *") {
153  return "short *";
154  } else if (c_type == "int32_t *") {
155  return "int *";
156  } else if (c_type == "int64_t *") {
157 #if __WORDSIZE == 64 || defined(__x86_64__)
158  return "long *";
159 #else
160  return "long long *";
161 #endif
162  } else {
163  return c_type.c_str();
164  }
165 }
166 
167 
168 
169 /** Write header to file.
170  * @param f file to write to
171  * @param filename name of file
172  */
173 void
174 ToLuaInterfaceGenerator::write_header(FILE *f, std::string filename)
175 {
176  fprintf(f, "\n/***************************************************************************\n");
177  fprintf(f, " * %s - Fawkes BlackBoard Interface - %s - tolua++ wrapper\n", filename.c_str(), class_name.c_str());
178  fprintf(f, " *\n");
179  if ( creation_date.length() > 0 ) {
180  fprintf(f, " * Interface created: %s\n", creation_date.c_str());
181  }
182  fprintf(f, " * Templated created: Thu Oct 12 10:49:19 2006\n");
183  fprintf(f, " * Copyright %s %s\n", year.c_str(),
184  ((author.length() > 0) ? author.c_str() : "AllemaniACs RoboCup Team") );
185  fprintf(f, " *\n");
186  fprintf(f, " ****************************************************************************/\n\n");
187  fprintf(f, "/*\n");
188  fprintf(f, " * This program is free software; you can redistribute it and/or modify\n");
189  fprintf(f, " * it under the terms of the GNU General Public License as published by\n");
190  fprintf(f, " * the Free Software Foundation; either version 2 of the License, or\n");
191  fprintf(f, " * (at your option) any later version.\n");
192  fprintf(f, " *\n");
193  fprintf(f, " * This program is distributed in the hope that it will be useful,\n");
194  fprintf(f, " * but WITHOUT ANY WARRANTY; without even the implied warranty of\n");
195  fprintf(f, " * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n");
196  fprintf(f, " * GNU Library General Public License for more details.\n");
197  fprintf(f, " *\n");
198  fprintf(f, " * You should have received a copy of the GNU General Public License\n");
199  fprintf(f, " * along with this program; if not, write to the Free Software Foundation,\n");
200  fprintf(f, " * Inc., 51 Franklin Street, Fifth floor, Boston, MA 02111-1307, USA.\n");
201  fprintf(f, " */\n\n");
202 }
203 
204 
205 /** Write constants to h file
206  * @param f file to write to
207  */
208 void
210 {
211  for ( vector<InterfaceConstant>::iterator i = constants.begin(); i != constants.end(); ++i) {
212  fprintf(f, " static const %s %s;\n", convert_type(i->getType()),
213  i->getName().c_str());
214  }
215  fprintf(f, "\n");
216 
217  for ( vector<InterfaceEnumConstant>::iterator i = enum_constants.begin(); i != enum_constants.end(); ++i) {
218  fprintf(f, " typedef enum {\n");
219  vector<InterfaceEnumConstant::EnumItem> items = (*i).get_items();
220  vector<InterfaceEnumConstant::EnumItem>::iterator j = items.begin();
221  while (j != items.end()) {
222  if (j->has_custom_value) {
223  fprintf(f, " %s = %i", j->name.c_str(), j->custom_value);
224  } else {
225  fprintf(f, " %s", j->name.c_str());
226  }
227  ++j;
228  if ( j != items.end() ) {
229  fprintf(f, ",\n");
230  } else {
231  fprintf(f, "\n");
232  }
233  }
234  fprintf(f, " } %s;\n\n", (*i).get_name().c_str());
235  }
236 }
237 
238 
239 /** Write messages to h file.
240  * @param f file to write to
241  */
242 void
244 {
245  for (vector<InterfaceMessage>::iterator i = messages.begin(); i != messages.end(); ++i) {
246  fprintf(f, " class %s : public Message\n"
247  " {\n", (*i).getName().c_str());
248  write_message_ctor_dtor_h(f, " ", (*i).getName(), (*i).getFields());
249  write_message_superclass_h(f);
250  write_methods_h(f, " ", (*i).getFields());
251 
252  fprintf(f, " };\n\n");
253  }
254 
255 }
256 
257 
258 /** Write constructor and destructor to h file.
259  * @param f file to write to
260  * @param is indentation space
261  * @param classname name of class
262  */
263 void
264 ToLuaInterfaceGenerator::write_ctor_dtor_h(FILE *f, std::string /* indent space */ is,
265  std::string classname)
266 {
267  fprintf(f,
268  "%s%s();\n"
269  "%s~%s();\n\n",
270  is.c_str(), classname.c_str(),
271  is.c_str(), classname.c_str());
272 }
273 
274 
275 /** Write constructor and destructor for message to h file.
276  * @param f file to write to
277  * @param is indentation space
278  * @param classname name of class
279  * @param fields vector of data fields of message
280  */
281 void
282 ToLuaInterfaceGenerator::write_message_ctor_dtor_h(FILE *f, std::string /* indent space */ is,
283  std::string classname,
284  std::vector<InterfaceField> fields)
285 {
286  vector<InterfaceField>::iterator i;
287 
288  if ( fields.size() > 0 ) {
289 
290  fprintf(f, "%s%s(", is.c_str(), classname.c_str());
291 
292  i = fields.begin();
293  while (i != fields.end()) {
294  fprintf(f, "%s ini_%s",
295  convert_type(i->getAccessType()), i->getName().c_str());
296  ++i;
297  if ( i != fields.end() ) {
298  fprintf(f, ", ");
299  }
300  }
301 
302  fprintf(f, ");\n");
303  }
304 
305 
306  write_ctor_dtor_h(f, is, classname);
307 }
308 
309 /** Write superclass methods.
310  * @param f file to write to
311  */
312 void
314 {
315  fprintf(f,
316  " bool oftype(const char *interface_type) const;\n"
317  " const void * datachunk() const;\n"
318  " unsigned int datasize() const;\n"
319  " const char * type() const;\n"
320  " const char * id() const;\n"
321  " const char * uid() const;\n"
322  " unsigned int serial() const;\n"
323  " unsigned int mem_serial() const;\n"
324  " bool operator== (Interface &comp) const;\n"
325  " const unsigned char * hash() const;\n"
326  " int hash_size() const;\n"
327  " const char * hash_printable() const;\n"
328  " bool is_writer() const;\n"
329 
330  " void set_from_chunk(void *chunk);\n"
331 
332  " virtual fawkes::Message * create_message @ create_message_generic(const char *type) const;\n"
333 
334  " void read();\n"
335  " void write();\n"
336 
337  " bool has_writer() const;\n"
338  " unsigned int num_readers() const;\n"
339 
340  " bool changed() const;\n"
341  " const fawkes::Time * timestamp() const;\n"
342  " void set_auto_timestamping(bool enabled);\n"
343  " void set_timestamp(const fawkes::Time *t);\n"
344  " void set_clock(fawkes::Clock *clock);\n"
345 
346  " unsigned int msgq_enqueue_copy(Message *message);\n"
347  " void msgq_remove(Message *message);\n"
348  " void msgq_remove(unsigned int message_id);\n"
349  " unsigned int msgq_size();\n"
350  " void msgq_flush();\n"
351  " void msgq_lock();\n"
352  " bool msgq_try_lock();\n"
353  " void msgq_unlock();\n"
354  " void msgq_pop();\n"
355  " fawkes::Message * msgq_first @ msgq_first_generic();\n"
356  " bool msgq_empty();\n"
357  "\n");
358 
359 }
360 
361 
362 /** Write superclass methods.
363  * @param f file to write to
364  */
365 void
367 {
368  fprintf(f,
369  " unsigned int id() const;\n"
370  "\n"
371  " unsigned int sender_id() const;\n"
372  " const char * sender_thread_name() const;\n"
373  " Interface * interface() const;\n"
374  " const char * type() const;\n"
375  "\n"
376  " const void * datachunk() const;\n"
377  " unsigned int datasize() const;\n"
378  "\n"
379  " void set_from_chunk(const void *chunk);\n"
380  "\n"
381  " /* from RefCount */\n"
382  " void ref();\n"
383  " void unref();\n"
384  " unsigned int refcount();\n"
385  "\n");
386 }
387 
388 
389 /** Write additional Lua code to file.
390  * The code is required for correctly type message access.
391  * @param f file to write to
392  * @param classname name of the interface class
393  */
394 void
395 ToLuaInterfaceGenerator::write_lua_code(FILE *f, std::string classname)
396 {
397  fprintf(f,
398  "\n$[\n\n"
399  "assert(fawkes.Interface.msgq_first)\n"
400  "assert(fawkes.Interface.msgq_enqueue)\n"
401  "assert(fawkes.Interface.create_message)\n\n"
402  "fawkes.%s.msgq_first = fawkes.Interface.msgq_first\n"
403  "fawkes.%s.msgq_enqueue = fawkes.Interface.msgq_enqueue\n"
404  "fawkes.%s.create_message = fawkes.Interface.create_message\n"
405  "\n$]\n\n",
406  classname.c_str(), classname.c_str(), classname.c_str());
407 }
408 
409 /** Write methods to h file.
410  * @param f file to write to
411  * @param is indentation space.
412  * @param fields fields to write accessor methods for.
413  */
414 void
415 ToLuaInterfaceGenerator::write_methods_h(FILE *f, std::string /* indent space */ is,
416  std::vector<InterfaceField> fields)
417 {
418  for (vector<InterfaceField>::iterator i = fields.begin(); i != fields.end(); ++i) {
419 
420  if ( (i->getLengthValue() > 0) && (i->getType() != "string" ) ) {
421  fprintf(f,
422  "%s%s %s%s(int index);\n",
423  is.c_str(),
424  (i->getType() == "byte") ? "unsigned int" : convert_type(i->getPlainAccessType()),
425  ( ((*i).getType() == "bool" ) ? "is_" : ""),
426  (*i).getName().c_str());
427 
428  fprintf(f,
429  "%svoid set_%s(unsigned int index, const %s new_%s);\n",
430  is.c_str(), (*i).getName().c_str(),
431  convert_type(i->getPlainAccessType()), i->getName().c_str());
432  } else {
433  fprintf(f,
434  "%s%s %s%s();\n",
435  is.c_str(), convert_type(i->getAccessType()),
436  ( ((*i).getType() == "bool" ) ? "is_" : ""),
437  (*i).getName().c_str());
438 
439  fprintf(f,
440  "%svoid set_%s(const %s new_%s);\n",
441  is.c_str(), (*i).getName().c_str(),
442  convert_type(i->getAccessType()), i->getName().c_str());
443  }
444  fprintf(f,
445  "%sint maxlenof_%s() const;\n",
446  is.c_str(), (*i).getName().c_str()
447  );
448  }
449 }
450 
451 
452 /** Write methods to h file.
453  * @param f file to write to
454  * @param is indentation space.
455  * @param fields fields to write accessor methods for.
456  * @param pseudo_maps pseudo maps
457  */
458 void
459 ToLuaInterfaceGenerator::write_methods_h(FILE *f, std::string /* indent space */ is,
460  std::vector<InterfaceField> fields,
461  std::vector<InterfacePseudoMap> pseudo_maps)
462 {
463  write_methods_h(f, is, fields);
464 
465  for (vector<InterfacePseudoMap>::iterator i = pseudo_maps.begin(); i != pseudo_maps.end(); ++i) {
466  fprintf(f,
467  "%s%s %s(%s key) const;\n"
468  "%svoid set_%s(const %s key, const %s new_value);\n",
469  is.c_str(), convert_type(i->getType()),
470  (*i).getName().c_str(), convert_type(i->getKeyType()),
471  is.c_str(), (*i).getName().c_str(),
472  convert_type(i->getKeyType()), convert_type(i->getType()));
473  }
474 }
475 
476 
477 /** Write h file.
478  * @param f file to write to
479  */
480 void
482 {
483  fprintf(f,
484  "$#include <interfaces/%s>\n"
485  "$#include <utils/time/time.h>\n"
486  "$#include <utils/time/clock.h>\n"
487  "$using namespace fawkes;\n"
488  "namespace fawkes {\n"
489  "class %s : public Interface\n"
490  "{\n",
491  filename_h.c_str(),
492  class_name.c_str());
493 
494  write_constants_h(f);
495  write_messages_h(f);
496  //write_ctor_dtor_h(f, " ", class_name);
497  write_methods_h(f, " ", data_fields, pseudo_maps);
498  write_superclass_h(f);
499  fprintf(f, "\n};\n\n");
500  write_lua_code(f, class_name);
501  fprintf(f, "}\n");
502 }
503 
504 
505 /** Generator cpp and h files.
506  */
507 void
509 {
510  char timestring[26]; // 26 is mentioned in man asctime_r
511  struct tm timestruct;
512  time_t t = time(NULL);
513  localtime_r(&t, &timestruct);
514  asctime_r(&timestruct, timestring);
515  gendate = timestring;
516 
517  FILE *toluaf;
518 
519  toluaf = fopen(string(dir + filename_tolua).c_str(), "w");
520 
521  if ( toluaf == NULL ) {
522  printf("Cannot open tolua file %s%s\n", dir.c_str(), filename_tolua.c_str());
523  }
524 
525  write_toluaf(toluaf);
526 
527  fclose(toluaf);
528 }
~ToLuaInterfaceGenerator()
Destructor.
void write_toluaf(FILE *f)
Write h file.
void write_superclass_h(FILE *f)
Write superclass methods.
void write_message_ctor_dtor_h(FILE *f, std::string is, std::string classname, std::vector< InterfaceField > fields)
Write constructor and destructor for message to h file.
void write_methods_h(FILE *f, std::string is, std::vector< InterfaceField > fields)
Write methods to h file.
void write_constants_h(FILE *f)
Write constants to h file.
const char * convert_type(std::string c_type)
Convert C type to Lua type.
STL namespace.
void write_messages_h(FILE *f)
Write messages to h file.
void write_message_superclass_h(FILE *f)
Write superclass methods.
void write_header(FILE *f, std::string filename)
Write header to file.
void generate()
Generator cpp and h files.
ToLuaInterfaceGenerator(std::string directory, std::string interface_name, std::string config_basename, std::string author, std::string year, std::string creation_date, std::string data_comment, const unsigned char *hash, size_t hash_size, const std::vector< InterfaceConstant > &constants, const std::vector< InterfaceEnumConstant > &enum_constants, const std::vector< InterfaceField > &data_fields, const std::vector< InterfacePseudoMap > &pseudo_maps, const std::vector< InterfaceMessage > &messages)
Constructor.
void write_ctor_dtor_h(FILE *f, std::string is, std::string classname)
Write constructor and destructor to h file.
void write_lua_code(FILE *f, std::string classname)
Write additional Lua code to file.