docu updates
[libt2n] / codegen / main.cpp
CommitLineData
0e627fe2
JT
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
d2f31d1e 20/*! \mainpage libt2n - (talk to neighbor)
a930cc99 21 \section intro_sec Introduction
d2f31d1e
JT
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.
a930cc99 26 It then generates the stubs needed.
d2f31d1e
JT
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.
a930cc99
JT
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
d2f31d1e
JT
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
a930cc99
JT
47 \section usage Usage example
48
d2f31d1e
JT
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"
a930cc99
JT
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
d2f31d1e
JT
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.
a930cc99
JT
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
d2f31d1e
JT
72 \par Build and install the package
73 \verbatim
74 autoreconf -f -i && ./configure && make install
75 \endverbatim
76
a930cc99
JT
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
d2f31d1e 81 \par The Makefile.am needs nothing special
a930cc99
JT
82 \verbinclude libt2n-example1-client/Makefile.am
83 \par The client program
84 \verbinclude libt2n-example1-client/client.cpp
d2f31d1e
JT
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
a930cc99
JT
106
107*/
108
109/*!
110 \example t2nexample.cpp
111 example input for libt2n-codegen
112*/
113
e50cc1d6
JT
114#include <libxml++/libxml++.h>
115#include <cassert>
116#include <iostream>
060fbd87
JT
117#include <set>
118#include <fstream>
9d3993e5 119#include <list>
6be7b70f
JT
120#include <stdexcept>
121#include <boost/lexical_cast.hpp>
3907d211
JT
122#ifdef HAVE_CONFIG_H
123#include "config.h"
124#endif
e50cc1d6 125
2f0896ce
JT
126
127//! map group to class name
128std::string
129groupClass(const std::string &group) {
130 return std::string("cmd_group_")+group;
131}
132
52b6f93a 133//! convert string to upper case
e035276b
JT
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
52b6f93a 140//! replace all characters f by r in string s
e035276b
JT
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
52b6f93a
JT
147//! strip prefix from string s
148/*!
149 \return string s without prefix or an empty string on error
150 */
e035276b
JT
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
e50cc1d6
JT
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
6be7b70f
JT
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
e50cc1d6
JT
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
d340d435
JT
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
e035276b
JT
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);}
9d3993e5 223 std::string noref() const {return noref_name.empty() ? name : noref_name;}
e035276b
JT
224};
225
226std::ostream &operator<<(std::ostream &o, const type_info &t) {
227 o << t.name;
228 return o;
229}
230
6be7b70f
JT
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
e50cc1d6
JT
238//! get type by id
239/*!
240 \return type name or empty string on error
241*/
e035276b 242type_info get_type(const xmlpp::Element* root, const std::string &id)
e50cc1d6 243{
e035276b 244 type_info error;
e50cc1d6
JT
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)
060fbd87 251
9d3993e5 252 // TODO: const and reference types handling is a ugly hack
060fbd87 253
e50cc1d6
JT
254 std::string tag(element->get_name());
255 if (tag=="ReferenceType") {
256 assert(element->get_attribute("type"));
e035276b 257 type_info ret(get_type(root, element->get_attribute("type")->get_value()));
e50cc1d6 258 if (ret==error) return error;
e035276b
JT
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;
e50cc1d6
JT
264 }else if (tag=="CvQualifiedType") {
265 assert(element->get_attribute("type"));
e035276b 266 type_info ret(get_type(root, element->get_attribute("type")->get_value()));
e50cc1d6 267 if (ret==error) return error;
e035276b
JT
268 ret.name=std::string("const ")+ret.name;
269 return ret;
6be7b70f
JT
270 }else if (tag=="PointerType") {
271 // not yet supported
272 return error;
e50cc1d6
JT
273 }
274
275 assert(element->get_attribute("name"));
e035276b 276 type_info ret;
fb5c13c6
JT
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 }
9d3993e5 285 ret.name+=element->get_attribute("name")->get_value();
e035276b 286 return ret;
e50cc1d6
JT
287}
288
060fbd87
JT
289struct t2n_procedure
290{
9d3993e5 291 typedef std::list<std::pair<std::string, type_info> > Args;
e50cc1d6 292
e035276b 293 type_info ret_type;
060fbd87 294 std::string name;
52b6f93a 295 std::string mangled;
060fbd87 296 Args args;
e50cc1d6 297
060fbd87 298 std::string ret_classname() const {
52b6f93a 299 return name+mangled+"_res";
060fbd87
JT
300 }
301 std::string cmd_classname() const {
52b6f93a 302 return name+mangled+"_cmd";
060fbd87
JT
303 }
304};
e50cc1d6 305
060fbd87
JT
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}
e50cc1d6 313
060fbd87
JT
314std::ostream &operator<<(std::ostream &o, const t2n_procedure &f) {
315 o << f.ret_type << " " << f.name << "(" << f.args << ")";
316 return o;
317}
e50cc1d6 318
060fbd87
JT
319class Parser
320{
321public:
d340d435 322 Parser(const std::string &fname) : m_fname(fname) {}
e50cc1d6 323
060fbd87
JT
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);
e50cc1d6 336 }
060fbd87 337 return m_procedures;
e50cc1d6 338 }
060fbd87
JT
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");
52b6f93a 349 const xmlpp::Attribute* mangled = element->get_attribute("mangled");
060fbd87 350 const xmlpp::Attribute* returns = element->get_attribute("returns");
52b6f93a 351 if ((!attributes)||(!name)||(!mangled)||(!returns)) return;
060fbd87
JT
352
353 // check wether the procedure is marked (TODO: improve)
354 // attributes are speparated by spaces?
060fbd87 355
060fbd87 356 t2n_procedure f;
d340d435 357 if (extract_group(attributes->get_value()).empty()) return;
e50cc1d6 358
060fbd87
JT
359 // we need the return type
360 f.ret_type=get_type(root, returns->get_value());
361 f.name=name->get_value();
52b6f93a 362 f.mangled=mangled->get_value();
060fbd87
JT
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"));
9d3993e5 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())));
6be7b70f
JT
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 }
060fbd87
JT
381 }
382 }
9d3993e5 383 std::cerr << "Found function: " << f << std::endl;
060fbd87 384 m_procedures.push_back(f);
e50cc1d6 385 }
e50cc1d6 386
060fbd87
JT
387 void visit_node(const xmlpp::Element* root, const xmlpp::Node* node = NULL, unsigned int indentation = 0)
388 {
389 if (!node) node=root;
060fbd87
JT
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);
e50cc1d6 394
060fbd87
JT
395 if(nodeText && nodeText->is_white_space()) //Let's ignore the indenting - you don't always want to do this.
396 return;
e50cc1d6 397
060fbd87 398 std::string nodename = node->get_name();
e50cc1d6 399
060fbd87
JT
400 if(!nodeText && !nodeComment && !nodename.empty()) //Let's not say "name: text".
401 {
52b6f93a 402 if (node->get_name() == "Function") parse_function(root, node);
060fbd87 403 }
060fbd87
JT
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 }
e50cc1d6 413 }
060fbd87
JT
414};
415
d340d435 416void output_common_hpp(std::ostream &o, const std::string &group, const std::list<t2n_procedure> &procs) {
2f0896ce 417 o << "class " << groupClass(group) << " : public libt2n::command\n"
d340d435
JT
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";
060fbd87 425
d340d435
JT
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) {
2f0896ce 445 o << "class " << it->cmd_classname() << " : public " << groupClass(group) << "\n"
d340d435
JT
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"
2f0896ce 455 << " ar & BOOST_SERIALIZATION_BASE_OBJECT_NVP(" << groupClass(group) << ");\n";
d340d435
JT
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 }
060fbd87
JT
481}
482
d340d435 483void output_common_cpp(std::ostream &o, const std::string &group, const std::list<t2n_procedure> &procs, const std::string &common_hpp) {
060fbd87 484 o << "#include \"" << common_hpp << "\"\n"
e035276b 485 << "#include <boost/serialization/export.hpp>\n"
060fbd87
JT
486 << "\n"
487 << "/* register types with boost serialization */\n";
2f0896ce 488 o << "BOOST_CLASS_EXPORT(" << groupClass(group) << ")\n";
060fbd87
JT
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";
e50cc1d6 492 }
060fbd87 493}
e50cc1d6 494
d340d435 495void output_client_hpp(std::ostream &o, const std::string &group, const std::list<t2n_procedure> &procs) {
060fbd87
JT
496 o << "#include <command_client.hxx>\n";
497
2f0896ce 498 o << "class " << groupClass(group) << "_client : public libt2n::command_client\n"
d340d435
JT
499 << "{\n"
500 << "public:\n"
2f0896ce 501 << groupClass(group) << "_client(libt2n::client_connection &_c,\n"
d340d435
JT
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";
060fbd87 508 }
d340d435 509 o << "};\n";
060fbd87 510}
e50cc1d6 511
d340d435 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) {
060fbd87
JT
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
d340d435 520 for (std::list<t2n_procedure>::const_iterator pit=procs.begin();pit!=procs.end();++pit) {
2f0896ce 521 o << pit->ret_type << " " << groupClass(group) << "_client::" << pit->name << "(" << pit->args << ")\n"
d340d435
JT
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";
e50cc1d6 534 }
060fbd87
JT
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
d340d435 542void output_server_hpp(std::ostream &o, const std::string & /* group */, const std::list<t2n_procedure> &procs, const std::string &common_hpp) {
a96ab628
JT
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
d340d435 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) {
060fbd87
JT
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;
e50cc1d6 559 }
060fbd87 560 o << ")); }\n";
e50cc1d6 561 }
060fbd87
JT
562 o << "#include \"" << common_cpp << "\"\n";
563}
564
060fbd87
JT
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),'.','_'));
3907d211 570 *this << "// automatically generated code (generated by libt2n-codegen " << VERSION << ") - do not edit\n" << std::endl;
060fbd87
JT
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[])
e50cc1d6 589{
3907d211
JT
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 }
060fbd87 600
3907d211 601 try{
85106017 602 std::string group(argv[1]);
0cfa3fb2 603 std::list<std::string> xmlfiles;
85106017 604 for (int i=2;i<argc;++i)
0cfa3fb2
JT
605 xmlfiles.push_back(argv[i]);
606
a96ab628 607 std::string prefix=group+"_";
0cfa3fb2
JT
608 std::list<t2n_procedure> procedures;
609 for (std::list<std::string>::iterator it=xmlfiles.begin();it!=xmlfiles.end();++it) {
d340d435
JT
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));
0cfa3fb2 614 }
6be7b70f
JT
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
6be7b70f
JT
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");
a96ab628 624 std::string server_hpp_fname(prefix+"server.hxx");
6be7b70f
JT
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"
85106017
JT
629 << "#include \"codegen-stubhead.hxx\"\n"
630 << "#include \"" << group << ".hxx\"\n";
631
d340d435 632 output_common_hpp(common_hpp, group, procedures);
6be7b70f 633
25924cae 634 cpp_file common_cpp(common_cpp_fname.c_str());
d340d435 635 output_common_cpp(common_cpp, group, procedures, common_hpp_fname);
6be7b70f 636
25924cae
JT
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"
85106017
JT
639 << "#include \"codegen-stubhead.hxx\"\n"
640 << "#include \"" << group << ".hxx\"\n";
d340d435 641 output_client_hpp(client_hpp, group, procedures);
6be7b70f 642
25924cae 643 cpp_file client_cpp(client_cpp_fname.c_str());
d340d435 644 output_client_cpp(client_cpp, group, procedures, common_hpp_fname, common_cpp_fname, client_hpp_fname);
6be7b70f 645
25924cae 646 header_file server_hpp(server_hpp_fname.c_str());
d340d435 647 output_server_hpp(server_hpp, group, procedures, common_hpp_fname);
a96ab628 648
25924cae 649 cpp_file server_cpp(server_cpp_fname.c_str());
d340d435 650 output_server_cpp(server_cpp, group, procedures, common_hpp_fname, common_cpp_fname);
6be7b70f 651 }catch(const parse_error &e){
25924cae
JT
652 std::cerr << e.what() << std::endl;
653 return EXIT_FAILURE;
6be7b70f
JT
654 }
655 return EXIT_SUCCESS;
e50cc1d6 656}