24#ifndef GenUnitsClassPlugin20150310H
25#define GenUnitsClassPlugin20150310H
27#include <google/protobuf/descriptor.h>
29#include <boost/config/warning_disable.hpp>
30#include <boost/lambda/lambda.hpp>
32#include <boost/phoenix/bind.hpp>
33#include <boost/phoenix/core.hpp>
34#include <boost/phoenix/operator.hpp>
35#include <boost/phoenix/stl.hpp>
37#include <boost/spirit/include/qi.hpp>
38#include <boost/spirit/include/qi_expect.hpp>
39#include <boost/spirit/include/qi_lit.hpp>
41#include <boost/algorithm/string/replace.hpp>
49#include <boost/bimap.hpp>
60namespace qi = boost::spirit::qi;
61namespace ascii = boost::spirit::ascii;
62namespace phoenix = boost::phoenix;
65inline boost::bimap<std::string, char> make_dim_bimap()
67 typedef boost::bimap<std::string, char> dim_bimap;
68 using dimension = dim_bimap::value_type;
71 dims.insert(dimension(
"length",
'L'));
72 dims.insert(dimension(
"time",
'T'));
73 dims.insert(dimension(
"mass",
'M'));
74 dims.insert(dimension(
"plane_angle",
'A'));
75 dims.insert(dimension(
"solid_angle",
'S'));
76 dims.insert(dimension(
"current",
'I'));
77 dims.insert(dimension(
"temperature",
'K'));
78 dims.insert(dimension(
"amount",
'N'));
79 dims.insert(dimension(
"luminous_intensity",
'J'));
80 dims.insert(dimension(
"information",
'B'));
81 dims.insert(dimension(
"dimensionless",
'-'));
87inline void push_char_base(std::vector<std::string>& vc, std::vector<std::string>& vs,
90 vc.emplace_back(1, c);
92 using bimap_type = boost::bimap<std::string, char>;
93 bimap_type dim_bimap = make_dim_bimap();
95 bimap_type::right_const_iterator it_right = dim_bimap.right.find(c);
97 vs.push_back(it_right->second);
101inline void push_string_base(std::vector<std::string>& vc, std::vector<std::string>& vs,
102 const std::string& s)
106 using bimap_type = boost::bimap<std::string, char>;
107 bimap_type dim_bimap = make_dim_bimap();
109 bimap_type::left_const_iterator it_left = dim_bimap.left.find(s);
111 vc.emplace_back(1, it_left->second);
115inline void push_char(std::vector<std::string>& vc,
const char& c) { vc.emplace_back(1, c); }
118inline void push_char_vec(std::vector<std::string>& vc,
const std::vector<char>& c)
120 vc.emplace_back(c.begin(), c.end());
127inline std::set<std::string> get_system_supported_base_dimensions(
const std::string& sysname)
129 if (sysname ==
"angle::degree" || sysname ==
"degree" || sysname ==
"angle::gradian" ||
130 sysname ==
"gradian" || sysname ==
"angle::revolution" || sysname ==
"revolution")
131 return {
"plane_angle",
"dimensionless"};
132 else if (sysname ==
"temperature::celsius" || sysname ==
"celsius" ||
133 sysname ==
"temperature::fahrenheit" || sysname ==
"fahrenheit")
134 return {
"temperature",
"dimensionless"};
147inline void validate_dimensions_for_system(
const std::vector<std::string>& dim_names,
148 const std::string& sysname)
150 const auto supported = get_system_supported_base_dimensions(sysname);
151 if (supported.empty())
154 const auto bimap = make_dim_bimap();
156 for (
const auto& dim : dim_names)
158 const bool is_base_dim = bimap.left.find(dim) != bimap.left.end();
164 throw std::runtime_error(
165 "Dimension '" + dim +
166 "' is a compound Boost Units dimension that requires multiple base "
167 "dimensions, but system '" + sysname +
168 "' only supports single-dimension units. Use a "
169 "general system such as \"si\" or define a new system that includes the appropriate dimensions.");
171 else if (supported.find(dim) == supported.end())
174 std::string supported_list;
175 for (
const auto& s : supported)
177 if (!supported_list.empty())
178 supported_list +=
", ";
179 supported_list +=
"'" + s +
"'";
181 throw std::runtime_error(
"Dimension '" + dim +
"' is not supported by system '" +
182 sysname +
"'. That system only supports: " + supported_list +
189template <
typename Iterator>
190bool parse_base_dimensions(Iterator first, Iterator last, std::vector<double>& base_dim_powers,
191 std::vector<std::string>& base_dim_chars,
192 std::vector<std::string>& base_dim_strings)
199 using phoenix::push_back;
204 using qi::phrase_parse;
208 bool r = phrase_parse(
211 +((char_(
"LTMASIKNJB-")[phoenix::bind(&push_char_base, phoenix::ref(base_dim_chars),
212 phoenix::ref(base_dim_strings), _1)] |
213 ((ascii::string(
"length") | ascii::string(
"time") | ascii::string(
"mass") |
214 ascii::string(
"plane_angle") | ascii::string(
"solid_angle") |
215 ascii::string(
"current") | ascii::string(
"temperature") | ascii::string(
"amount") |
216 ascii::string(
"luminous_intensity") | ascii::string(
"information") |
217 ascii::string(
"dimensionless"))[phoenix::bind(
218 &push_string_base, phoenix::ref(base_dim_chars), phoenix::ref(base_dim_strings),
220 -(ascii::string(
"_base_dimension")) >>
221 ((
'^' > double_[push_back(phoenix::ref(base_dim_powers), _1)]) |
222 eps[push_back(phoenix::ref(base_dim_powers), 1)])),
230 catch (
const std::runtime_error& e)
237template <
typename Iterator>
238bool parse_derived_dimensions(Iterator first, Iterator last,
239 std::vector<std::string>& derived_dim_operators,
240 std::vector<std::string>& derived_dim_strings)
243 using phoenix::push_back;
248 using qi::phrase_parse;
252 std::vector<std::string> params;
253 bool r = boost::spirit::qi::parse(
255 +((+char_(
"a-z1_"))[phoenix::bind(&push_char_vec, boost::phoenix::ref(params), _1)] >>
257 (char_(
"*/")[phoenix::bind(&push_char, phoenix::ref(derived_dim_operators), _1)] |
258 eps[push_back(phoenix::ref(derived_dim_operators), std::string(
"*"))]) >>
261 if (derived_dim_operators.size())
262 derived_dim_operators.pop_back();
264 for (
auto& param : params)
266 std::string::size_type dim_pos = param.find(
"_dimension");
267 if (dim_pos != std::string::npos)
268 param = param.substr(0, dim_pos);
270 derived_dim_strings.push_back(param);
277 catch (
const std::runtime_error& e)
284inline std::string get_field_type_name(google::protobuf::FieldDescriptor::CppType type)
288 case google::protobuf::FieldDescriptor::CPPTYPE_INT32:
return "google::protobuf::int32";
289 case google::protobuf::FieldDescriptor::CPPTYPE_INT64:
return "google::protobuf::int64";
290 case google::protobuf::FieldDescriptor::CPPTYPE_UINT32:
return "google::protobuf::uint32";
291 case google::protobuf::FieldDescriptor::CPPTYPE_UINT64:
return "google::protobuf::uint64";
292 case google::protobuf::FieldDescriptor::CPPTYPE_DOUBLE:
return "double";
293 case google::protobuf::FieldDescriptor::CPPTYPE_FLOAT:
return "float";
294 default:
return "double";
315inline void include_units_headers(
const std::string& sysname, std::ostream& os)
325 if (sysname ==
"si" || sysname ==
"boost::units::si" || sysname ==
"angle::radian")
327 os <<
"#include <boost/units/systems/si.hpp>" << std::endl;
329 else if (sysname ==
"cgs" || sysname ==
"boost::units::cgs")
331 os <<
"#include <boost/units/systems/cgs.hpp>" << std::endl;
333 else if (sysname ==
"celsius" || sysname ==
"boost::units::celsius" ||
334 sysname ==
"temperature::celsius")
336 os <<
"#include <boost/units/systems/temperature/celsius.hpp>" << std::endl;
338 else if (sysname ==
"fahrenheit" || sysname ==
"boost::units::fahrenheit" ||
339 sysname ==
"temperature::fahrenheit")
341 os <<
"#include <boost/units/systems/temperature/fahrenheit.hpp>" << std::endl;
343 else if (sysname ==
"degree" || sysname ==
"boost::units::degree" || sysname ==
"angle::degree")
345 os <<
"#include <boost/units/systems/angle/degrees.hpp>" << std::endl;
347 else if (sysname ==
"gradian" || sysname ==
"boost::units::gradian" ||
348 sysname ==
"angle::gradian")
350 os <<
"#include <boost/units/systems/angle/gradians.hpp>" << std::endl;
352 else if (sysname ==
"revolution" || sysname ==
"boost::units::revolution" ||
353 sysname ==
"angle::revolution")
355 os <<
"#include <boost/units/systems/angle/revolutions.hpp>" << std::endl;
360 std::string sysname_sub = boost::replace_all_copy(sysname,
"::",
"/");
361 os <<
"#include \"" << sysname_sub <<
".hpp\"" << std::endl;
368inline void include_base_unit_headers(
const std::string& base_unit_category_and_name,
372 std::string cat_name_sub = boost::replace_all_copy(base_unit_category_and_name,
"::",
"/");
373 os <<
"#include <boost/units/base_units/" << cat_name_sub <<
".hpp>" << std::endl;
376inline void include_custom_unit_headers(
const std::string& header, std::ostream& os)
379 os <<
"#include \"" << header <<
"\"" << std::endl;
382inline void add_absolute(std::string& before, std::string& after)
384 before =
"boost::units::absolute<" + before;
388inline void add_prefix(
const std::string& prefix, std::string& before, std::string& after)
391 if (prefix ==
"yotta")
393 else if (prefix ==
"zetta")
395 else if (prefix ==
"exa")
397 else if (prefix ==
"peta")
399 else if (prefix ==
"tera")
401 else if (prefix ==
"giga")
403 else if (prefix ==
"mega")
405 else if (prefix ==
"kilo")
407 else if (prefix ==
"hecto")
409 else if (prefix ==
"deka")
411 else if (prefix ==
"deci")
413 else if (prefix ==
"centi")
415 else if (prefix ==
"milli")
417 else if (prefix ==
"micro")
419 else if (prefix ==
"nano")
421 else if (prefix ==
"pico")
423 else if (prefix ==
"femto")
425 else if (prefix ==
"atto")
427 else if (prefix ==
"zepto")
429 else if (prefix ==
"yocto")
432 throw(std::runtime_error(std::string(
"Invalid SI prefix: " + prefix)));
434 std::stringstream power_ss;
437 before =
"boost::units::make_scaled_unit<" + before;
438 after +=
", boost::units::scale<10, boost::units::static_rational<" + power_ss.str() +
443inline void construct_units_typedef_from_dimension(
const std::string& fieldname,
444 const std::string& sysname,
const bool& absolute,
445 const std::string& prefix, std::ostream& os)
448 std::string sysname_ns;
449 if (sysname ==
"si" || sysname ==
"cgs" || sysname ==
"celsius" || sysname ==
"fahrenheit" ||
450 sysname ==
"degree" || sysname ==
"gradian" || sysname ==
"revolution")
451 sysname_ns =
"boost::units::" + sysname +
"::system";
452 else if (sysname ==
"boost::units::si" || sysname ==
"boost::units::cgs")
453 sysname_ns = sysname +
"::system";
454 else if (sysname ==
"temperature::celsius" || sysname ==
"temperature::fahrenheit")
455 sysname_ns = boost::replace_all_copy(sysname,
"temperature::",
"boost::units::");
456 else if (sysname ==
"angle::degree" || sysname ==
"angle::gradian" ||
457 sysname ==
"angle::revolution")
458 sysname_ns = boost::replace_all_copy(sysname,
"angle::",
"boost::units::") +
"::system";
459 else if (sysname ==
"angle::radian")
460 sysname_ns =
"boost::units::si::system";
462 sysname_ns = sysname;
467 if (absolute && !prefix.empty())
468 throw(std::runtime_error(
469 std::string(
"'prefix' is not supported with an absolute temperature field")));
472 add_absolute(before, after);
475 add_prefix(prefix, before, after);
478 os <<
"typedef " << before <<
"boost::units::unit<" << fieldname <<
"_dimension," << sysname_ns
479 <<
"> " << after << fieldname <<
"_unit;" << std::endl;
484inline void construct_base_dims_typedef(
const std::vector<std::string>& dim_vec,
485 const std::vector<double>& power_vec,
486 const std::string& fieldname,
const std::string& sysname,
487 const bool& rel_temperature,
const std::string& prefix,
491 bool temperature_dimension =
false;
492 if (dim_vec[0] ==
"temperature" && dim_vec.size() == 1)
493 temperature_dimension =
true;
494 if (dim_vec[0] ==
"dimensionless" && dim_vec.size() == 1)
496 os <<
"typedef boost::units::dimensionless_type " << fieldname <<
"_dimension;"
502 os <<
"typedef boost::units::derived_dimension< ";
503 for (std::size_t i = 0, n = dim_vec.size(); i < n; i++)
505 os <<
"boost::units::" << dim_vec[i] <<
"_base_dimension," << power_vec[i];
506 if (i != dim_vec.size() - 1)
509 os <<
" >::type " << fieldname <<
"_dimension;" << std::endl;
513 construct_units_typedef_from_dimension(fieldname, sysname,
514 temperature_dimension && !rel_temperature, prefix, os);
518inline void construct_derived_dims_typedef(
const std::vector<std::string>& dim_vec,
519 const std::vector<std::string>& operator_vec,
520 const std::string& fieldname,
const std::string& sysname,
521 const bool& rel_temperature,
const std::string& prefix,
526 bool temperature_dimension =
false;
528 if (dim_vec.size() == 1)
530 if (dim_vec[0] ==
"temperature")
531 temperature_dimension =
true;
532 if (dim_vec[0] ==
"dimensionless")
533 os <<
"typedef boost::units::dimensionless_type " << fieldname <<
"_dimension;"
536 os <<
"typedef boost::units::" << dim_vec[0] <<
"_dimension " << fieldname
537 <<
"_dimension;" << std::endl;
544 std::string result = dim_vec[0];
545 for (std::size_t i = 0, n = operator_vec.size(); i < n; i++)
547 if (operator_vec[i] ==
"/")
548 result =
"boost::mpl::divides<boost::units::" + result +
549 "_dimension,boost::units::" + dim_vec[i + 1] +
"_dimension>::type";
550 else if (operator_vec[i] ==
"*")
551 result =
"boost::mpl::times<boost::units::" + result +
552 "_dimension,boost::units::" + dim_vec[i + 1] +
"_dimension>::type";
554 os << result <<
" " << fieldname <<
"_dimension;" << std::endl;
558 construct_units_typedef_from_dimension(fieldname, sysname,
559 temperature_dimension && !rel_temperature, prefix, os);
566inline void construct_units_typedef_from_base_unit(
const std::string& fieldname,
567 const std::string& base_unit_category_and_name,
568 const bool& rel_temperature,
569 const std::string& prefix, std::ostream& os)
571 bool temperature_unit =
false;
574 if ((base_unit_category_and_name.find(
"temperature") != std::string::npos) ||
575 (base_unit_category_and_name.find(
"kelvin") != std::string::npos))
576 temperature_unit =
true;
578 bool absolute = temperature_unit && !rel_temperature;
583 add_absolute(before, after);
586 add_prefix(prefix, before, after);
589 os <<
"typedef " << before <<
"boost::units::" << base_unit_category_and_name
590 <<
"_base_unit::unit_type " << after << fieldname <<
"_unit;" << std::endl;
597inline void construct_units_typedef_from_custom_unit(
const std::string& fieldname,
598 const std::string& unit_name,
599 const bool& rel_temperature,
600 const std::string& prefix, std::ostream& os)
602 bool temperature_unit =
false;
605 if ((unit_name.find(
"temperature") != std::string::npos) ||
606 (unit_name.find(
"kelvin") != std::string::npos))
607 temperature_unit =
true;
609 bool absolute = temperature_unit && !rel_temperature;
614 add_absolute(before, after);
617 add_prefix(prefix, before, after);
620 os <<
"typedef " << before << unit_name <<
" " << after << fieldname <<
"_unit;" << std::endl;
626inline void construct_field_class_plugin(
const std::string& fieldname, std::ostream& os,
627 const std::string& value_type,
bool is_repeated)
632 os <<
"template<typename Quantity >" << std::endl;
633 os <<
" void set_" << fieldname <<
"_with_units(";
636 os <<
"Quantity value_w_units)" << std::endl;
637 os <<
" { set_" << fieldname <<
"(";
640 os <<
"boost::units::quantity<" << fieldname <<
"_unit," << value_type
641 <<
" >(value_w_units).value() ); };" << std::endl;
646 os <<
"template<typename Quantity >" << std::endl;
647 os <<
" void add_" << fieldname <<
"_with_units(Quantity value_w_units)" << std::endl;
648 os <<
" { add_" << fieldname <<
"(boost::units::quantity<" << fieldname <<
"_unit,"
649 << value_type <<
" >(value_w_units).value() ); };" << std::endl;
654 os <<
"template<typename Quantity >" << std::endl;
655 os <<
" Quantity " << fieldname <<
"_with_units(";
658 os <<
") const" << std::endl;
659 os <<
" { return Quantity(" << fieldname <<
"(";
662 os <<
") * " << fieldname <<
"_unit()); };" << std::endl;
666 os <<
"boost::units::quantity< " << fieldname <<
"_unit," << value_type <<
" > " << fieldname
670 os <<
") const" << std::endl;
671 os <<
" { return " << fieldname <<
"_with_units<boost::units::quantity< " << fieldname
672 <<
"_unit," << value_type <<
" > >(";
675 os <<
"); };" << std::endl;
Dynamic Compact Control Language namespace.