diff --git a/tool/CMakeLists.txt b/tool/CMakeLists.txt new file mode 100644 index 00000000..f8eec172 --- /dev/null +++ b/tool/CMakeLists.txt @@ -0,0 +1,14 @@ +project(proto_to_struct) + +cmake_minimum_required(VERSION 3.10) + +set(CMAKE_CXX_STANDARD 20) + +find_package(Protobuf REQUIRED) +include_directories(${PROTOBUF_INCLUDE_DIR}) +include_directories(${CMAKE_CURRENT_BINARY_DIR}) + +set(SOURCE_FILE proto_to_struct.cpp) + +add_executable(proto_to_struct ${SOURCE_FILE}) +target_link_libraries(proto_to_struct protobuf::libprotobuf protobuf::libprotoc pthread) diff --git a/tool/README.md b/tool/README.md new file mode 100644 index 00000000..7a840c9b --- /dev/null +++ b/tool/README.md @@ -0,0 +1,167 @@ +# Instructions for using the .proto file to struc_pack header tool + +## compile + +libprotobuf and libprotoc version is 3.21.0. + +```shell +mkdir build +cd build +cmake .. && make +``` + +## usage + +Usage: + +```shell +protoc --plugin=protoc-gen-example=./proto_to_struct data.proto --example_out=protos +``` + +data.proto is the original file that is intended to be the structure pack file. + +`--example_out=` is followed by the path to the generated file. + +data.proto: + +```proto +syntax = "proto3"; + +package mygame; + +option optimize_for = SPEED; +option cc_enable_arenas = true; + +message Vec3 { + float x = 1; + float y = 2; + float z = 3; +} + +message Weapon { + string name = 1; + int32 damage = 2; +} + +message Monster { + Vec3 pos = 1; + int32 mana = 2; + int32 hp = 3; + string name = 4; + bytes inventory = 5; + enum Color { + Red = 0; + Green = 1; + Blue = 2; + } + Color color = 6; + repeated Weapon weapons = 7; + Weapon equipped = 8; + repeated Vec3 path = 9; +} + +message Monsters { + repeated Monster monsters = 1; +} + +message person { + int32 id = 1; + string name = 2; + int32 age = 3; + double salary = 4; +} + +message persons { + repeated person person_list = 1; +} + +message bench_int32 { + int32 a = 1; + int32 b = 2; + int32 c = 3; + int32 d = 4; +} +``` + +generate struct pack file: + +```cpp +#pragma once +#include + +#define PUBLIC(T) : public iguana::base_impl + +enum class Color { + Red = 0, + Green = 1, + Blue = 2, +}; + +struct Vec3 PUBLIC(Vec3) { + Vec3() = default; + Vec3(float a, float b, float c) : x(a), y(b), z(c) {} + float x; + float y; + float z; +}; +YLT_REFL(Vec3, x, y, z); + +struct Weapon PUBLIC(Weapon) { + Weapon() = default; + Weapon(std::string a, int32 b) : name(std::move(a)), damage(b) {} + std::string name; + int32 damage; +}; +YLT_REFL(Weapon, name, damage); + +struct Monster PUBLIC(Monster) { + Monster() = default; + Monster(Vec3 a, int32 b, int32 c, std::string d, std::string e, enum Color f, std::vector g, Weapon h, std::vector i) : pos(a), mana(b), hp(c), name(std::move(d)), inventory(std::move(e)), weapons(std::move(g)), equipped(h), path(std::move(i)) {} + Vec3 pos; + int32 mana; + int32 hp; + std::string name; + std::string inventory; + enum Color color; + std::vector weapons; + Weapon equipped; + std::vector path; +}; +YLT_REFL(Monster, pos, mana, hp, name, inventory, color, weapons, equipped, path); + +struct Monsters PUBLIC(Monsters) { + Monsters() = default; + Monsters(std::vector a) : monsters(std::move(a)) {} + std::vector monsters; +}; +YLT_REFL(Monsters, monsters); + +struct person PUBLIC(person) { + person() = default; + person(int32 a, std::string b, int32 c, double d) : id(a), name(std::move(b)), age(c), salary(d) {} + int32 id; + std::string name; + int32 age; + double salary; +}; +YLT_REFL(person, id, name, age, salary); + +struct persons PUBLIC(persons) { + persons() = default; + persons(std::vector a) : person_list(std::move(a)) {} + std::vector person_list; +}; +YLT_REFL(persons, person_list); + +struct bench_int32 PUBLIC(bench_int32) { + bench_int32() = default; + bench_int32(int32 a, int32 b, int32 c, int32 d) : a(a), b(b), c(c), d(d) {} + int32 a; + int32 b; + int32 c; + int32 d; +}; +YLT_REFL(bench_int32, a, b, c, d); + + +``` \ No newline at end of file diff --git a/tool/proto_to_struct.cpp b/tool/proto_to_struct.cpp new file mode 100644 index 00000000..44eeea4b --- /dev/null +++ b/tool/proto_to_struct.cpp @@ -0,0 +1,119 @@ +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +#include "struct_code_generator.hpp" +#include "struct_token.hpp" + +bool write_to_output(google::protobuf::io::ZeroCopyOutputStream* output, + const void* data, int size) { + const uint8_t* in = reinterpret_cast(data); + int in_size = size; + + void* out; + int out_size; + + while (true) { + if (!output->Next(&out, &out_size)) { + return false; + } + + if (in_size <= out_size) { + memcpy(out, in, in_size); + output->BackUp(out_size - in_size); + return true; + } + + memcpy(out, in, out_size); + in += out_size; + in_size -= out_size; + } +} + +class struct_code_generator : public google::protobuf::compiler::CodeGenerator { + public: + virtual ~struct_code_generator() {} + + virtual bool Generate(const google::protobuf::FileDescriptor* file, + const std::string& parameter, + google::protobuf::compiler::GeneratorContext* context, + std::string* error) const override { + std::string filename = file->name() + ".h"; + auto output = context->Open(filename); + // Use ZeroCopyOutputStream + google::protobuf::io::ZeroCopyOutputStream* zero_copy_output = output; + + std::vector proto_module_info; + std::vector proto_enum_info; + for (int i = 0; i < file->message_type_count(); ++i) { + // struct name + const google::protobuf::Descriptor* descriptor = file->message_type(i); + + struct_enum enum_token; + enum_token.clear(); + enum_token.get_enum_fields(descriptor); + proto_enum_info.emplace_back(enum_token); + + struct_tokenizer tokenizer; + tokenizer.clear(); + tokenizer.tokenizer(descriptor); + proto_module_info.emplace_back(tokenizer); + } + + std::string struct_header = code_generate_header(); + write_to_output(zero_copy_output, (const void*)struct_header.c_str(), + struct_header.size()); + + // codegen struct enum + for (auto enum_inst : proto_enum_info) { + std::string enum_str = ""; + enum_str = code_generate_enum(enum_inst); + write_to_output(zero_copy_output, (const void*)enum_str.c_str(), + enum_str.size()); + } + + // codegen struct + std::vector struct_module_contents; + + for (auto single_struct : proto_module_info) { + std::string struct_default_str = ""; + std::string struct_constructor_str = ""; + std::string struct_body_str = ""; + std::string struct_macro_str = ""; + + struct_default_str = + code_generate_struct_default(single_struct.get_struct_name()); + struct_constructor_str = code_generate_struct_constructor( + single_struct.get_struct_name(), single_struct.get_tokens()); + struct_body_str = code_generate_body(single_struct.get_tokens()); + struct_macro_str = code_generate_ylt_macro( + single_struct.get_struct_name(), single_struct.get_tokens()); + + write_to_output(zero_copy_output, (const void*)struct_default_str.c_str(), + struct_default_str.size()); + write_to_output(zero_copy_output, + (const void*)struct_constructor_str.c_str(), + struct_constructor_str.size()); + write_to_output(zero_copy_output, (const void*)struct_body_str.c_str(), + struct_body_str.size()); + write_to_output(zero_copy_output, (const void*)struct_macro_str.c_str(), + struct_macro_str.size()); + } + + delete zero_copy_output; + return true; + } +}; + +int main(int argc, char* argv[]) { + google::protobuf::compiler::PluginMain(argc, argv, + new struct_code_generator()); + return 0; +} \ No newline at end of file diff --git a/tool/struct_code_generator.hpp b/tool/struct_code_generator.hpp new file mode 100644 index 00000000..4dfb04ff --- /dev/null +++ b/tool/struct_code_generator.hpp @@ -0,0 +1,197 @@ +#pragma once +#include + +#include "struct_token.hpp" + +char parameter_value[27] = "abcdefghijklmnopqrstuvwxyz"; + +std::string code_generate_header() { + std::string result = + "#pragma once\n#include \n\n#define PUBLIC(T) : " + "public iguana::base_impl\n\n"; + return result; +} + +std::string code_generate_struct_default(const std::string &struct_name) { + std::string result = "struct "; + result.append(struct_name); + result.append(" PUBLIC("); + result.append(struct_name); + result.append(") {\n\t"); + + result.append(struct_name); + result.append("() = default;\n\t"); + + return result; +} + +std::string code_generate_struct_constructor( + const std::string &struct_name, const std::vector lists) { + std::string result = struct_name + "("; + int i = 0; + int list_size = lists.size() - 1; + for (auto it = lists.begin(); it != lists.end(); it++) { + if (it->type == struct_token_type::pod) { + if (it->lable == lable_type::lable_repeated) { + result.append("std::vector<"); + result.append(it->type_name); + result.append("> "); + result += parameter_value[i]; + } + else { + result.append(it->type_name); + result.append(" "); + result += parameter_value[i]; + } + if (i != list_size) + result.append(", "); + i++; + } + else if (it->type == struct_token_type::proto_string) { + if (it->lable == lable_type::lable_repeated) { + result.append("std::vector "); + result += parameter_value[i]; + } + else { + result.append("std::"); + result.append(it->type_name); + result.append(" "); + result += parameter_value[i]; + } + if (i != list_size) + result.append(", "); + i++; + } + else { + if (it->lable == lable_type::lable_repeated) { + result.append("std::vector<"); + result.append(it->type_name); + result.append("> "); + result += parameter_value[i]; + } + else { + result.append(it->type_name); + result.append(" "); + result += parameter_value[i]; + } + if (i != list_size) + result.append(", "); + i++; + } + } + result.append(") : "); + + int j = 0; + for (auto ll : lists) { + if (ll.type == struct_token_type::pod || + ll.type == struct_token_type::message) { + result.append(ll.var_name); + result.append("("); + if (ll.lable == lable_type::lable_repeated) { + result.append("std::move("); + result += parameter_value[j]; + result.append(")"); + if (j != list_size) + result.append("), "); + else + result.append(") {}"); + } + else { + result += parameter_value[j]; + if (j != list_size) + result.append("), "); + else + result.append(") {}"); + } + } + else if (ll.type == struct_token_type::proto_string) { + result.append(ll.var_name); + result.append("("); + result.append("std::move("); + result += parameter_value[j]; + result.append(")"); + if (j != list_size) + result.append("), "); + else + result.append(") {}"); + } + j++; + } + result.append("\n"); + return result; +} + +std::string code_generate_body(const std::vector &lists) { + std::string result; + for (auto ll : lists) { + result.append("\t"); + if (ll.lable == lable_type::lable_repeated) { + if (ll.type == struct_token_type::proto_string) { + result.append("std::vector "); + result.append(ll.var_name); + result.append(";\n"); + } + else { + result.append("std::vector<"); + result.append(ll.type_name); + result.append("> "); + result.append(ll.var_name); + result.append(";\n"); + } + } + else { + if (ll.type == struct_token_type::proto_string) + result.append("std::"); + result.append(ll.type_name); + result.append(" "); + result.append(ll.var_name); + result.append(";\n"); + } + } + result.append("};\n"); + + return result; +} + +std::string code_generate_ylt_macro(const std::string &struct_name, + const std::vector &lists) { + std::string result = "YLT_REFL("; + result.append(struct_name); + result.append(", "); + int i = 0; + int list_size = lists.size() - 1; + for (auto ll : lists) { + if (i != list_size) { + result.append(ll.var_name); + result.append(", "); + } + else { + result.append(ll.var_name); + } + i++; + } + result.append(");\n\n"); + return result; +} + +std::string code_generate_enum(const struct_enum &enum_inst) { + std::string result = "enum class "; + if (enum_inst.enum_name_.empty()) + return ""; + result.append(enum_inst.enum_name_); + result.append(" {\n"); + if (enum_inst.fields_.size() == 0) { + result.append("}\n"); + } + else { + for (auto i : enum_inst.fields_) { + result.append("\t"); + result.append(i.first); + result.append(" = "); + result.append(std::to_string(i.second)); + result.append(",\n"); + } + result.append("};\n\n"); + } + return result; +} \ No newline at end of file diff --git a/tool/struct_token.hpp b/tool/struct_token.hpp new file mode 100644 index 00000000..a9d491a6 --- /dev/null +++ b/tool/struct_token.hpp @@ -0,0 +1,116 @@ +#pragma once +#include + +#include +#include + +enum class struct_token_type { pod, proto_string, message, null_type }; + +enum class lable_type { + lable_optional, + lable_required, + lable_repeated, + lable_null +}; + +struct struct_token { + struct_token() { + this->var_name = ""; + this->type_name = ""; + this->type = struct_token_type::null_type; + this->lable = lable_type::lable_null; + } + + void clear(); + std::string var_name; + std::string type_name; + struct_token_type type; + lable_type lable; +}; + +class struct_tokenizer { + public: + struct_tokenizer() { clear(); } + + void tokenizer(const google::protobuf::Descriptor* descriptor) { + struct_name_ = descriptor->name(); + for (int j = 0; j < descriptor->field_count(); ++j) { + struct_token token = {}; + const google::protobuf::FieldDescriptor* field = descriptor->field(j); + token.var_name = field->name(); + if (field->type() == google::protobuf::FieldDescriptor::TYPE_MESSAGE) { + token.type_name = field->message_type()->name(); + token.type = struct_token_type::message; + } + else if (field->type() == + google::protobuf::FieldDescriptor::TYPE_STRING) { + token.type_name = field->type_name(); + token.type = struct_token_type::proto_string; + // std::cout << "string var anme is: " << token.var_name << std::endl; + } + else { + token.type_name = field->type_name(); + if (token.type_name == "bytes") { + token.type = struct_token_type::proto_string; + token.type_name = "string"; + } + else if (token.type_name == "enum") { + const google::protobuf::EnumDescriptor* enum_desc = + field->enum_type(); + token.type_name = "enum " + enum_desc->name(); + } + else { + token.type = struct_token_type::pod; + } + } + + if (field->label() == + google::protobuf::FieldDescriptor::Label::LABEL_REPEATED) + token.lable = lable_type::lable_repeated; + else if (field->label() == + google::protobuf::FieldDescriptor::Label::LABEL_OPTIONAL) + token.lable = lable_type::lable_optional; + else if (field->label() == + google::protobuf::FieldDescriptor::Label::LABEL_REQUIRED) + token.lable = lable_type::lable_required; + + token_lists_.emplace_back(token); + } + } + + void clear() { + token_lists_.clear(); + struct_name_ = ""; + } + + std::vector& get_tokens() { return token_lists_; } + std::string& get_struct_name() { return struct_name_; } + + private: + std::vector token_lists_; + std::string struct_name_; // struct name +}; + +struct struct_enum { + struct_enum() { clear(); } + + void clear() { + this->enum_name_ = ""; + this->fields_.clear(); + } + + void get_enum_fields(const google::protobuf::Descriptor* descriptor) { + for (int e = 0; e < descriptor->enum_type_count(); ++e) { + const google::protobuf::EnumDescriptor* enum_desc = + descriptor->enum_type(e); + enum_name_ = enum_desc->name(); + for (int v = 0; v < enum_desc->value_count(); ++v) { + const google::protobuf::EnumValueDescriptor* value_desc = + enum_desc->value(v); + fields_.push_back({value_desc->name(), value_desc->number()}); + } + } + } + std::string enum_name_; + std::vector> fields_; +}; \ No newline at end of file