fix8  version 1.4.0
Open Source C++ FIX Framework
f8cutils.cpp
Go to the documentation of this file.
1 //-----------------------------------------------------------------------------------------
2 /*
3 
4 Fix8 is released under the GNU LESSER GENERAL PUBLIC LICENSE Version 3.
5 
6 Fix8 Open Source FIX Engine.
7 Copyright (C) 2010-16 David L. Dight <fix@fix8.org>
8 
9 Fix8 is free software: you can redistribute it and / or modify it under the terms of the
10 GNU Lesser General Public License as published by the Free Software Foundation, either
11 version 3 of the License, or (at your option) any later version.
12 
13 Fix8 is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without
14 even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
15 
16 You should have received a copy of the GNU Lesser General Public License along with Fix8.
17 If not, see <http://www.gnu.org/licenses/>.
18 
19 BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY FOR THE PROGRAM, TO
20 THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE
21 COPYRIGHT HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY
22 KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
23 WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS TO
24 THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE,
25 YOU ASSUME THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION.
26 
27 IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING WILL ANY COPYRIGHT
28 HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR REDISTRIBUTE THE PROGRAM AS PERMITTED
29 ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR
30 CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT
31 NOT LIMITED TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR
32 THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS), EVEN IF SUCH
33 HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES.
34 
35 */
36 //-----------------------------------------------------------------------------------------
37 #include "precomp.hpp"
38 // f8 headers
39 #include <fix8/f8includes.hpp>
40 #include <fix8/usage.hpp>
41 #include <f8c.hpp>
42 
43 //-----------------------------------------------------------------------------------------
44 using namespace std;
45 using namespace FIX8;
46 
47 //-----------------------------------------------------------------------------------------
48 extern string shortName, odir, prefix;
49 extern bool verbose, nocheck, nowarn, incpath;
50 extern string spacer, precompHdr;
51 extern const string GETARGLIST;
52 extern unsigned glob_errors, glob_warnings;
53 
54 //-----------------------------------------------------------------------------------------
55 void print_usage();
56 int process(XmlElement& xf, Ctxt& ctxt);
57 int load_fix_version (XmlElement& xf, Ctxt& ctxt);
58 int load_fields(XmlElement& xf, FieldSpecMap& fspec);
59 void process_special_traits(const unsigned short field, FieldTraits& fts);
60 int process_message_fields(const std::string& where, XmlElement *xt, FieldTraits& fts,
61  const FieldToNumMap& ftonSpec, FieldSpecMap& fspec, const Components& compon);
62 int load_messages(XmlElement& xf, MessageSpecMap& mspec, const FieldToNumMap& ftonSpec, FieldSpecMap& fspec);
63 void process_ordering(MessageSpecMap& mspec);
64 void process_value_enums(FieldSpecMap::const_iterator itr, ostream& ost_hpp, ostream& ost_cpp);
65 const string& mkel(const string& base, const string& compon, string& where);
67 unsigned lookup_component(const Components& compon, const f8String& name);
68 string bintoaschex(const string& from);
69 
70 //-----------------------------------------------------------------------------------------
71 namespace
72 {
73  const string ident_set("abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ_0123456789");
74 };
75 
76 //-----------------------------------------------------------------------------------------
77 ostream *open_ofile(const string& odir, const string& fname, string& target)
78 {
79  if (!exist(odir))
80  return nullptr;
81  ostringstream ofs;
82  string odirect(odir);
83  ofs << CheckAddTrailingSlash(odirect) << fname;
84  target = ofs.str();
85  unique_ptr<ofstream> os(new ofstream(target.c_str()));
86  if (!*os)
87  {
88  cerr << "Error opening file \'" << target << '\'';
89  if (errno)
90  cerr << " (" << strerror(errno) << ')';
91  cerr << endl;
92 
93  return nullptr;
94  }
95 
96  return os.release();
97 }
98 
99 //-----------------------------------------------------------------------------------------
100 const string& mkel(const string& base, const string& compon, string& where)
101 {
102  ostringstream ostr;
103  ostr << base << '/' << compon;
104  return where = ostr.str();
105 }
106 
107 //-----------------------------------------------------------------------------------------
108 const string& filepart(const string& source, string& where)
109 {
110  string::size_type pos(source.find_last_of('/'));
111  return pos == string::npos ? where = source : where = source.substr(pos + 1);
112 }
113 
114 //-----------------------------------------------------------------------------------------
116 {
117  const XmlElement *fix(xf.find("fix"));
118  if (!fix)
119  {
120  cerr << "No fix header element found in " << shortName << endl;
121  return -1;
122  }
123 
124  string major, minor, revision("0"), type("FIX");
125 
126  if (!fix->GetAttr("major", major) || !fix->GetAttr("minor", minor))
127  {
128  cerr << "Missing required attributes (major/minor) from fix header in " << shortName << endl;
129  return -1;
130  }
131 
132  if (!fix->GetAttr("revision", revision))
133  fix->GetAttr("servicepack", revision);
134  fix->GetAttr("type", type);
135 
136  // fix version: <Major:1><Minor:1><Revision:2> eg. 4.2r10 is 4210
137  ctxt._version = stoi(major) * 1000 + stoi(minor) * 100 + stoi(revision);
138  if (type == "FIX" && ctxt._version < 4000)
139  {
140  cerr << "Unsupported FIX version " << ctxt._version << " from fix header in " << shortName << endl;
141  return -1;
142  }
143 
144  ostringstream ostr;
145  ostr << type << ctxt._version;
146  ctxt._systemns = ostr.str();
147  if (ctxt._fixns.empty())
148  ctxt._fixns = ctxt._systemns;
149  ctxt._clname = prefix;
150 
151  ostr.str("");
152  ostr << type << '.' << major << '.' << minor;
153  ctxt._beginstr = ostr.str();
154 
155  return 0;
156 }
157 
158 //-----------------------------------------------------------------------------------------
159 void process_special_traits(const unsigned short field, FieldTraits& fts)
160 {
161  switch(field)
162  {
163  case Common_BeginString:
164  case Common_BodyLength:
165  case Common_CheckSum:
166  fts.set(field, FieldTrait::suppress); // drop through
167  case Common_MsgType:
168  fts.set(field, FieldTrait::automatic);
169  fts.clear(field, FieldTrait::mandatory); // don't check for presence
170  default:
171  break;
172  }
173 }
174 
175 //-----------------------------------------------------------------------------------------
176 void process_value_enums(FieldSpecMap::const_iterator itr, ostream& ost_hpp, ostream& ost_cpp)
177 {
178  string typestr;
179  if (FieldTrait::get_type_string(itr->second._ftype, typestr).empty())
180  return;
181  typestr.insert(0, "const ");
182  typestr += ' ';
183 
184  ost_cpp << typestr << itr->second._name << "_realm[] " << endl << spacer << "{ ";
185  unsigned cnt(0);
186  for (RealmMap::const_iterator ditr(itr->second._dvals->begin()); ditr != itr->second._dvals->end(); ++ditr)
187  {
188  if (cnt)
189  ost_cpp << ", ";
190  ost_cpp << *ditr->first;
191  string transdesc(ditr->second);
192  // replace any illegal c++ identifier characters
193  InPlaceReplaceInSet(ident_set, transdesc, '_');
194  ost_hpp << typestr << itr->second._name << '_';
195  if (ditr->first->is_range())
196  ost_hpp << (cnt == 0 ? "lower" : "upper");
197  else if (transdesc.empty())
198  ost_hpp << *ditr->first;
199  else
200  ost_hpp << transdesc;
201  ost_hpp << '(' << *ditr->first << ");" << endl;
202  ++cnt;
203  }
204  ost_hpp << "const size_t " << itr->second._name << "_realm_els(" << itr->second._dvals->size() << ");" << endl;
205  ost_cpp << " };" << endl;
206 
207  ost_cpp << "const char *" << itr->second._name << "_descriptions[] " << endl << spacer << "{ ";
208  cnt = 0;
209  for (RealmMap::const_iterator ditr(itr->second._dvals->begin()); ditr != itr->second._dvals->end(); ++ditr)
210  {
211  if (cnt)
212  ost_cpp << ", ";
213  ost_cpp << '"' << ditr->second << '"';
214  ++cnt;
215  }
216  ost_cpp << " };" << endl;
217 }
218 
219 //-----------------------------------------------------------------------------------------
220 int process_message_fields(const std::string& where, const XmlElement *xt, FieldTraits& fts, const FieldToNumMap& ftonSpec,
221  FieldSpecMap& fspec, const Components& compon)
222 {
223  unsigned processed(0);
224  XmlElement::XmlSet flist;
225  if (xt->find(where, flist))
226  {
227  for(const auto *pp : flist)
228  {
229  string fname, required;
230  if (pp->GetAttr("name", fname) && pp->GetAttr("required", required))
231  {
232  FieldToNumMap::const_iterator ftonItr(ftonSpec.find(fname));
233  FieldSpecMap::iterator fs_itr;
234  if (ftonItr == ftonSpec.end() || (fs_itr = fspec.find(ftonItr->second)) == fspec.end())
235  {
236  cerr << shortName << ':' << recover_line(*pp) << ": error: Field element missing required attributes" << endl;
237  ++glob_errors;
238  continue;
239  }
240 
241  string compname;
242  unsigned compidx(pp->GetAttr("component", compname) ? lookup_component(compon, compname) : 0);
243 
244  // add FieldTrait
245  if (!fts.add(FieldTrait(fs_itr->first, fs_itr->second._ftype, pp->GetSubIdx(), required == "Y", false, compidx)))
246  {
247  if (!nowarn)
248  cerr << shortName << ':' << recover_line(*pp) << ": warning: Could not add trait object '" << fname << "' (duplicate ?)" << endl;
249  ++glob_warnings;
250  }
251  else
252  {
253  process_special_traits(fs_itr->first, fts);
254  ++processed;
255  fs_itr->second.set_used();
256  }
257  }
258  else
259  {
260  cerr << shortName << ':' << recover_line(*pp) << ": error: Field element missing required attributes" << endl;
261  ++glob_errors;
262  }
263  }
264  }
265 
266  return processed;
267 }
268 
269 //-----------------------------------------------------------------------------------------
270 string bintoaschex(const string& from)
271 {
272  ostringstream result;
273  for (const auto& cc : from)
274  result << uppercase << hex << setw(2) << setfill('0') << static_cast<unsigned short>(cc);
275  return "FIX8_" + result.str() + '_';
276 }
277 
278 //-----------------------------------------------------------------------------------------
280 {
281  for (const auto& pp : mspec)
282  {
283  FieldTraitOrder mo;
284  for (const auto& ii : pp.second._fields.get_presence())
285  mo.insert({&ii});
286 
287  unsigned cnt(0);
288  for (auto *ii : mo)
289  ii->_pos = ++cnt;
290  }
291 }
292 
293 //-----------------------------------------------------------------------------------------
295 {
296  for (const auto& pp : gm)
297  {
298  FieldTraitOrder go;
299  for (const auto& ii : pp.second._fields.get_presence())
300  go.insert({&ii});
301 
302  unsigned gcnt(0);
303  for (auto *ii : go)
304  ii->_pos = ++gcnt;
305 
306  if (!pp.second._groups.empty())
307  process_message_group_ordering(pp.second._groups);
308  }
309 }
310 
311 //-----------------------------------------------------------------------------------------
313 {
314  for (const auto& pp : globmap)
315  for (const auto& ii : pp.second)
316  process_message_group_ordering(ii.second._groups);
317 }
318 
319 //-----------------------------------------------------------------------------------------
321 {
322  UsageMan um("f8c", GETARGLIST, "<input xml schema>");
323  um.setdesc("f8c -- compile FIX xml schema");
324  um.add('o', "odir <dir>", "output target directory (default ./)");
325  um.add('p', "prefix <prefix>", "output filename prefix (default Myfix)");
326  um.add('H', "pch <filename>", "use specified precompiled header name for Windows (default none)");
327  um.add('d', "dump", "dump 1st pass parsed source xml file, exit");
328  um.add('e', "extension", "Generate with .cxx/.hxx extensions (default .cpp/.hpp)");
329  um.add('f', "fields", "generate code for all defined fields even if they are not used in any message (default no)");
330  um.add('F', "xfields", "specify additional fields with associated messages (see documentation for details)");
331  um.add('h', "help", "help, this screen");
332  um.add('i', "ignore", "ignore errors, attempt to generate code anyhow (default no)");
333  um.add('k', "keep", "retain generated temporaries even if there are errors (.*.tmp)");
334  um.add('v', "version", "print version, exit");
335  um.add('I', "info", "print package info, exit");
336  um.add('s', "second", "2nd pass only, no precompile (default both)");
337  um.add('S', "noshared", "Treat every group as unique and expose all static traits. Do not share metadata in message classes (default shared)");
338  um.add('N', "nounique", "do not enforce unique field parsing (default false)");
339  um.add('R', "norealm", "do not generate realm constructed field instantiators (default false)");
340  um.add('W', "nowarn", "suppress warning messages (default false)");
341  um.add('C', "nocheck", "do not embed version checking in generated code (default false)");
342  um.add('D', "defaulted", "do not generate default router bodies. Application must provide all router definitions (default false)");
343  um.add('U', "noconst", "Generate non-const Router method declarations (default false, const)");
344  um.add('u', "unused", "Report unused fields, requires verbose option (default false)");
345  um.add('r', "retain", "retain 1st pass code (default delete)");
346  um.add('b', "binary", "print binary/ABI details, exit");
347  um.add('P', "incpath", "prefix system include path with \"fix8\" in generated compilation units (default yes)");
348  um.add('c', "classes <server|client>", "generate user session classes (default neither)");
349  um.add('t', "tabwidth", "tabwidth for generated code (default 3 spaces)");
350  um.add('x', "fixt <file>", "For FIXT hosted transports or for FIX5.0 and above, the input FIXT schema file");
351  um.add('V', "verbose", "be more verbose when processing");
352  um.add('n', "namespace <ns>", "namespace to place generated code in (default FIXMmvv e.g. FIX4400)");
353  um.add("e.g.");
354  um.add("@f8c -p Texfix -n TEX myfix.xml");
355  um.add("@f8c -rp Texfix -n TEX -x ../schema/FIXT11.xml myfix.xml");
356  um.add("@f8c -p Texfix -n TEX -c client -x ../schema/FIXT11.xml myfix.xml");
357  um.add("@f8c -p Texfix -n TEX -c client -x ../schema/FIXT11.xml myfix.xml -F \"<field number='9999' name='SampleUserField' type='STRING' messages='NewOrderSingle:Y ExecutionReport:Y OrderCancelRequest:N' />");
358  um.print(cerr);
359 }
360 
361 //-------------------------------------------------------------------------------------------------
362 RealmObject *RealmObject::create(const string& from, FieldTrait::FieldType ftype, bool isRange)
363 {
364  if (FieldTrait::is_int(ftype))
365  return new TypedRealm<int>(stoi(from), isRange);
366  if (FieldTrait::is_char(ftype))
367  return new CharRealm(from[0], isRange);
368  if (FieldTrait::is_float(ftype))
369  return new TypedRealm<fp_type>(get_value<fp_type>(from), isRange);
370  if (FieldTrait::is_string(ftype))
371  return new StringRealm(from, isRange);
372  return nullptr;
373 }
374 
375 //-------------------------------------------------------------------------------------------------
376 string insert_year()
377 {
378  struct tm *ptim;
379  time_t now(time(0));
380 #ifdef _MSC_VER
381  ptim = localtime (&now);
382 #else
383  struct tm tim;
384  localtime_r(&now, &tim);
385  ptim = &tim;
386 #endif
387 
388  ostringstream ostr;
389  ostr << setw(2) << (ptim->tm_year - 100);
390  return ostr.str();
391 }
392 
393 //-------------------------------------------------------------------------------------------------
394 void generate_includes(ostream& to)
395 {
396  static const vector<string> incfiles
397  {
398  //"f8includes.hpp"
399  "f8exception.hpp",
400  "hypersleep.hpp",
401  "mpmc.hpp",
402  "thread.hpp",
403  "f8types.hpp",
404  "f8utils.hpp",
405  "tickval.hpp",
406  "logger.hpp",
407  "traits.hpp",
408  "field.hpp",
409  "message.hpp"
410  };
411 
412  to << "// f8 includes" << endl;
413  for (const auto& pp : incfiles)
414  to << "#include " << (incpath ? "<fix8/" : "<") << pp << '>' << endl;
415 }
416 
417 //-------------------------------------------------------------------------------------------------
418 ostream& FIX8::operator<<(ostream& os, const MessageSpec& what)
419 {
420  os << "Name:" << what._name;
421  if (!what._description.empty())
422  os << " Description:" << what._description;
423  if (!what._comment.empty())
424  os << " Comment:" << what._comment;
425  os << " isadmin:" << boolalpha << what._is_admin << endl;
426  os << "Fields:" << endl << what._fields;
427  for (const auto& pp : what._groups)
428  os << "Group (" << pp.first << "): " << endl << pp.second << endl;
429 
430  return os;
431 }
432 
FieldTraits _fields
Definition: f8c.hpp:157
bool nocheck
f8c compilation context.
Definition: f8c.hpp:45
std::string _systemns
Definition: f8c.hpp:53
const unsigned short Common_CheckSum(10)
void clear(const unsigned short field, Presence::const_iterator &itr, FieldTrait::TraitTypes type=FieldTrait::present)
Definition: traits.hpp:550
const string & mkel(const string &base, const string &compon, string &where)
Definition: f8cutils.cpp:100
bool nowarn
string prefix
F8API std::string & InPlaceReplaceInSet(const std::string &iset, std::string &src, const char repl='_')
std::map< std::string, const XmlElement * > Components
Definition: f8c.hpp:183
bool add(const char sw, const std::string &lsw, const std::string &help)
Definition: usage.hpp:73
string spacer
Definition: f8c.cpp:96
std::string _name
Definition: f8c.hpp:160
f8c internal message representation.
Definition: f8c.hpp:155
bool incpath
string precompHdr
Definition: f8c.cpp:96
std::map< unsigned, struct MessageSpec > GroupMap
Definition: f8c.hpp:120
string bintoaschex(const string &from)
Definition: f8cutils.cpp:270
void print_usage()
Definition: f8cutils.cpp:320
const string & filepart(const string &source, string &where)
Definition: f8cutils.cpp:108
std::ostream & operator<<(std::ostream &os, const GroupBase &what)
Definition: message.hpp:1373
ostream * open_ofile(const string &odir, const string &fname, string &target)
Definition: f8cutils.cpp:77
std::string _fixns
Definition: f8c.hpp:53
int recover_line(const XmlElement &xf)
Definition: f8c.hpp:204
std::string _comment
Definition: f8c.hpp:160
unsigned lookup_component(const Components &compon, const f8String &name)
Definition: f8c.cpp:1528
void process_special_traits(const unsigned short field, FieldTraits &fts)
Definition: f8cutils.cpp:159
f8c character realm type.
Definition: f8c.hpp:109
std::string _clname
Definition: f8c.hpp:53
Used for static trait interrogation.
Definition: traits.hpp:57
void generate_includes(ostream &to)
Definition: f8cutils.cpp:394
int load_messages(XmlElement &xf, MessageSpecMap &mspec, const FieldToNumMap &ftonSpec, FieldSpecMap &fspec)
std::map< unsigned, struct FieldSpec > FieldSpecMap
Definition: f8c.hpp:118
void setdesc(const std::string &desc)
Definition: usage.hpp:91
A simple xml parser with Xpath style lookup.
Definition: xml.hpp:48
const string GETARGLIST
void print(std::ostream &os) const
Definition: usage.hpp:95
f8c range or set domain realm.
Definition: f8c.hpp:58
int process(XmlElement &xf, Ctxt &ctxt)
Definition: f8c.cpp:928
std::multiset< const FieldTrait *, FieldTrait::PosCompare > FieldTraitOrder
Definition: f8c.hpp:176
const unsigned short Common_MsgType(35)
void process_value_enums(FieldSpecMap::const_iterator itr, ostream &ost_hpp, ostream &ost_cpp)
Definition: f8cutils.cpp:176
std::set< const XmlElement *, EntityOrderComp > XmlSet
Definition: xml.hpp:74
std::map< std::string, unsigned > FieldToNumMap
Definition: f8c.hpp:119
const unsigned short Common_BodyLength(9)
f8c string realm type.
Definition: f8c.hpp:102
std::map< const std::string, MessageSpec > MessageSpecMap
Definition: f8c.hpp:175
string odir
string shortName
Definition: f8c.cpp:96
bool verbose
unsigned glob_errors
GroupMap _groups
Definition: f8c.hpp:158
const unsigned short Common_BeginString(8)
F8API std::string & CheckAddTrailingSlash(std::string &source)
f8c typed realm.
Definition: f8c.hpp:89
std::map< unsigned, CommonGroups > CommonGroupMap
Definition: f8c.hpp:180
bool add(const FieldTrait &what)
Definition: traits.hpp:575
A collection of FieldTraits for a message. Which fields are required, which are present.
Definition: traits.hpp:437
unsigned _version
Definition: f8c.hpp:52
std::string _description
Definition: f8c.hpp:160
bool _is_admin
Definition: f8c.hpp:161
int load_fix_version(XmlElement &xf, Ctxt &ctxt)
Definition: f8cutils.cpp:115
string insert_year()
Definition: f8cutils.cpp:376
int load_fields(XmlElement &xf, FieldSpecMap &fspec)
Definition: f8c.cpp:453
bool exist(const std::string &fname)
Definition: f8utils.hpp:1068
F8API const XmlElement * find(const std::string &what, const std::string *atag=nullptr, const std::string *aval=nullptr, const char delim='/') const
int process_message_fields(const std::string &where, XmlElement *xt, FieldTraits &fts, const FieldToNumMap &ftonSpec, FieldSpecMap &fspec, const Components &compon)
std::string f8String
Definition: f8types.hpp:47
Convenient program help/usage wrapper. Generates a standardised usage message.
Definition: usage.hpp:42
void set(const unsigned short field, Presence::const_iterator &itr, FieldTrait::TraitTypes type)
Definition: traits.hpp:524
std::string _beginstr
Definition: f8c.hpp:53
void process_ordering(MessageSpecMap &mspec)
Definition: f8cutils.cpp:279
void process_message_group_ordering(const GroupMap &gm)
Definition: f8cutils.cpp:294
unsigned glob_warnings
void process_group_ordering(const CommonGroupMap &gm)
Definition: f8cutils.cpp:312