Skip to content

Commit

Permalink
feat: implement tool to convert proto to struct pb
Browse files Browse the repository at this point in the history
  • Loading branch information
helintongh committed Nov 11, 2024
1 parent ae5a951 commit cc8ac5e
Show file tree
Hide file tree
Showing 5 changed files with 613 additions and 0 deletions.
14 changes: 14 additions & 0 deletions tool/CMakeLists.txt
Original file line number Diff line number Diff line change
@@ -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)
167 changes: 167 additions & 0 deletions tool/README.md
Original file line number Diff line number Diff line change
@@ -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 <ylt/struct_pb.hpp>

#define PUBLIC(T) : public iguana::base_impl<T>

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<Weapon> g, Weapon h, std::vector<Vec3> 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<Weapon> weapons;
Weapon equipped;
std::vector<Vec3> path;
};
YLT_REFL(Monster, pos, mana, hp, name, inventory, color, weapons, equipped, path);

struct Monsters PUBLIC(Monsters) {
Monsters() = default;
Monsters(std::vector<Monster> a) : monsters(std::move(a)) {}
std::vector<Monster> 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<person> a) : person_list(std::move(a)) {}
std::vector<person> 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);


```
119 changes: 119 additions & 0 deletions tool/proto_to_struct.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,119 @@
#include <google/protobuf/compiler/code_generator.h>
#include <google/protobuf/compiler/plugin.h>
#include <google/protobuf/descriptor.h>
#include <google/protobuf/io/zero_copy_stream.h>
#include <google/protobuf/io/zero_copy_stream_impl.h>

#include <cinttypes>
#include <fstream>
#include <iostream>
#include <memory>

#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<const uint8_t*>(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<struct_tokenizer> proto_module_info;
std::vector<struct_enum> 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<std::string> 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;
}
Loading

0 comments on commit cc8ac5e

Please sign in to comment.