DCCL v5
Loading...
Searching...
No Matches
gen_units_class_plugin.h
1// Copyright 2015-2023:
2// GobySoft, LLC (2013-)
3// Community contributors (see AUTHORS file)
4// File authors:
5// Toby Schneider <toby@gobysoft.org>
6// Stephanie Petillo <stephanie@gobysoft.org>
7//
8//
9// This file is part of the Dynamic Compact Control Language Library
10// ("DCCL").
11//
12// DCCL is free software: you can redistribute it and/or modify
13// it under the terms of the GNU Lesser General Public License as published by
14// the Free Software Foundation, either version 2.1 of the License, or
15// (at your option) any later version.
16//
17// DCCL is distributed in the hope that it will be useful,
18// but WITHOUT ANY WARRANTY; without even the implied warranty of
19// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
20// GNU Lesser General Public License for more details.
21//
22// You should have received a copy of the GNU Lesser General Public License
23// along with DCCL. If not, see <http://www.gnu.org/licenses/>.
24#ifndef GenUnitsClassPlugin20150310H
25#define GenUnitsClassPlugin20150310H
26
27#include <google/protobuf/descriptor.h>
28
29#include <boost/config/warning_disable.hpp>
30#include <boost/lambda/lambda.hpp>
31
32#include <boost/phoenix/bind.hpp>
33#include <boost/phoenix/core.hpp>
34#include <boost/phoenix/operator.hpp>
35#include <boost/phoenix/stl.hpp>
36
37#include <boost/spirit/include/qi.hpp>
38#include <boost/spirit/include/qi_expect.hpp>
39#include <boost/spirit/include/qi_lit.hpp>
40
41#include <boost/algorithm/string/replace.hpp>
42
43#include <iostream>
44#include <set>
45#include <sstream>
46#include <string>
47#include <vector>
48
49#include <boost/bimap.hpp>
50
52// Parsing methods for protobuf messages' units fields:
53// base_dimensions, derived_dimensions, unit_system
55
56namespace dccl
57{
58namespace units
59{
60namespace qi = boost::spirit::qi;
61namespace ascii = boost::spirit::ascii;
62namespace phoenix = boost::phoenix;
63
64// Create a bimap of the available base dimensions
65inline boost::bimap<std::string, char> make_dim_bimap()
66{
67 typedef boost::bimap<std::string, char> dim_bimap;
68 using dimension = dim_bimap::value_type;
69
70 dim_bimap dims;
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", '-'));
82
83 return dims;
84}
85
86// Create vectors of base dimension long and short strings from short (char) inputs
87inline void push_char_base(std::vector<std::string>& vc, std::vector<std::string>& vs,
88 const char& c)
89{
90 vc.emplace_back(1, c);
91
92 using bimap_type = boost::bimap<std::string, char>;
93 bimap_type dim_bimap = make_dim_bimap();
94
95 bimap_type::right_const_iterator it_right = dim_bimap.right.find(c);
96
97 vs.push_back(it_right->second);
98}
99
100// Create vectors of base dimension long and short strings from long (string) inputs
101inline void push_string_base(std::vector<std::string>& vc, std::vector<std::string>& vs,
102 const std::string& s)
103{
104 vs.push_back(s);
105
106 using bimap_type = boost::bimap<std::string, char>;
107 bimap_type dim_bimap = make_dim_bimap();
108
109 bimap_type::left_const_iterator it_left = dim_bimap.left.find(s);
110
111 vc.emplace_back(1, it_left->second);
112}
113
114// Make a vector of strings from char inputs (for creating the derived_dimensions operator vector)
115inline void push_char(std::vector<std::string>& vc, const char& c) { vc.emplace_back(1, c); }
116
117// Make a vector of strings from vector<char> inputs (for creating the derived_dimensions vector of strings)
118inline void push_char_vec(std::vector<std::string>& vc, const std::vector<char>& c)
119{
120 vc.emplace_back(c.begin(), c.end());
121}
122
123// Returns the set of base dimension names that the given unit system supports.
124// Returns an empty set for general systems (si, cgs) that support all dimensions.
125// Specialized single-dimension systems (angle, temperature) return only their
126// supported base dimensions.
127inline std::set<std::string> get_system_supported_base_dimensions(const std::string& sysname)
128{
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"};
135 else
136 return {}; // empty = general system (si, cgs, angle::radian, etc.)
137}
138
139// Validates that all dimension names in dim_names are compatible with the given system.
140// Throws std::runtime_error if an incompatible combination is detected.
141//
142// Specialized systems (angle::degree, temperature::celsius, etc.) only support their
143// native base dimension. Compound Boost Units dimension names (e.g. "angular_velocity",
144// "pressure") require multiple base dimensions and cannot be used reliably with
145// specialized systems because the missing base dimensions are silently treated as
146// dimensionless, causing incorrect unit conversions.
147inline void validate_dimensions_for_system(const std::vector<std::string>& dim_names,
148 const std::string& sysname)
149{
150 const auto supported = get_system_supported_base_dimensions(sysname);
151 if (supported.empty())
152 return; // General system — all dimensions are supported
153
154 const auto bimap = make_dim_bimap();
155
156 for (const auto& dim : dim_names)
157 {
158 const bool is_base_dim = bimap.left.find(dim) != bimap.left.end();
159 if (!is_base_dim)
160 {
161 // Compound Boost Units dimension (e.g. "angular_velocity", "pressure").
162 // These require multiple base dimensions and cannot work correctly with
163 // specialized single-dimension systems.
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.");
170 }
171 else if (supported.find(dim) == supported.end())
172 {
173 // Known base dimension but not supported by this specialized system.
174 std::string supported_list;
175 for (const auto& s : supported)
176 {
177 if (!supported_list.empty())
178 supported_list += ", ";
179 supported_list += "'" + s + "'";
180 }
181 throw std::runtime_error("Dimension '" + dim + "' is not supported by system '" +
182 sysname + "'. That system only supports: " + supported_list +
183 ".");
184 }
185 }
186}
187
188// Parser for base_dimensions input
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)
193{
194 //base_dim_chars = vc;
195 //base_dim_strings = vs;
196 //base_dim_powers = v;
197
198 using ascii::space;
199 using phoenix::push_back;
200 using qi::_1;
201 using qi::char_;
202 using qi::double_;
203 using qi::eps;
204 using qi::phrase_parse;
205
206 try
207 {
208 bool r = phrase_parse(
209 first, /* start iterator */
210 last, /* end iterator */
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),
219 _1)])) >>
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)])),
223 space /* the skip-parser */
224 );
225
226 if (first != last) // fail if we did not get a full match
227 return false;
228 return r;
229 }
230 catch (const std::runtime_error& e)
231 {
232 return false;
233 }
234}
235
236// Parser for derived_dimensions input
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)
241{
242 using ascii::space;
243 using phoenix::push_back;
244 using qi::_1;
245 using qi::char_;
246 using qi::double_;
247 using qi::eps;
248 using qi::phrase_parse;
249
250 try
251 {
252 std::vector<std::string> params;
253 bool r = boost::spirit::qi::parse(
254 first, last,
255 +((+char_("a-z1_"))[phoenix::bind(&push_char_vec, boost::phoenix::ref(params), _1)] >>
256 -(*char_(" ") >>
257 (char_("*/")[phoenix::bind(&push_char, phoenix::ref(derived_dim_operators), _1)] |
258 eps[push_back(phoenix::ref(derived_dim_operators), std::string("*"))]) >>
259 *char_(" "))));
260
261 if (derived_dim_operators.size())
262 derived_dim_operators.pop_back();
263
264 for (auto& param : params)
265 {
266 std::string::size_type dim_pos = param.find("_dimension");
267 if (dim_pos != std::string::npos)
268 param = param.substr(0, dim_pos);
269 //std::cout << *it << std::endl;
270 derived_dim_strings.push_back(param);
271 }
272
273 if (first != last) // fail if we did not get a full match
274 return false;
275 return r;
276 }
277 catch (const std::runtime_error& e)
278 {
279 return false;
280 }
281}
282
283// Extract field type name as string
284inline std::string get_field_type_name(google::protobuf::FieldDescriptor::CppType type)
285{
286 switch (type)
287 {
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";
295 }
296}
297
298} // namespace units
299} // namespace dccl
300
302// Print units class plugin pieces to some type of ostream
304
305// Example:
306// ClassName (Range)
307// fieldname (distance)
308// sysname (si)
309// dimtype_dimension (length_dimension)
310
311// Boost Units - Dimensions Reference:
312// http://www.boost.org/doc/libs/1_57_0/doc/html/boost_units/Reference.html#dimensions_reference
313
314// Generate necessary units systems headers
315inline void include_units_headers(const std::string& sysname, std::ostream& os)
316{
317 os << std::endl;
318 /* os <<std::endl; */
319 /* os <<"//===================" <<std::endl; */
320 /* os <<std::endl; */
321
322 // pre-defined systems from boost units:
323 // http://www.boost.org/doc/libs/1_54_0/boost/units/systems/
324
325 if (sysname == "si" || sysname == "boost::units::si" || sysname == "angle::radian")
326 {
327 os << "#include <boost/units/systems/si.hpp>" << std::endl;
328 }
329 else if (sysname == "cgs" || sysname == "boost::units::cgs")
330 {
331 os << "#include <boost/units/systems/cgs.hpp>" << std::endl;
332 }
333 else if (sysname == "celsius" || sysname == "boost::units::celsius" ||
334 sysname == "temperature::celsius")
335 {
336 os << "#include <boost/units/systems/temperature/celsius.hpp>" << std::endl;
337 }
338 else if (sysname == "fahrenheit" || sysname == "boost::units::fahrenheit" ||
339 sysname == "temperature::fahrenheit")
340 {
341 os << "#include <boost/units/systems/temperature/fahrenheit.hpp>" << std::endl;
342 }
343 else if (sysname == "degree" || sysname == "boost::units::degree" || sysname == "angle::degree")
344 {
345 os << "#include <boost/units/systems/angle/degrees.hpp>" << std::endl;
346 }
347 else if (sysname == "gradian" || sysname == "boost::units::gradian" ||
348 sysname == "angle::gradian")
349 {
350 os << "#include <boost/units/systems/angle/gradians.hpp>" << std::endl;
351 }
352 else if (sysname == "revolution" || sysname == "boost::units::revolution" ||
353 sysname == "angle::revolution")
354 {
355 os << "#include <boost/units/systems/angle/revolutions.hpp>" << std::endl;
356 }
357 else
358 {
359 //include necessary non-boost-units system headers
360 std::string sysname_sub = boost::replace_all_copy(sysname, "::", "/");
361 os << "#include \"" << sysname_sub << ".hpp\"" << std::endl;
362 }
363}
364
365// Generate necessary units systems headers for an individually defined base unit.
366// Unit must be available in this list:
367// http://www.boost.org/doc/libs/1_57_0/doc/html/boost_units/Reference.html#boost_units.Reference.alphabetical_listing_of_base_units
368inline void include_base_unit_headers(const std::string& base_unit_category_and_name,
369 std::ostream& os)
370{
371 os << std::endl;
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;
374}
375
376inline void include_custom_unit_headers(const std::string& header, std::ostream& os)
377{
378 os << std::endl;
379 os << "#include \"" << header << "\"" << std::endl;
380}
381
382inline void add_absolute(std::string& before, std::string& after)
383{
384 before = "boost::units::absolute<" + before;
385 after += "> ";
386}
387
388inline void add_prefix(const std::string& prefix, std::string& before, std::string& after)
389{
390 int power = 1;
391 if (prefix == "yotta")
392 power = 24;
393 else if (prefix == "zetta")
394 power = 21;
395 else if (prefix == "exa")
396 power = 18;
397 else if (prefix == "peta")
398 power = 15;
399 else if (prefix == "tera")
400 power = 12;
401 else if (prefix == "giga")
402 power = 9;
403 else if (prefix == "mega")
404 power = 6;
405 else if (prefix == "kilo")
406 power = 3;
407 else if (prefix == "hecto")
408 power = 2;
409 else if (prefix == "deka")
410 power = 1;
411 else if (prefix == "deci")
412 power = -1;
413 else if (prefix == "centi")
414 power = -2;
415 else if (prefix == "milli")
416 power = -3;
417 else if (prefix == "micro")
418 power = -6;
419 else if (prefix == "nano")
420 power = -9;
421 else if (prefix == "pico")
422 power = -12;
423 else if (prefix == "femto")
424 power = -15;
425 else if (prefix == "atto")
426 power = -18;
427 else if (prefix == "zepto")
428 power = -21;
429 else if (prefix == "yocto")
430 power = -24;
431 else
432 throw(std::runtime_error(std::string("Invalid SI prefix: " + prefix)));
433
434 std::stringstream power_ss;
435 power_ss << power;
436
437 before = "boost::units::make_scaled_unit<" + before;
438 after += ", boost::units::scale<10, boost::units::static_rational<" + power_ss.str() +
439 "> > >::type ";
440}
441
442// Generate a unit typedef when given derived_ or base_dimensions
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)
446{
447 // Namespace the sysname if necessary
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";
461 else
462 sysname_ns = sysname;
463
464 std::string before;
465 std::string after;
466
467 if (absolute && !prefix.empty())
468 throw(std::runtime_error(
469 std::string("'prefix' is not supported with an absolute temperature field")));
470
471 if (absolute)
472 add_absolute(before, after);
473
474 if (!prefix.empty())
475 add_prefix(prefix, before, after);
476
477 // Typedef the unit
478 os << "typedef " << before << "boost::units::unit<" << fieldname << "_dimension," << sysname_ns
479 << "> " << after << fieldname << "_unit;" << std::endl;
480 os << std::endl;
481}
482
483// Generate a dimension typedef when given base_dimensions
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,
488 std::ostream& os)
489{
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)
495 {
496 os << "typedef boost::units::dimensionless_type " << fieldname << "_dimension;"
497 << std::endl;
498 ;
499 }
500 else
501 {
502 os << "typedef boost::units::derived_dimension< ";
503 for (std::size_t i = 0, n = dim_vec.size(); i < n; i++)
504 {
505 os << "boost::units::" << dim_vec[i] << "_base_dimension," << power_vec[i];
506 if (i != dim_vec.size() - 1)
507 os << ", ";
508 }
509 os << " >::type " << fieldname << "_dimension;" << std::endl;
510 }
511 os << std::endl;
512
513 construct_units_typedef_from_dimension(fieldname, sysname,
514 temperature_dimension && !rel_temperature, prefix, os);
515}
516
517// Generate a dimension typedef when given derived_dimensions
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,
522 std::ostream& os)
523{
525
526 bool temperature_dimension = false;
527
528 if (dim_vec.size() == 1)
529 {
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;"
534 << std::endl;
535 else
536 os << "typedef boost::units::" << dim_vec[0] << "_dimension " << fieldname
537 << "_dimension;" << std::endl;
538 }
539 else
540 { //construct new dimension type with mpl divides/times calls based on operators and powers
541 //see example here:
542 //http://www.boost.org/doc/libs/1_57_0/doc/html/boost_units/Examples.html#boost_units.Examples.DimensionExample
543 os << "typedef ";
544 std::string result = dim_vec[0];
545 for (std::size_t i = 0, n = operator_vec.size(); i < n; i++)
546 {
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";
553 }
554 os << result << " " << fieldname << "_dimension;" << std::endl;
555 }
556 os << std::endl;
557
558 construct_units_typedef_from_dimension(fieldname, sysname,
559 temperature_dimension && !rel_temperature, prefix, os);
560}
561
562//=============VVVV
563
564//===========
565// Generate a unit typedef when given base_unit
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)
570{
571 bool temperature_unit = false;
572
573 //expect to see "temperature::celsius" or "temperature::fahrenheit" or "si::kelvin"
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;
577
578 bool absolute = temperature_unit && !rel_temperature;
579
580 std::string before;
581 std::string after;
582 if (absolute)
583 add_absolute(before, after);
584
585 if (!prefix.empty())
586 add_prefix(prefix, before, after);
587
588 // Namespace and typedef the unit
589 os << "typedef " << before << "boost::units::" << base_unit_category_and_name
590 << "_base_unit::unit_type " << after << fieldname << "_unit;" << std::endl;
591
592 os << std::endl;
593}
594
595//===========
596// Generate a unit typedef when given custom unit
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)
601{
602 bool temperature_unit = false;
603
604 //expect to see "temperature::celsius" or "temperature::fahrenheit" or "si::kelvin"
605 if ((unit_name.find("temperature") != std::string::npos) ||
606 (unit_name.find("kelvin") != std::string::npos))
607 temperature_unit = true;
608
609 bool absolute = temperature_unit && !rel_temperature;
610
611 std::string before;
612 std::string after;
613 if (absolute)
614 add_absolute(before, after);
615
616 if (!prefix.empty())
617 add_prefix(prefix, before, after);
618
619 // Namespace and typedef the unit
620 os << "typedef " << before << unit_name << " " << after << fieldname << "_unit;" << std::endl;
621 os << std::endl;
622}
623
624//===========
625// Generate the body of the units plugin for the message class
626inline void construct_field_class_plugin(const std::string& fieldname, std::ostream& os,
627 const std::string& value_type, bool is_repeated)
628{
630
631 // Overloading set_fieldname to accept boost units, i.e. quantity<unit<fieldname_dimension,sysname::system> >
632 os << "template<typename Quantity >" << std::endl;
633 os << " void set_" << fieldname << "_with_units(";
634 if (is_repeated)
635 os << "int index, ";
636 os << "Quantity value_w_units)" << std::endl;
637 os << " { set_" << fieldname << "(";
638 if (is_repeated)
639 os << "index, ";
640 os << "boost::units::quantity<" << fieldname << "_unit," << value_type
641 << " >(value_w_units).value() ); };" << std::endl;
642 os << std::endl;
643
644 if (is_repeated)
645 {
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;
650 os << std::endl;
651 }
652
653 //returns whatever units are requested
654 os << "template<typename Quantity >" << std::endl;
655 os << " Quantity " << fieldname << "_with_units(";
656 if (is_repeated)
657 os << "int index";
658 os << ") const" << std::endl;
659 os << " { return Quantity(" << fieldname << "(";
660 if (is_repeated)
661 os << "index";
662 os << ") * " << fieldname << "_unit()); };" << std::endl;
663 os << std::endl;
664
665 //returns same units only
666 os << "boost::units::quantity< " << fieldname << "_unit," << value_type << " > " << fieldname
667 << "_with_units(";
668 if (is_repeated)
669 os << "int index";
670 os << ") const" << std::endl;
671 os << " { return " << fieldname << "_with_units<boost::units::quantity< " << fieldname
672 << "_unit," << value_type << " > >(";
673 if (is_repeated)
674 os << "index";
675 os << "); };" << std::endl;
676 os << std::endl;
677}
678
679//=============^^^^
680
681#endif
Dynamic Compact Control Language namespace.
Definition any.h:28