From 5259c0fb826d7a97267aea13be7543c1768113cb Mon Sep 17 00:00:00 2001 From: Bruno Ribeiro Date: Fri, 26 Jan 2024 18:52:04 -0300 Subject: [PATCH] Modernize some constructions --- compiler/cpp/code.txt | 2 +- compiler/cpp/cppgen.cc | 73 +++++++++++++++++-------------------- compiler/main.cc | 2 +- compiler/proto3.cc | 56 +++++++++++------------------ include/protogen/proto3.hh | 74 ++++++++++++++------------------------ 5 files changed, 82 insertions(+), 125 deletions(-) diff --git a/compiler/cpp/code.txt b/compiler/cpp/code.txt index d18b0b0..7ccc8e9 100644 --- a/compiler/cpp/code.txt +++ b/compiler/cpp/code.txt @@ -44,7 +44,7 @@ #include #include -#ifndef PROTOGEN_$4$ +#ifndef PROTOGEN$4$ #error Missing include of protogen $1$ header #endif ------ diff --git a/compiler/cpp/cppgen.cc b/compiler/cpp/cppgen.cc index e057013..943fe0c 100644 --- a/compiler/cpp/cppgen.cc +++ b/compiler/cpp/cppgen.cc @@ -15,6 +15,8 @@ */ #include +#include +#include #include #include #include @@ -45,8 +47,6 @@ struct GeneratorContext bool cpp_enable_errors; bool cpp_use_lists; std::string custom_parent; - std::string version; - std::string versionNo; GeneratorContext( Printer &printer, Proto3 &root ) : printer(printer), root(root), number_names(false), obfuscate_strings(false), @@ -115,7 +115,11 @@ static std::string nativeType( const Field &field ) return "uint8_t"; else if (field.type.id == protogen::TYPE_MESSAGE) + { + if (field.type.ref == nullptr) + throw protogen::exception("Message type reference is null"); return nativePackage(field.type.ref->package) + "::" + field.type.ref->name; + } else throw protogen::exception("Invalid field type"); } @@ -123,7 +127,7 @@ static std::string nativeType( const Field &field ) /** * Translates protobuf3 types to C++ types. */ -static std::string fieldNativeType( GeneratorContext &ctx, const Field &field, bool useLists ) +static std::string fieldNativeType( const Field &field, bool useLists ) { std::string valueType; @@ -156,8 +160,8 @@ static std::string fieldNativeType( GeneratorContext &ctx, const Field &field, b return valueType; else { - std::string output = "protogen_"; - output += ctx.version; + std::string output = "protogen"; + output += PROTOGEN_VERSION_NAMING; output += "::field<"; output += valueType; output += '>'; @@ -194,7 +198,7 @@ static void generateModel( GeneratorContext &ctx, const Message &message ) ctx.printer("\tstruct $1$_type\n\t{\n", message.name); for (auto field : message.fields) { - ctx.printer("\t\t$1$ $2$;\n", fieldNativeType(ctx, field, ctx.cpp_use_lists), fieldStorage(field) ); + ctx.printer("\t\t$1$ $2$;\n", fieldNativeType(field, ctx.cpp_use_lists), fieldStorage(field) ); } ctx.printer("\t};\n"); @@ -321,46 +325,43 @@ static std::string makeGuard( const std::string &fileName ) return out; } -typedef std::vector MessageList; +typedef std::vector> MessageList; +typedef std::unordered_set> MessageSet; -static bool contains( const MessageList &items, const protogen::Message *message ) +static bool contains( const MessageList &items, const std::shared_ptr &message ) { - for (auto mi = items.begin(); mi != items.end(); ++mi) - if (*mi == message) return true; + for (const auto ¤t : items) + if (current == message) + return true; return false; } - -static void sort( MessageList &items, MessageList &pending, protogen::Message *message ) +static void sort( MessageList &output, MessageSet &pending, std::shared_ptr message ) { - if (contains(pending, message)) return; // circular reference - if (contains(items, message)) return; // already processed + if (pending.count(message) > 0) + throw exception("circular reference with '" + message->name + "'"); + if (contains(output, message)) + return; // already processed - pending.push_back(message); - - for (auto fi = message->fields.begin(); fi != message->fields.end(); ++fi) + pending.insert(message); + for (auto &field : message->fields) { - if (fi->type.ref == nullptr) continue; - if (!contains(items, fi->type.ref)) sort(items, pending, fi->type.ref); + if (field.type.ref != nullptr && !contains(output, field.type.ref)) + sort(output, pending, field.type.ref); } + pending.erase(message); - items.push_back(message); - - auto it = std::find(pending.begin(), pending.end(), message); - if (it != pending.end()) pending.erase(it); + output.push_back(message); } - static void sort( GeneratorContext &ctx ) { - std::vector items; - std::vector pending; - for (auto mi = ctx.root.messages.begin(); mi != ctx.root.messages.end(); ++mi) - { - sort(items, pending, *mi); - } + std::vector> messages; + std::unordered_set> pending; + for (auto &message : ctx.root.messages) + sort(messages, pending, message); - ctx.root.messages.swap(items); + ctx.root.messages.swap(messages); } static void generateInclusions( GeneratorContext &ctx ) @@ -371,16 +372,8 @@ static void generateInclusions( GeneratorContext &ctx ) static void generateModel( GeneratorContext &ctx ) { - char version[12] = { 0 }; - SNPRINTF(version, sizeof(version) - 1, "%02X%02X%02X", - (int) PROTOGEN_MAJOR, (int) PROTOGEN_MINOR, (int) PROTOGEN_PATCH); - ctx.versionNo = version; - SNPRINTF(version, sizeof(version) - 1, "%d_%d_%d", - (int) PROTOGEN_MAJOR, (int) PROTOGEN_MINOR, (int) PROTOGEN_PATCH); - ctx.version = version; - std::string guard = makeGuard(ctx.root.fileName); - ctx.printer(CODE_HEADER, PROTOGEN_VERSION, ctx.root.fileName, guard, ctx.version); + ctx.printer(CODE_HEADER, PROTOGEN_VERSION, ctx.root.fileName, guard, PROTOGEN_VERSION_NAMING); sort(ctx); diff --git a/compiler/main.cc b/compiler/main.cc index 1c45ab9..e52d311 100644 --- a/compiler/main.cc +++ b/compiler/main.cc @@ -69,7 +69,7 @@ int main( int argc, char **argv ) protogen::Proto3 proto; try { - protogen::Proto3::parse(proto, input, fullPath); + proto.parse(input, fullPath); protogen::CppGenerator gen; gen.generate(proto, *output); } catch (protogen::exception &ex) diff --git a/compiler/proto3.cc b/compiler/proto3.cc index 795abb5..a36d364 100644 --- a/compiler/proto3.cc +++ b/compiler/proto3.cc @@ -509,17 +509,6 @@ std::ostream &operator<<( std::ostream &out, protogen::Proto3 &proto ); namespace protogen { - -TypeInfo::TypeInfo() : id(TYPE_DOUBLE), ref(nullptr), repeated(false) -{ -} - - -Field::Field() : index(0) -{ -} - - std::string Message::qualifiedName() const { if (package.empty()) return name; @@ -593,7 +582,7 @@ static void parseStandardOption( ProtoContext &ctx, OptionMap &entries ) } -static Message *findMessage( ProtoContext &ctx, const std::string &name ) +static std::shared_ptr findMessage( ProtoContext &ctx, const std::string &name ) { for (auto it = ctx.tree.messages.begin(); it != ctx.tree.messages.end(); ++it) if ((*it)->qualifiedName() == name) return *it; @@ -650,13 +639,19 @@ static void parseField( ProtoContext &ctx, Message &message ) throw exception("Missing field type", TOKEN_POSITION(ctx.tokens.current)); // name - if (ctx.tokens.next().code != TOKEN_NAME) throw exception("Missing field name", TOKEN_POSITION(ctx.tokens.current)); + if (ctx.tokens.next().code != TOKEN_NAME) + throw exception("Missing field name", TOKEN_POSITION(ctx.tokens.current)); field.name = ctx.tokens.current.value; // equal symbol - if (ctx.tokens.next().code != TOKEN_EQUAL) throw exception("Expected '='", TOKEN_POSITION(ctx.tokens.current)); + if (ctx.tokens.next().code != TOKEN_EQUAL) + throw exception("Expected '='", TOKEN_POSITION(ctx.tokens.current)); // index - if (ctx.tokens.next().code != TOKEN_INTEGER) throw exception("Missing field index", TOKEN_POSITION(ctx.tokens.current)); + if (ctx.tokens.next().code != TOKEN_INTEGER) + throw exception("Missing field index", TOKEN_POSITION(ctx.tokens.current)); field.index = (int) strtol(ctx.tokens.current.value.c_str(), nullptr, 10); + // https://protobuf.dev/programming-guides/proto3/#assigning + if (field.index <= 0 || field.index >= 0x1FFFFFFF || field.index == 0x4E1F || field.index == 0x4A38) + throw exception("Invalid field index", TOKEN_POSITION(ctx.tokens.current)); ctx.tokens.next(); @@ -710,10 +705,7 @@ static void parseMessage( ProtoContext &ctx ) { if (ctx.tokens.current.code == TOKEN_MESSAGE && ctx.tokens.next().code == TOKEN_NAME) { - Message *message = new(std::nothrow) Message(); - if (message == nullptr) - throw exception("Out of memory"); - //splitPackage(message.package, ctx.package); + auto message = std::make_shared(); message->package = ctx.package; message->name = ctx.tokens.current.value; if (ctx.tokens.next().code != TOKEN_BEGIN) @@ -790,14 +782,7 @@ static void parseProto( ProtoContext &ctx ) } while (ctx.tokens.current.code != 0); } - -Proto3::~Proto3() -{ - for (auto it = messages.begin(); it != messages.end(); ++it) delete *it; -} - - -void Proto3::parse( Proto3 &tree, std::istream &input, const std::string &fileName ) +void Proto3::parse( std::istream &input, const std::string &fileName ) { std::ios_base::fmtflags flags = input.flags(); std::noskipws(input); @@ -807,8 +792,8 @@ void Proto3::parse( Proto3 &tree, std::istream &input, const std::string &fileNa InputStream< std::istream_iterator > is(begin, end); Tokenizer< std::istream_iterator > tok(is); - ProtoContext ctx(tok, tree, is); - tree.fileName = fileName; + ProtoContext ctx(tok, *this, is); + this->fileName = fileName; try { @@ -821,15 +806,16 @@ void Proto3::parse( Proto3 &tree, std::istream &input, const std::string &fileNa } // check if we have unresolved types - for (auto mit = tree.messages.begin(); mit != tree.messages.end(); ++mit) + for (auto &message : this->messages) { - for (auto fit = (*mit)->fields.begin(); fit != (*mit)->fields.end(); ++fit) + for (auto &field : message->fields) { - if (fit->type.ref != nullptr || fit->type.id != TYPE_MESSAGE) continue; + if (field.type.id != TYPE_MESSAGE || field.type.ref != nullptr) + continue; - fit->type.ref = findMessage(ctx, fit->type.qname); - if (fit->type.ref == nullptr) - throw exception("Unable to find message '" + fit->type.qname + "'"); + field.type.ref = findMessage(ctx, field.type.qname); + if (field.type.ref == nullptr) + throw exception("Unable to find message '" + field.type.qname + "'"); } } } diff --git a/include/protogen/proto3.hh b/include/protogen/proto3.hh index 79dc021..c556b7a 100644 --- a/include/protogen/proto3.hh +++ b/include/protogen/proto3.hh @@ -1,5 +1,5 @@ /* - * Copyright 2020 Bruno Ribeiro + * Copyright 2024 Bruno Ribeiro * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -17,20 +17,15 @@ #ifndef PROTOGEN_PROTO3_HH #define PROTOGEN_PROTO3_HH - #include #include #include #include +#include #include - namespace protogen { - -#ifndef PROTOGEN_FIELD_TYPES -#define PROTOGEN_FIELD_TYPES - enum FieldType { TYPE_DOUBLE = 6, @@ -51,22 +46,17 @@ enum FieldType TYPE_MESSAGE = 21, }; -#endif // PROTOGEN_FIELD_TYPES - - class Message; struct TypeInfo { - FieldType id; + FieldType id = FieldType::TYPE_DOUBLE; std::string qname; - Message *ref; - bool repeated; - - TypeInfo(); + std::shared_ptr ref; + bool repeated = false; + bool optional = false; }; - enum class OptionType { IDENTIFIER, @@ -75,56 +65,44 @@ enum class OptionType BOOLEAN }; - struct OptionEntry { std::string name; - OptionType type; + OptionType type = OptionType::IDENTIFIER; std::string value; - int line; + int line = 0; }; - typedef std::unordered_map OptionMap; - -class Field +struct Field { - public: - TypeInfo type; - std::string name; - int index; - OptionMap options; - - Field(); + TypeInfo type; + std::string name; + int index = 0; + OptionMap options; }; - -class Message +struct Message { - public: - std::vector fields; - std::string name; - std::string package; - OptionMap options; - - std::string qualifiedName() const; - void splitPackage( std::vector &out ); -}; + std::vector fields; + std::string name; + std::string package; + OptionMap options; + std::string qualifiedName() const; + void splitPackage( std::vector &out ); +}; -class Proto3 +struct Proto3 { - public: - std::vector messages; - OptionMap options; - std::string fileName; + std::vector> messages; + OptionMap options; + std::string fileName; - ~Proto3(); - static void parse( Proto3 &tree, std::istream &input, const std::string &fileName = ""); + void parse( std::istream &input, const std::string &fileName = ""); }; - } // protogen std::ostream &operator<<( std::ostream &out, protogen::Proto3 &proto );