25#include "gen_units_class_plugin.h"
26#include "option_extensions.pb.h"
27#include <boost/algorithm/string.hpp>
29#include <google/protobuf/compiler/cpp/cpp_generator.h>
30#include <google/protobuf/compiler/plugin.h>
31#include <google/protobuf/descriptor.h>
32#include <google/protobuf/io/printer.h>
33#include <google/protobuf/io/zero_copy_stream.h>
39std::set<std::string> systems_to_include_;
40std::set<std::string> base_units_to_include_;
41std::string filename_h_;
42std::string load_file_cpp_;
43std::shared_ptr<std::fstream> load_file_output_;
45std::string load_file_base_{
46 R
"DEL(#include <dccl/codec.h>
48// DO NOT REMOVE: required by loader code
49std::vector<const google::protobuf::Descriptor*> descriptors;
50template<typename PB> struct DCCLLoader { DCCLLoader() { descriptors.push_back(PB::descriptor()); }};
51// END: required by loader code
56 void dccl3_load(dccl::Codec* dccl)
58 // DO NOT REMOVE: required by loader code
59 for(auto d : descriptors)
61 // END: required by loader code
64 void dccl3_unload(dccl::Codec* dccl)
66 // DO NOT REMOVE: required by loader code
67 for(auto d : descriptors)
69 // END: required by loader code
73// BEGIN (TO END OF FILE): AUTOGENERATED LOADERS
76class DCCLGenerator :
public google::protobuf::compiler::cpp::CppGenerator
83 bool Generate(
const google::protobuf::FileDescriptor* file,
const std::string& parameter,
84 google::protobuf::compiler::GeneratorContext* generator_context,
85 std::string* error)
const override;
88 void generate_message(
89 const google::protobuf::Descriptor* desc,
90 google::protobuf::compiler::GeneratorContext* generator_context,
91 std::shared_ptr<std::string> message_unit_system = std::shared_ptr<std::string>())
const;
92 void generate_field(
const google::protobuf::FieldDescriptor* field,
93 google::protobuf::io::Printer* printer,
94 std::shared_ptr<std::string> message_unit_system)
const;
95 bool check_field_type(
const google::protobuf::FieldDescriptor* field)
const;
97 void generate_load_file_headers()
const;
98 void generate_load_file_message_loader(
const google::protobuf::Descriptor* desc)
const;
101bool DCCLGenerator::check_field_type(
const google::protobuf::FieldDescriptor* field)
const
103 bool is_integer = field->cpp_type() == google::protobuf::FieldDescriptor::CPPTYPE_INT32 ||
104 field->cpp_type() == google::protobuf::FieldDescriptor::CPPTYPE_INT64 ||
105 field->cpp_type() == google::protobuf::FieldDescriptor::CPPTYPE_UINT32 ||
106 field->cpp_type() == google::protobuf::FieldDescriptor::CPPTYPE_UINT64;
108 bool is_float = field->cpp_type() == google::protobuf::FieldDescriptor::CPPTYPE_DOUBLE ||
109 field->cpp_type() == google::protobuf::FieldDescriptor::CPPTYPE_FLOAT;
111 if (!is_float && !is_integer)
113 throw std::runtime_error(
"Can only use (dccl.field).base_dimensions on numeric fields");
118bool DCCLGenerator::Generate(
const google::protobuf::FileDescriptor* file,
119 const std::string& parameter,
120 google::protobuf::compiler::GeneratorContext* generator_context,
121 std::string* error)
const
124 google::protobuf::compiler::cpp::CppGenerator::Generate(file, parameter, generator_context,
128 std::vector<std::pair<std::string, std::string>> options;
129 google::protobuf::compiler::ParseGeneratorParameter(parameter, &options);
131 for (
const auto& option : options)
133 const auto& key = option.first;
134 const auto& value = option.second;
135 if (key ==
"dccl3_load_file")
137 load_file_cpp_ = value;
138 load_file_output_ = std::make_shared<std::fstream>(
139 load_file_cpp_, std::ios::in | std::ios::out | std::ios::app);
141 if (!load_file_output_->is_open())
143 *error =
"Failed to open dccl3_load_file: " + load_file_cpp_;
149 *error =
"Unknown parameter: " + key;
156 const std::string& filename = file->name();
157 filename_h_ = filename.substr(0, filename.find(
".proto")) +
".pb.h";
160 if (load_file_output_)
161 generate_load_file_headers();
163 for (
int message_i = 0, message_n = file->message_type_count(); message_i < message_n;
168 generate_message(file->message_type(message_i), generator_context);
170 catch (std::exception& e)
173 std::runtime_error(std::string(
"Failed to generate DCCL code: \n") + e.what()));
177 std::shared_ptr<google::protobuf::io::ZeroCopyOutputStream> include_output(
178 generator_context->OpenForInsert(filename_h_,
"includes"));
179 google::protobuf::io::Printer include_printer(include_output.get(),
'$');
180 std::stringstream includes_ss;
182 includes_ss <<
"#include <boost/units/quantity.hpp>" << std::endl;
183 includes_ss <<
"#include <boost/units/absolute.hpp>" << std::endl;
184 includes_ss <<
"#include <boost/units/dimensionless_type.hpp>" << std::endl;
185 includes_ss <<
"#include <boost/units/make_scaled_unit.hpp>" << std::endl;
187 for (
const auto& it : systems_to_include_) { include_units_headers(it, includes_ss); }
188 for (
const auto& it : base_units_to_include_)
190 include_base_unit_headers(it, includes_ss);
192 include_printer.Print(includes_ss.str().c_str());
196 catch (std::exception& e)
203void DCCLGenerator::generate_message(
204 const google::protobuf::Descriptor* desc,
205 google::protobuf::compiler::GeneratorContext* generator_context,
206 std::shared_ptr<std::string> message_unit_system)
const
211 std::shared_ptr<google::protobuf::io::ZeroCopyOutputStream> output(
212 generator_context->OpenForInsert(filename_h_,
"class_scope:" + desc->full_name()));
213 google::protobuf::io::Printer printer(output.get(),
'$');
215 if (desc->options().HasExtension(dccl::msg))
217 if (desc->options().GetExtension(dccl::msg).id() != 0)
219 std::stringstream id_enum;
220 id_enum <<
"enum DCCLParameters { DCCL_ID = "
221 << desc->options().GetExtension(dccl::msg).id() <<
", "
222 <<
" DCCL_MAX_BYTES = "
223 << desc->options().GetExtension(dccl::msg).max_bytes() <<
" };\n";
224 printer.Print(id_enum.str().c_str());
226 if (load_file_output_)
227 generate_load_file_message_loader(desc);
232 desc->options().GetExtension(dccl::msg);
233 if (dccl_msg_options.has_unit_system())
235 message_unit_system.reset(
new std::string(dccl_msg_options.unit_system()));
236 systems_to_include_.insert(dccl_msg_options.unit_system());
240 for (
int field_i = 0, field_n = desc->field_count(); field_i < field_n; ++field_i)
242 generate_field(desc->field(field_i), &printer, message_unit_system);
245 for (
int nested_type_i = 0, nested_type_n = desc->nested_type_count();
246 nested_type_i < nested_type_n; ++nested_type_i)
247 generate_message(desc->nested_type(nested_type_i), generator_context,
248 message_unit_system);
251 catch (std::exception& e)
253 throw(std::runtime_error(std::string(
"Message: \n" + desc->full_name() +
"\n" + e.what())));
257void DCCLGenerator::generate_field(
const google::protobuf::FieldDescriptor* field,
258 google::protobuf::io::Printer* printer,
259 std::shared_ptr<std::string> message_unit_system)
const
264 field->options().GetExtension(dccl::field);
266 if (!dccl_field_options.has_units())
271 if ((dccl_field_options.units().has_base_dimensions() &&
272 dccl_field_options.units().has_derived_dimensions()) ||
273 (dccl_field_options.units().has_base_dimensions() &&
274 dccl_field_options.units().has_unit()) ||
275 (dccl_field_options.units().has_unit() &&
276 dccl_field_options.units().has_derived_dimensions()))
278 throw(std::runtime_error(
"May define either (dccl.field).units.base_dimensions or "
279 "(dccl.field).units.derived_dimensions or "
280 "(dccl.field).units.unit, but not more than one."));
282 else if (dccl_field_options.units().has_unit())
284 std::stringstream new_methods;
286 construct_units_typedef_from_base_unit(
287 field->name(), dccl_field_options.units().unit(),
288 dccl_field_options.units().relative_temperature(),
289 dccl_field_options.units().prefix(), new_methods);
290 construct_field_class_plugin(field->name(), new_methods,
291 dccl::units::get_field_type_name(field->cpp_type()),
292 field->is_repeated());
293 printer->Print(new_methods.str().c_str());
294 base_units_to_include_.insert(dccl_field_options.units().unit());
296 else if (dccl_field_options.units().has_base_dimensions())
298 std::stringstream new_methods;
300 std::vector<double> powers;
301 std::vector<std::string> short_dimensions;
302 std::vector<std::string> dimensions;
303 if (dccl::units::parse_base_dimensions(
304 dccl_field_options.units().base_dimensions().begin(),
305 dccl_field_options.units().base_dimensions().end(), powers, short_dimensions,
308 if (!dccl_field_options.units().has_system() && !message_unit_system)
309 throw(std::runtime_error(
310 std::string(
"Field must have 'system' defined or message must have "
311 "'unit_system' defined when using 'base_dimensions'.")));
314 const std::string& unit_system =
315 (!dccl_field_options.units().has_system() && message_unit_system)
316 ? *message_unit_system
317 : dccl_field_options.units().system();
319 construct_base_dims_typedef(dimensions, powers, field->name(), unit_system,
320 dccl_field_options.units().relative_temperature(),
321 dccl_field_options.units().prefix(), new_methods);
323 construct_field_class_plugin(field->name(), new_methods,
324 dccl::units::get_field_type_name(field->cpp_type()),
325 field->is_repeated());
326 printer->Print(new_methods.str().c_str());
327 systems_to_include_.insert(unit_system);
331 throw(std::runtime_error(std::string(
"Failed to parse base_dimensions string: \"" +
332 dccl_field_options.units().base_dimensions() +
336 else if (dccl_field_options.units().has_derived_dimensions())
338 std::stringstream new_methods;
340 std::vector<std::string> operators;
341 std::vector<std::string> dimensions;
342 if (dccl::units::parse_derived_dimensions(
343 dccl_field_options.units().derived_dimensions().begin(),
344 dccl_field_options.units().derived_dimensions().end(), operators, dimensions))
346 if (!dccl_field_options.units().has_system() && !message_unit_system)
347 throw(std::runtime_error(
348 std::string(
"Field must have 'system' defined or message must have "
349 "'unit_system' defined when using 'derived_dimensions'.")));
350 const std::string& unit_system =
351 (!dccl_field_options.units().has_system() && message_unit_system)
352 ? *message_unit_system
353 : dccl_field_options.units().system();
355 construct_derived_dims_typedef(dimensions, operators, field->name(), unit_system,
356 dccl_field_options.units().relative_temperature(),
357 dccl_field_options.units().prefix(), new_methods);
359 construct_field_class_plugin(field->name(), new_methods,
360 dccl::units::get_field_type_name(field->cpp_type()),
361 field->is_repeated());
362 printer->Print(new_methods.str().c_str());
363 systems_to_include_.insert(unit_system);
367 throw(std::runtime_error(
368 std::string(
"Failed to parse derived_dimensions string: \"" +
369 dccl_field_options.units().derived_dimensions() +
"\"")));
373 catch (std::exception& e)
376 std::runtime_error(std::string(
"Field: \n" + field->DebugString() +
"\n" + e.what())));
380void DCCLGenerator::generate_load_file_headers()
const
382 bool file_is_empty = (load_file_output_->peek() == std::ifstream::traits_type::eof());
383 load_file_output_->clear();
385 bool header_already_written =
false;
388 *load_file_output_ << load_file_base_ << std::flush;
392 for (std::string line; getline(*load_file_output_, line);)
394 if (line.find(
"\"" + filename_h_ +
"\"") != std::string::npos)
396 header_already_written =
true;
402 load_file_output_->clear();
404 load_file_output_->seekp(0, std::ios_base::end);
406 if (!header_already_written)
407 *load_file_output_ <<
"#include \"" << filename_h_ <<
"\"" << std::endl;
410void DCCLGenerator::generate_load_file_message_loader(
411 const google::protobuf::Descriptor* desc)
const
414 load_file_output_->seekp(0, std::ios_base::beg);
417 std::string cpp_name = desc->full_name();
418 boost::algorithm::replace_all(cpp_name,
".",
"::");
421 std::string loader_name = desc->full_name() +
"_loader";
422 boost::algorithm::replace_all(loader_name,
".",
"_");
423 boost::algorithm::to_lower(loader_name);
425 bool loader_already_written =
false;
426 for (std::string line; getline(*load_file_output_, line);)
428 if (line.find(
"<" + cpp_name +
">") != std::string::npos)
430 loader_already_written =
true;
434 load_file_output_->clear();
435 load_file_output_->seekp(0, std::ios_base::end);
437 if (!loader_already_written)
438 *load_file_output_ <<
"DCCLLoader<" << cpp_name <<
"> " << loader_name <<
";" << std::endl;
441int main(
int argc,
char* argv[])
444 return google::protobuf::compiler::PluginMain(argc, argv, &generator);