docu updates
[libt2n] / codegen / main.cpp
... / ...
CommitLineData
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
95socket: cannot open `socket' (No such file or directory)
96$ libt2n-example1-server &
97[1] 7711
98$ file socket
99socket: socket
100$ libt2n-example1-client && echo ok
101ok
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
128std::string
129groupClass(const std::string &group) {
130 return std::string("cmd_group_")+group;
131}
132
133//! convert string to upper case
134std::string
135toupper(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
141std::string
142replace(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 */
151std::string
152strip(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*/
164const 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
182std::string
183get_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*/
195std::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
204std::string
205extract_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
218struct 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
226std::ostream &operator<<(std::ostream &o, const type_info &t) {
227 o << t.name;
228 return o;
229}
230
231struct 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*/
242type_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
289struct 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
306std::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
314std::ostream &operator<<(std::ostream &o, const t2n_procedure &f) {
315 o << f.ret_type << " " << f.name << "(" << f.args << ")";
316 return o;
317}
318
319class Parser
320{
321public:
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 }
339protected:
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
416void 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
483void 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
495void 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
512void 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
542void 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
550void 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
565struct 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
579struct 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
587int
588main(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}