docu updates
[libt2n] / codegen / main.cpp
1 /*
2     Copyright (C) 2006                                                    
3     intra2net.com                                                         
4
5     This program is free software; you can redistribute it and/or modify
6     it under the terms of the GNU General Public License as published by
7     the Free Software Foundation; either version 2 of the License, or
8     (at your option) any later version.
9
10     This program is distributed in the hope that it will be useful,
11     but WITHOUT ANY WARRANTY; without even the implied warranty of
12     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13     GNU General Public License for more details.
14
15     You should have received a copy of the GNU General Public License
16     along with this program; if not, write to the Free Software
17     Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
18 */
19
20 /*! \mainpage libt2n - (talk to neighbor)
21  \section intro_sec Introduction
22  libt2n (talk to neighbor) is a C++ library for inter-process communication (IPC, s.a. http://en.wikipedia.org/wiki/Inter-process_communication)
23  with an additional code generator to make remote procedure calls simple. 
24  \par 
25  XXX: improve this paragraph: The input for the code generator is standard C++ code (in fact we use gccxml to parse the C++ code and the code generator takes the XML as input) and you mark the procedures you want to expose to other processes.
26  It then generates the stubs needed.
27  The exported procedures can be grouped. For each group the code generator is called and generates 6 output files: group_common.hxx, group_common.cpp, group_client.hxx, group_client.cpp, group_server.hxx, group_server.cpp. The _common files are used by client and server whereas the _client files are used by the client and the _server files by the server only.
28  \par
29  To simplify the build process a Makefile snippet is provided that allows to create a server program and a client library (including a corresponding .pc file) using the autotools easily.
30  \section install_sec Installation
31
32  \subsection requirements Requirements
33  - boost <http://www.boost.org/> (serialization <http://www.boost.org/libs/serialization/doc/>)
34  - gccxml <http://www.gccxml.org>
35  - libxmlpp <http://libxmlplusplus.sourceforge.net/>
36
37  \subsection recommended Recommended
38  - pkg-config <http://pkgconfig.freedesktop.org/wiki/>
39  - autotools (automake, autoconf, libtool)
40
41  \subsection Compilation
42  \verbatim
43  ./configure && make install
44  \endverbatim
45
46
47  \section usage Usage example
48
49  In this example we create two packages using the autotools:
50  - server program and client library to connect to the server. The server exports a simple procedure using one group: "t2nexample"
51  - client program using the library
52
53  \subsection server Example server program and client library
54
55  \par The procedure to export (input for the code generator - libt2n-codegen): t2nexample.cpp:
56  \verbinclude libt2n-example1/t2nexample.cpp
57
58  \par Required includes go into the group header file: t2nexample.hxx:
59  libt2n uses boost for serialization. This means all types involved in a remote procedure call must be boost serializable. In this example we use std::string and boost
60  already provides serialization for std::string in the boost/serialization/string.hpp header file.
61  \verbinclude libt2n-example1/t2nexample.hxx
62
63  \par The server program:
64  \verbinclude libt2n-example1/server.cpp
65
66  \par Using autoconf and automake to build a example server program and a client library.
67  In the configure.in(.ac) we put a check for libt2n:
68  \verbinclude libt2n-example1/configure.in
69  Writing the Makefile.am isn't difficult either:
70  \verbinclude libt2n-example1/Makefile.am
71
72  \par Build and install the package
73  \verbatim
74  autoreconf -f -i && ./configure && make install
75  \endverbatim
76
77  \subsection client Client using the library
78  Using the library is as simple as using any other library using pkg-config (the pkg-config .pc file is created automatically by the included Makefile snippet)
79  \par We only have to check that the library is installed
80  \verbinclude libt2n-example1-client/configure.in
81  \par The Makefile.am needs nothing special
82  \verbinclude libt2n-example1-client/Makefile.am
83  \par The client program
84  \verbinclude libt2n-example1-client/client.cpp
85
86  \par Build and install the package
87  \verbatim
88  autoreconf -f -i && ./configure && make install
89  \endverbatim
90
91  \par Test
92  \verbatim
93 $ cd /tmp
94 $ file socket
95 socket: cannot open `socket' (No such file or directory)
96 $ libt2n-example1-server &
97 [1] 7711
98 $ file socket
99 socket: socket
100 $ libt2n-example1-client && echo ok
101 ok
102 $ kill %1
103 $ rm socket
104  \endverbatim
105
106  
107 */
108
109 /*!
110  \example t2nexample.cpp
111  example input for libt2n-codegen
112 */
113
114 #include <libxml++/libxml++.h>
115 #include <cassert>
116 #include <iostream>
117 #include <set>
118 #include <fstream>
119 #include <list>
120 #include <stdexcept>
121 #include <boost/lexical_cast.hpp>
122 #ifdef HAVE_CONFIG_H
123 #include "config.h"
124 #endif
125
126
127 //! map group to class name
128 std::string
129 groupClass(const std::string &group) {
130     return std::string("cmd_group_")+group;
131 }
132
133 //! convert string to upper case
134 std::string
135 toupper(std::string s) {
136      for (unsigned i=0; i<s.length(); ++i) s[i]=toupper(s[i]);
137      return s;
138 }
139
140 //! replace all characters f by r in string s
141 std::string
142 replace(std::string s, char f, char r) {
143      for (unsigned i=0; i<s.length(); ++i) if (s[i]==f) s[i]=r;
144      return s;
145 }
146
147 //! strip prefix from string s
148 /*!
149   \return string s without prefix or an empty string on error
150  */
151 std::string
152 strip(std::string s, std::string prefix)
153 {
154      std::string error;
155      if ( (prefix.length()>s.length() ) || ( std::string(s,0,prefix.length())!=prefix ) ) return error;
156      return std::string(s, prefix.length(), s.length()-prefix.length());
157 }
158
159 //! get child element by id
160 /*!
161   \return pointer to element having id or null on error
162   \todo find libxmlpp pendant
163 */
164 const xmlpp::Element* get_element_by_id(const xmlpp::Element* element, const std::string &id)
165 {
166      const xmlpp::Attribute* cid = element->get_attribute("id");
167      if ( cid && ( cid->get_value() == id)) return element;
168
169      //Recurse through child nodes:
170      xmlpp::Node::NodeList list = element->get_children();
171      for(xmlpp::Node::NodeList::iterator iter = list.begin(); iter != list.end(); ++iter)
172      {
173           const xmlpp::Element* element = dynamic_cast<const xmlpp::Element*>(*iter);
174           if (element) {
175                const xmlpp::Element* match = get_element_by_id(element, id);
176                if (match) return match;
177           }
178      }
179      return NULL;
180 }
181
182 std::string
183 get_file(const xmlpp::Element* root, const std::string &file_id)
184 {
185      std::string error;
186      const xmlpp::Element* e=get_element_by_id(root, file_id);
187      if ((!e)||(!e->get_attribute("name"))) return error;
188      return e->get_attribute("name")->get_value();
189 }
190
191 //! get namespace by id
192 /*!
193   \return namespace name or empty string on error
194 */
195 std::string get_namespace(const xmlpp::Element* root, const std::string &id)
196 {
197      std::string error;
198      const xmlpp::Element* element(get_element_by_id(root, id));
199      if ((!element)||(!element->get_attribute("name"))) return error;
200      return element->get_attribute("name")->get_value();
201 }
202
203 //! extract group from attributes
204 std::string
205 extract_group(const std::string &attrs)
206 {
207      // todo: improve this
208      std::string error;
209      std::string to_match("gccxml(libt2n-");
210      std::string::size_type p(attrs.find(to_match));
211      if (p==std::string::npos) return error;
212      std::string group(attrs, p+to_match.length(), attrs.length());
213      p=group.find_first_of(')');
214      assert(p!=std::string::npos);
215      return std::string(group,0,p);
216 }
217
218 struct type_info
219 {
220      std::string name;
221      std::string noref_name;
222      bool operator==(const type_info& o) {return (name==o.name) && (noref_name == o.noref_name);}
223      std::string noref() const {return noref_name.empty() ? name : noref_name;}
224 };
225
226 std::ostream &operator<<(std::ostream &o, const type_info &t) {
227      o << t.name;
228      return o;
229 }
230
231 struct parse_error : public std::runtime_error
232 {
233      parse_error(const std::string &file, unsigned line, const std::string &msg)
234           : std::runtime_error(file+":"+boost::lexical_cast<std::string>(line)+": error: "+msg)
235           {}
236 };
237
238 //! get type by id
239 /*!
240   \return type name or empty string on error
241 */
242 type_info get_type(const xmlpp::Element* root, const std::string &id)
243 {
244      type_info error;
245      const xmlpp::Element* element(get_element_by_id(root, id));
246      if (!element) return error;
247
248      // TODO: not yet complete
249      // if we recurse - when do we stop?
250      // if it is a typedef? yes? (hmm if the typedef is in the file parsed this will not work)
251
252      // TODO: const and reference types handling is a ugly hack
253
254      std::string tag(element->get_name());
255      if (tag=="ReferenceType") {
256           assert(element->get_attribute("type"));
257           type_info ret(get_type(root, element->get_attribute("type")->get_value()));
258           if (ret==error) return error;
259           // at the moment we only support const &
260           // todo: nice error message!
261           if ((ret.noref_name=strip(ret.name,"const ")).empty()) return error;
262           ret.name=ret.name+"&";
263           return ret;
264      }else if (tag=="CvQualifiedType") {
265           assert(element->get_attribute("type"));
266           type_info ret(get_type(root, element->get_attribute("type")->get_value()));
267           if (ret==error) return error;
268           ret.name=std::string("const ")+ret.name;
269           return ret;
270      }else if (tag=="PointerType") {
271           // not yet supported
272           return error;
273      }
274
275      assert(element->get_attribute("name"));
276      type_info ret;
277      if (element->get_attribute("context")) {
278           ret.name=get_namespace(root, element->get_attribute("context")->get_value());
279           if (ret.name!="::")
280                ret.name+="::";
281           else
282                // do not explicitely add ::
283                ret.name="";
284      }
285      ret.name+=element->get_attribute("name")->get_value();
286      return ret;
287 }
288
289 struct t2n_procedure
290 {
291      typedef std::list<std::pair<std::string, type_info> > Args;
292
293      type_info ret_type;
294      std::string name;
295      std::string mangled;
296      Args  args;
297
298      std::string ret_classname() const {
299           return name+mangled+"_res";
300      }
301      std::string cmd_classname() const {
302           return name+mangled+"_cmd";
303      }
304 };
305
306 std::ostream &operator<<(std::ostream &o, const t2n_procedure::Args &args) {
307      for (t2n_procedure::Args::const_iterator it=args.begin();it!=args.end();++it) {
308           if (it!=args.begin()) o << ", ";
309           o << it->second << " " << it->first;
310      }
311      return o;
312 }
313
314 std::ostream &operator<<(std::ostream &o, const t2n_procedure &f) {
315      o << f.ret_type << " " << f.name << "(" << f.args << ")";
316      return o;
317 }
318
319 class Parser
320 {
321 public:
322      Parser(const std::string &fname) : m_fname(fname) {}
323
324      std::list<t2n_procedure> get_procedures() {
325           xmlpp::DomParser parser;
326           //    parser.set_validate();
327           parser.set_substitute_entities(); //We just want the text to be resolved/unescaped automatically.
328           parser.parse_file(m_fname);
329           if(parser)
330           {
331                //Walk the tree:
332                const xmlpp::Node* pNode = parser.get_document()->get_root_node(); //deleted by DomParser.
333                const xmlpp::Element* root = dynamic_cast<const xmlpp::Element*>(pNode);
334                assert(root);
335                visit_node(root);
336           }
337           return m_procedures;
338      }
339 protected:
340      std::string m_fname;
341      std::list<t2n_procedure> m_procedures;
342
343      void parse_function(const xmlpp::Element* root, const xmlpp::Node* node) {
344           const xmlpp::Element* element = dynamic_cast<const xmlpp::Element*>(node);
345           if (!element) return;
346
347           const xmlpp::Attribute* attributes = element->get_attribute("attributes");
348           const xmlpp::Attribute* name = element->get_attribute("name");
349           const xmlpp::Attribute* mangled = element->get_attribute("mangled");
350           const xmlpp::Attribute* returns = element->get_attribute("returns");
351           if ((!attributes)||(!name)||(!mangled)||(!returns)) return;
352
353           // check wether the procedure is marked (TODO: improve)
354           // attributes are speparated by spaces?
355
356           t2n_procedure f;
357           if (extract_group(attributes->get_value()).empty()) return;
358
359           // we need the return type
360           f.ret_type=get_type(root, returns->get_value());
361           f.name=name->get_value();
362           f.mangled=mangled->get_value();
363
364           xmlpp::Node::NodeList list = node->get_children("Argument");
365           for(xmlpp::Node::NodeList::iterator iter = list.begin(); iter != list.end(); ++iter)
366           {
367                const xmlpp::Element* arg = dynamic_cast<const xmlpp::Element*>(*iter);
368                if ( arg ) {
369                     assert(arg->get_name() == "Argument");
370                     assert(arg->get_attribute("name"));
371                     assert(arg->get_attribute("type"));
372                     f.args.push_back(std::pair<std::string, type_info>(arg->get_attribute("name")->get_value(), get_type(root, arg->get_attribute("type")->get_value())));
373                     // todo: ugly - could be any other error
374                     if (f.args.back().second.name.empty()) {
375                          assert(element->get_attribute("file"));
376                          assert(element->get_attribute("line"));
377                          throw parse_error(get_file(root, element->get_attribute("file")->get_value()),
378                                            boost::lexical_cast<unsigned>(element->get_attribute("line")->get_value())-1,
379                                            std::string("type of parameter `")+f.args.back().first+"' not (yet?) supported");
380                     }
381                }
382           }
383           std::cerr << "Found function: " << f << std::endl;
384           m_procedures.push_back(f);
385      }
386
387      void visit_node(const xmlpp::Element* root, const xmlpp::Node* node = NULL, unsigned int indentation = 0)
388           {
389                if (!node) node=root;
390           
391                const xmlpp::ContentNode* nodeContent = dynamic_cast<const xmlpp::ContentNode*>(node);
392                const xmlpp::TextNode* nodeText = dynamic_cast<const xmlpp::TextNode*>(node);
393                const xmlpp::CommentNode* nodeComment = dynamic_cast<const xmlpp::CommentNode*>(node);
394
395                if(nodeText && nodeText->is_white_space()) //Let's ignore the indenting - you don't always want to do this.
396                     return;
397     
398                std::string nodename = node->get_name();
399
400                if(!nodeText && !nodeComment && !nodename.empty()) //Let's not say "name: text".
401                {
402                     if (node->get_name() == "Function") parse_function(root, node);
403                }
404                if(!nodeContent)
405                {
406                     //Recurse through child nodes:
407                     xmlpp::Node::NodeList list = node->get_children();
408                     for(xmlpp::Node::NodeList::iterator iter = list.begin(); iter != list.end(); ++iter)
409                     {
410                          visit_node(root, *iter, indentation + 2); //recursive
411                     }
412                }
413           }
414 };
415
416 void output_common_hpp(std::ostream &o, const std::string &group, const std::list<t2n_procedure> &procs) {
417     o << "class " << groupClass(group) << " : public libt2n::command\n"
418       << "{\n"
419       << "private:\n"
420       << "      friend class boost::serialization::access;\n"
421       << "      template<class Archive>\n"
422       << "      void serialize(Archive & ar, const unsigned int /* version */)\n"
423       << "      {ar & BOOST_SERIALIZATION_BASE_OBJECT_NVP(libt2n::command);}\n"
424       << "};\n";
425      
426     for (std::list<t2n_procedure>::const_iterator it=procs.begin();it!=procs.end();++it) {
427         o << "class " << it->ret_classname() << " : public libt2n::result\n"
428           << "{\n"
429           << "private:\n"
430           << "  " << it->ret_type << " res;\n"
431           << "  friend class boost::serialization::access;\n"
432           << "  template<class Archive>\n"
433           << "  void serialize(Archive & ar, const unsigned int /* version */)\n"
434           << "  {\n"
435           << "          ar & BOOST_SERIALIZATION_BASE_OBJECT_NVP(libt2n::result);\n"
436           << "          ar & BOOST_SERIALIZATION_NVP(res);\n"
437           << "  }\n"
438           << "public:\n"
439           << "  " << it->ret_classname() << "() {}\n"
440           << "  " << it->ret_classname() << "(const " << it->ret_type << " &_res) : res(_res) {}\n"
441           << "  " << it->ret_type << " get_data() { return res; }\n"
442           << "};\n";
443     }
444     for (std::list<t2n_procedure>::const_iterator it=procs.begin();it!=procs.end();++it) {
445         o << "class " << it->cmd_classname() << " : public " << groupClass(group) << "\n"
446           << "{\n"
447           << "private:\n";
448         for (t2n_procedure::Args::const_iterator ait=it->args.begin();ait!=it->args.end();++ait) {
449             o << "      " << ait->second.noref() << " " << ait->first << ";\n";
450         }
451         o << "  friend class boost::serialization::access;\n"
452           << "  template<class Archive>\n"
453           << "  void serialize(Archive & ar, const unsigned int /* version */)\n"
454           << "  {\n"
455           << "          ar & BOOST_SERIALIZATION_BASE_OBJECT_NVP(" << groupClass(group) << ");\n";
456         for (t2n_procedure::Args::const_iterator ait=it->args.begin();ait!=it->args.end();++ait) {
457             o << "              ar & BOOST_SERIALIZATION_NVP(" << ait->first << ");\n";
458         }
459         
460         // default constructor
461         o << "  }\n"
462           << "\n"
463           << "public:\n"
464           << "  " << it->cmd_classname() << "() {}\n";
465         
466         // constructor taking all arguments
467         o << "  " << it->cmd_classname() << "(";
468         for (t2n_procedure::Args::const_iterator ait=it->args.begin();ait!=it->args.end();++ait) {
469             if (ait!=it->args.begin()) o << ", ";
470             o << ait->second << " _" << ait->first;
471         }
472         o << ") : ";
473         for (t2n_procedure::Args::const_iterator ait=it->args.begin();ait!=it->args.end();++ait) {
474             if (ait!=it->args.begin()) o << ", ";
475             o << ait->first << "(_" << ait->first << ")";
476         }
477         o << " {}\n"
478           << "  libt2n::result* operator()();\n"
479           << "};\n";
480     }
481 }
482
483 void output_common_cpp(std::ostream &o, const std::string &group, const std::list<t2n_procedure> &procs, const std::string &common_hpp) {
484      o << "#include \"" << common_hpp << "\"\n"
485        << "#include <boost/serialization/export.hpp>\n"
486        << "\n"
487        << "/* register types with boost serialization */\n";
488      o << "BOOST_CLASS_EXPORT(" << groupClass(group) << ")\n";
489      for (std::list<t2n_procedure>::const_iterator it=procs.begin();it!=procs.end();++it) {
490           o << "BOOST_CLASS_EXPORT("<<it->ret_classname()<<")\n"
491             << "BOOST_CLASS_EXPORT("<<it->cmd_classname()<<")\n";
492      }
493 }
494
495 void output_client_hpp(std::ostream &o, const std::string &group, const std::list<t2n_procedure> &procs) {
496      o << "#include <command_client.hxx>\n";
497
498      o << "class " << groupClass(group) << "_client : public libt2n::command_client\n"
499        << "{\n"
500        << "public:\n"
501        << groupClass(group) << "_client(libt2n::client_connection &_c,\n"
502        << "     long long _command_timeout_usec=command_timeout_usec_default,\n"
503        << "     long long _hello_timeout_usec=hello_timeout_usec_default)\n"
504        << "     : libt2n::command_client(_c,_command_timeout_usec,_hello_timeout_usec)\n"
505        << "     {}\n";
506      for (std::list<t2n_procedure>::const_iterator pit=procs.begin();pit!=procs.end();++pit) {
507          o << " " << *pit << ";\n";
508      }
509      o << "};\n";
510 }
511
512 void output_client_cpp(std::ostream &o, const std::string &group, const std::list<t2n_procedure> &procs, const std::string &common_hpp, const std::string &common_cpp, const std::string &client_hpp) {
513      o << "#include \"" << client_hpp << "\"\n"
514        << "#include \"" << common_hpp << "\"\n"
515        << "// fake\n";
516      for (std::list<t2n_procedure>::const_iterator it=procs.begin();it!=procs.end();++it) {
517           o << "libt2n::result* " << it->cmd_classname() << "::operator()() { return NULL; }\n";
518      }
519
520      for (std::list<t2n_procedure>::const_iterator pit=procs.begin();pit!=procs.end();++pit) {
521          o << pit->ret_type << " " << groupClass(group) << "_client::" << pit->name << "(" << pit->args << ")\n"
522            << "{\n"
523            << " libt2n::result_container rc;\n"
524            << " send_command(new " << pit->cmd_classname() << "(";
525          for (t2n_procedure::Args::const_iterator ait=pit->args.begin();ait!=pit->args.end();++ait) {
526              if (ait!=pit->args.begin()) o << ", ";
527              o << ait->first;
528          }
529          o << "), rc);\n"
530            << " " << pit->ret_classname() << "* res=dynamic_cast<" << pit->ret_classname() << "*>(rc.get_result());\n"
531            << " if (!res) throw libt2n::t2n_communication_error(\"result object of wrong type\");\n"
532            << " return res->get_data();\n"
533            << "}\n";
534      }
535
536      // include in this compilation unit to ensure the compilation unit is used
537      // see also:
538      // http://www.google.de/search?q=g%2B%2B+static+initializer+in+static+library
539      o << "#include \"" << common_cpp << "\"\n";
540 }
541
542 void output_server_hpp(std::ostream &o, const std::string & /* group */, const std::list<t2n_procedure> &procs, const std::string &common_hpp) {
543      o << "#include \"" << common_hpp << "\"\n";
544
545      // output function declarations
546      for (std::list<t2n_procedure>::const_iterator it=procs.begin();it!=procs.end();++it)
547        o << *it << ";\n";
548 }
549
550 void output_server_cpp(std::ostream &o, const std::string &group, const std::list<t2n_procedure> &procs, const std::string &common_hpp, const std::string &common_cpp) {
551      o << "#include \"" << common_hpp << "\"\n";
552
553      for (std::list<t2n_procedure>::const_iterator it=procs.begin();it!=procs.end();++it) {
554           o << *it << ";\n";
555           o << "libt2n::result* " << it->cmd_classname() << "::operator()() { return new " << it->ret_classname() << "(" << it->name << "(";
556           for (t2n_procedure::Args::const_iterator ait=it->args.begin();ait!=it->args.end();++ait) {
557                if (ait!=it->args.begin()) o << ", ";
558                o << ait->first;
559           }
560           o << ")); }\n";
561      }
562      o << "#include \"" << common_cpp << "\"\n";
563 }
564
565 struct header_file : public std::ofstream
566 {
567      header_file(const char* fname) : std::ofstream(fname) {
568           std::cerr << "create header: '" << fname << "'" << std::endl;
569           std::string macro(replace(toupper(fname),'.','_'));
570           *this << "// automatically generated code (generated by libt2n-codegen " << VERSION << ") - do not edit\n" << std::endl;
571           *this << "#ifndef " << macro << "\n"
572                 << "#define " << macro << "\n";
573      }
574      ~header_file() {
575           *this << "#endif" << std::endl;
576      }
577 };
578
579 struct cpp_file : public std::ofstream
580 {
581      cpp_file(const char* fname) : std::ofstream(fname) {
582           std::cerr << "create cpp: '" << fname << "'" << std::endl;
583           *this << "// automatically generated code - do not edit\n" << std::endl;
584      }
585 };
586
587 int
588 main(int argc, char* argv[])
589 {
590     // todo: maybe use getopt
591     if ((argc>1)&&(std::string(argv[1])=="--version")) {
592         std::cerr << VERSION << std::endl;
593         return 0;
594     }
595     if (argc < 3)
596     {
597         std::cerr << "Usage: " << argv[0] << "default-group gccxml-file1 gccxml-file2 ... " << std::endl;
598         return 1;
599     }
600
601     try{
602           std::string group(argv[1]);
603           std::list<std::string> xmlfiles;
604           for (int i=2;i<argc;++i)
605             xmlfiles.push_back(argv[i]);
606
607           std::string prefix=group+"_";
608           std::list<t2n_procedure> procedures;
609           for (std::list<std::string>::iterator it=xmlfiles.begin();it!=xmlfiles.end();++it) {
610               std::cerr << "Parse " << *it << std::endl;
611               Parser parser(*it);
612               const std::list<t2n_procedure> &p(parser.get_procedures());
613               std::copy(p.begin(), p.end(), std::back_inserter(procedures));
614           }
615
616           std::cerr << "Procedures:" << std::endl;
617           for (std::list<t2n_procedure>::const_iterator it=procedures.begin();it!=procedures.end();++it)
618                std::cerr << *it << ";" << std::endl;
619
620           std::string common_hpp_fname(prefix+"common.hxx");
621           std::string common_cpp_fname(prefix+"common.cpp");
622           std::string client_hpp_fname(prefix+"client.hxx");
623           std::string client_cpp_fname(prefix+"client.cpp");
624           std::string server_hpp_fname(prefix+"server.hxx");
625           std::string server_cpp_fname(prefix+"server.cpp");
626
627           header_file common_hpp(common_hpp_fname.c_str());
628           common_hpp << "// boost serialization is picky about order of include files => we have to include this one first\n"
629                      << "#include \"codegen-stubhead.hxx\"\n"
630                      << "#include \"" << group << ".hxx\"\n";
631
632           output_common_hpp(common_hpp, group, procedures);
633
634           cpp_file common_cpp(common_cpp_fname.c_str());
635           output_common_cpp(common_cpp, group, procedures, common_hpp_fname);
636
637           header_file client_hpp(client_hpp_fname.c_str());
638           client_hpp << "// boost serialization is picky about order of include files => we have to include this one first\n"
639                      << "#include \"codegen-stubhead.hxx\"\n"
640                      << "#include \"" << group << ".hxx\"\n";
641           output_client_hpp(client_hpp, group, procedures);
642
643           cpp_file client_cpp(client_cpp_fname.c_str());
644           output_client_cpp(client_cpp, group, procedures, common_hpp_fname, common_cpp_fname, client_hpp_fname);
645
646           header_file server_hpp(server_hpp_fname.c_str());
647           output_server_hpp(server_hpp, group, procedures, common_hpp_fname);
648
649           cpp_file server_cpp(server_cpp_fname.c_str());
650           output_server_cpp(server_cpp, group, procedures, common_hpp_fname, common_cpp_fname);
651      }catch(const parse_error &e){
652        std::cerr << e.what() << std::endl;
653        return EXIT_FAILURE;
654      }
655      return EXIT_SUCCESS;
656 }