From 8bc0477b1e6725dc972f94da73d3fc6e48300b02 Mon Sep 17 00:00:00 2001 From: Kybxd <627940450@qq.com> Date: Thu, 4 Sep 2025 14:57:43 +0800 Subject: [PATCH 1/6] feat: gen cpp hub files by tpl --- .../embed/templates/hub.pc.cc.tpl | 156 ++++++ .../embed/templates/hub.pc.h.tpl | 160 ++++++ .../embed/templates/hub_shard.pc.cc.tpl | 20 + .../helper/protofile.go | 59 +++ cmd/protoc-gen-cpp-tableau-loader/hub.go | 474 ++---------------- cmd/protoc-gen-cpp-tableau-loader/shard.go | 127 ----- .../cpp-tableau-loader/src/protoconf/hub.pc.h | 6 +- .../src/protoconf/hub_shard0.pc.cc | 2 + .../src/protoconf/hub_shard1.pc.cc | 2 + 9 files changed, 454 insertions(+), 552 deletions(-) create mode 100644 cmd/protoc-gen-cpp-tableau-loader/embed/templates/hub.pc.cc.tpl create mode 100644 cmd/protoc-gen-cpp-tableau-loader/embed/templates/hub.pc.h.tpl create mode 100644 cmd/protoc-gen-cpp-tableau-loader/embed/templates/hub_shard.pc.cc.tpl create mode 100644 cmd/protoc-gen-cpp-tableau-loader/helper/protofile.go delete mode 100644 cmd/protoc-gen-cpp-tableau-loader/shard.go diff --git a/cmd/protoc-gen-cpp-tableau-loader/embed/templates/hub.pc.cc.tpl b/cmd/protoc-gen-cpp-tableau-loader/embed/templates/hub.pc.cc.tpl new file mode 100644 index 00000000..23db0150 --- /dev/null +++ b/cmd/protoc-gen-cpp-tableau-loader/embed/templates/hub.pc.cc.tpl @@ -0,0 +1,156 @@ +#include "hub.pc.h" + +#include "load.pc.h" +#include "logger.pc.h" +#include "util.pc.h" +{{ if not .Shards }} +// clang-format off +{{ range .Protofiles }}#include "{{ toSnake .Name }}.pc.h" +{{ end }}// clang-format on +{{ end }} +namespace tableau { +std::once_flag Registry::once; +Registrar Registry::registrar; + +Hub::Hub() { tableau::Registry::Init(); } + +void Hub::InitOnce(std::shared_ptr options) { + std::call_once(init_once_, [&]() { options_ = options; }); +} + +bool Hub::Load(const std::filesystem::path& dir, Format fmt /* = Format::kJSON */, + std::shared_ptr options /* = nullptr */) { + auto msger_map = InternalLoad(dir, fmt, options); + if (!msger_map) { + return false; + } + bool ok = Postprocess(msger_map); + if (!ok) { + return false; + } + SetMessagerMap(msger_map); + return true; +} + +bool Hub::AsyncLoad(const std::filesystem::path& dir, Format fmt /* = Format::kJSON */, + std::shared_ptr options /* = nullptr */) { + auto msger_map = InternalLoad(dir, fmt, options); + if (!msger_map) { + return false; + } + bool ok = Postprocess(msger_map); + if (!ok) { + return false; + } + sched_->Dispatch(std::bind(&Hub::SetMessagerMap, this, msger_map)); + return true; +} + +int Hub::LoopOnce() { return sched_->LoopOnce(); } + +void Hub::InitScheduler() { + sched_ = new internal::Scheduler(); + sched_->Current(); +} + +std::shared_ptr Hub::InternalLoad(const std::filesystem::path& dir, Format fmt /* = Format::kJSON */, + std::shared_ptr options /* = nullptr */) const { + // intercept protobuf error logs + auto old_handler = google::protobuf::SetLogHandler(util::ProtobufLogHandler); + auto msger_map = NewMessagerMap(); + options = options ? options : std::make_shared(); + for (auto iter : *msger_map) { + auto&& name = iter.first; + ATOM_DEBUG("loading %s", name.c_str()); + auto mopts = options->ParseMessagerOptionsByName(name); + bool ok = iter.second->Load(dir, fmt, mopts); + if (!ok) { + ATOM_ERROR("load %s failed: %s", name.c_str(), GetErrMsg().c_str()); + // restore to old protobuf log handler + google::protobuf::SetLogHandler(old_handler); + return nullptr; + } + ATOM_DEBUG("loaded %s", name.c_str()); + } + + // restore to old protobuf log handler + google::protobuf::SetLogHandler(old_handler); + return msger_map; +} + +std::shared_ptr Hub::NewMessagerMap() const { + std::shared_ptr msger_map = std::make_shared(); + for (auto&& it : Registry::registrar) { + if (!options_ || !options_->filter || options_->filter(it.first)) { + (*msger_map)[it.first] = it.second(); + } + } + return msger_map; +} + +std::shared_ptr Hub::GetMessagerMap() const { return GetMessagerContainerWithProvider()->msger_map_; } + +void Hub::SetMessagerMap(std::shared_ptr msger_map) { + // replace with thread-safe guarantee. + std::unique_lock lock(mutex_); + msger_container_ = std::make_shared(msger_map); +} + +const std::shared_ptr Hub::GetMessager(const std::string& name) const { + return GetMessagerContainerWithProvider()->GetMessager(name); +} + +std::shared_ptr Hub::GetMessagerContainerWithProvider() const { + if (options_ && options_->provider) { + return options_->provider(*this); + } + return msger_container_; +} + +bool Hub::Postprocess(std::shared_ptr msger_map) { + // create a temporary hub with messager container for post process + Hub tmp_hub; + tmp_hub.SetMessagerMap(msger_map); + + // messager-level postprocess + for (auto iter : *msger_map) { + auto msger = iter.second; + bool ok = msger->ProcessAfterLoadAll(tmp_hub); + if (!ok) { + SetErrMsg("hub call ProcessAfterLoadAll failed, messager: " + iter.first); + return false; + } + } + return true; +} + +std::time_t Hub::GetLastLoadedTime() const { return GetMessagerContainerWithProvider()->last_loaded_time_; } +{{ if not .Shards }}{{ range .Protofiles }}{{ range .Messagers }} +template <> +const std::shared_ptr<{{ . }}> Hub::Get<{{ . }}>() const { + return GetMessagerContainerWithProvider()->{{ toSnake . }}_; +} +{{ end }}{{ end }}{{ end }} +MessagerContainer::MessagerContainer(std::shared_ptr msger_map /* = nullptr*/) + : msger_map_(msger_map ? msger_map : std::make_shared()), last_loaded_time_(std::time(nullptr)) {{ "{" }}{{ if .Shards }}{{ range .Shards }} + InitShard{{ . }}();{{ end }}{{ else }}{{ range .Protofiles }}{{ range .Messagers }} + {{ toSnake . }}_ = std::dynamic_pointer_cast<{{ . }}>(GetMessager({{ . }}::Name()));{{ end }}{{ end }}{{ end }} +} + +const std::shared_ptr MessagerContainer::GetMessager(const std::string& name) const { + if (msger_map_) { + auto it = msger_map_->find(name); + if (it != msger_map_->end()) { + return it->second; + } + } + return nullptr; +} + +void Registry::Init() { + std::call_once(once, []() {{ "{" }}{{ if .Shards }}{{ range .Shards }} + InitShard{{ . }}();{{ end }}{{ else }}{{ range .Protofiles }}{{ range .Messagers }} + Register<{{ . }}>();{{ end }}{{ end }}{{ end }} + }); +} +} // namespace tableau diff --git a/cmd/protoc-gen-cpp-tableau-loader/embed/templates/hub.pc.h.tpl b/cmd/protoc-gen-cpp-tableau-loader/embed/templates/hub.pc.h.tpl new file mode 100644 index 00000000..5de5098c --- /dev/null +++ b/cmd/protoc-gen-cpp-tableau-loader/embed/templates/hub.pc.h.tpl @@ -0,0 +1,160 @@ +#pragma once +#include +#include +#include +#include +#include +#include + +#include "load.pc.h" +#include "scheduler.pc.h" + +namespace tableau { +class MessagerContainer; +class Hub; + +using MessagerMap = std::unordered_map>; +// FilterFunc filter in messagers if returned value is true. +// NOTE: name is the protobuf message name, e.g.: "message ItemConf{...}". +using Filter = std::function; +// MessagerContainerProvider provides a custom MessagerContainer for hub. +// If not specified, the hub's default MessagerContainer will be used. +// NOTE: This func must return non-nil MessagerContainer. +using MessagerContainerProvider = std::function(const Hub&)>; + +struct HubOptions { + // Filter can only filter in certain specific messagers based on the + // condition that you provide. + Filter filter; + // Provide custom MessagerContainer. For keeping configuration access + // consistent in a coroutine or a transaction. + MessagerContainerProvider provider; +}; + +class Hub { + public: + Hub(); + + // InitOnce inits the hub only once, and the subsequent calls will not take effect. + void InitOnce(std::shared_ptr options); + + /***** Synchronous Loading *****/ + // Load fills messages (in MessagerContainer) from files in the specified directory and format. + bool Load(const std::filesystem::path& dir, Format fmt = Format::kJSON, + std::shared_ptr options = nullptr); + + /***** Asynchronous Loading *****/ + // Load configs into temp MessagerContainer, and you should call LoopOnce() in you app's main loop, + // in order to take the temp MessagerContainer into effect. + bool AsyncLoad(const std::filesystem::path& dir, Format fmt = Format::kJSON, + std::shared_ptr options = nullptr); + int LoopOnce(); + // You'd better initialize the scheduler in the main thread. + void InitScheduler(); + + /***** MessagerMap *****/ + std::shared_ptr GetMessagerMap() const; + void SetMessagerMap(std::shared_ptr msger_map); + + /***** MessagerContainer *****/ + // This function is exposed only for use in MessagerContainerProvider. + std::shared_ptr GetMessagerContainer() const { return msger_container_; } + + /***** Access APIs *****/ + template + const std::shared_ptr Get() const; + + template + const U* Get(Args... args) const; + + template + const U* GetOrderedMap(Args... args) const; + + // GetLastLoadedTime returns the time when hub's msger_container_ was last set. + inline std::time_t GetLastLoadedTime() const; + + private: + std::shared_ptr InternalLoad(const std::filesystem::path& dir, Format fmt = Format::kJSON, + std::shared_ptr options = nullptr) const; + std::shared_ptr NewMessagerMap() const; + std::shared_ptr GetMessagerContainerWithProvider() const; + const std::shared_ptr GetMessager(const std::string& name) const; + + bool Postprocess(std::shared_ptr msger_map); + + private: + // For thread-safe guarantee during configuration updating. + std::mutex mutex_; + // All messagers' container. + std::shared_ptr msger_container_; + // Loading scheduler. + internal::Scheduler* sched_ = nullptr; + // Init once + std::once_flag init_once_; + // Hub options + std::shared_ptr options_; +}; + +template +const std::shared_ptr Hub::Get() const { + auto msg = GetMessager(T::Name()); + return std::dynamic_pointer_cast(msg); +} + +template +const U* Hub::Get(Args... args) const { + auto msger = Get(); + return msger ? msger->Get(args...) : nullptr; +} + +template +const U* Hub::GetOrderedMap(Args... args) const { + auto msger = Get(); + return msger ? msger->GetOrderedMap(args...) : nullptr; +} +{{ range .Protofiles }}{{ range .Messagers }} +class {{ . }}; +template <> +const std::shared_ptr<{{ . }}> Hub::Get<{{ . }}>() const; +{{ end }}{{ end }} +class MessagerContainer { + friend class Hub; + + public: + MessagerContainer(std::shared_ptr msger_map = nullptr); + + private: + const std::shared_ptr GetMessager(const std::string& name) const; +{{ range .Shards }} void InitShard{{ . }}(); +{{ end }} + private: + std::shared_ptr msger_map_; + std::time_t last_loaded_time_;{{ range .Protofiles }}{{ range .Messagers }} + std::shared_ptr<{{ . }}> {{ toSnake . }}_;{{ end }}{{ end }} +}; + +using MessagerGenerator = std::function()>; +// messager name -> messager generator +using Registrar = std::unordered_map; +class Registry { + friend class Hub; + + public: + static void Init(); + + template + static void Register(); +{{ if .Shards }} + private: +{{ range .Shards }} static void InitShard{{ . }}(); +{{ end }}{{ end }} + private: + static std::once_flag once; + static Registrar registrar; +}; + +template +void Registry::Register() { + registrar[T::Name()] = []() { return std::make_shared(); }; +} +} // namespace tableau diff --git a/cmd/protoc-gen-cpp-tableau-loader/embed/templates/hub_shard.pc.cc.tpl b/cmd/protoc-gen-cpp-tableau-loader/embed/templates/hub_shard.pc.cc.tpl new file mode 100644 index 00000000..db20e654 --- /dev/null +++ b/cmd/protoc-gen-cpp-tableau-loader/embed/templates/hub_shard.pc.cc.tpl @@ -0,0 +1,20 @@ +#include "hub.pc.h" + +// clang-format off +{{ range .Protofiles }}#include "{{ toSnake .Name }}.pc.h" +{{ end }}// clang-format on + +namespace tableau {{ "{" }}{{ range .Protofiles }}{{ range .Messagers }} +template <> +const std::shared_ptr<{{ . }}> Hub::Get<{{ . }}>() const { + return GetMessagerContainerWithProvider()->{{ toSnake . }}_; +} +{{ end }}{{ end }} +void MessagerContainer::InitShard{{ .Shard }}() {{ "{" }}{{ range .Protofiles }}{{ range .Messagers }} + {{ toSnake . }}_ = std::dynamic_pointer_cast<{{ . }}>(GetMessager({{ . }}::Name()));{{ end }}{{ end }} +} + +void Registry::InitShard{{ .Shard }}() {{ "{" }}{{ range .Protofiles }}{{ range .Messagers }} + Register<{{ . }}>();{{ end }}{{ end }} +} +} // namespace tableau diff --git a/cmd/protoc-gen-cpp-tableau-loader/helper/protofile.go b/cmd/protoc-gen-cpp-tableau-loader/helper/protofile.go new file mode 100644 index 00000000..68247622 --- /dev/null +++ b/cmd/protoc-gen-cpp-tableau-loader/helper/protofile.go @@ -0,0 +1,59 @@ +package helper + +import ( + "errors" + "sort" + + "github.com/tableauio/loader/internal/options" + "github.com/tableauio/tableau/proto/tableaupb" + "google.golang.org/protobuf/compiler/protogen" + "google.golang.org/protobuf/proto" + "google.golang.org/protobuf/types/descriptorpb" +) + +type ProtoFiles []*ProtoFile + +type ProtoFile struct { + Name string + Messagers []string +} + +func ParseProtoFiles(gen *protogen.Plugin) ProtoFiles { + var protofiles []*ProtoFile + for _, f := range gen.Files { + if !options.NeedGenFile(f) { + continue + } + opts := f.Desc.Options().(*descriptorpb.FileOptions) + workbook := proto.GetExtension(opts, tableaupb.E_Workbook).(*tableaupb.WorkbookOptions) + if workbook == nil { + continue + } + var messagers []string + for _, message := range f.Messages { + opts, ok := message.Desc.Options().(*descriptorpb.MessageOptions) + if !ok { + gen.Error(errors.New("get message options failed")) + } + worksheet, ok := proto.GetExtension(opts, tableaupb.E_Worksheet).(*tableaupb.WorksheetOptions) + if !ok { + gen.Error(errors.New("get worksheet extension failed")) + } + if worksheet != nil { + messagerName := string(message.Desc.Name()) + messagers = append(messagers, messagerName) + } + } + // sort messagers in one file to keep in order + sort.Strings(messagers) + protofiles = append(protofiles, &ProtoFile{ + Name: f.GeneratedFilenamePrefix, + Messagers: messagers, + }) + } + // sort all files to keep in order + sort.Slice(protofiles, func(i, j int) bool { + return protofiles[i].Name < protofiles[j].Name + }) + return protofiles +} diff --git a/cmd/protoc-gen-cpp-tableau-loader/hub.go b/cmd/protoc-gen-cpp-tableau-loader/hub.go index 042c95e7..9d120d32 100644 --- a/cmd/protoc-gen-cpp-tableau-loader/hub.go +++ b/cmd/protoc-gen-cpp-tableau-loader/hub.go @@ -1,442 +1,74 @@ package main import ( - "errors" - "sort" + "math" + "strconv" + "text/template" "github.com/iancoleman/strcase" "github.com/tableauio/loader/cmd/protoc-gen-cpp-tableau-loader/helper" - "github.com/tableauio/loader/internal/options" - "github.com/tableauio/tableau/proto/tableaupb" "google.golang.org/protobuf/compiler/protogen" - "google.golang.org/protobuf/proto" - "google.golang.org/protobuf/types/descriptorpb" ) -func getAllOrderedFilesAndMessagers(gen *protogen.Plugin) (protofiles []string, fileMessagers map[string][]string) { - fileMessagers = map[string][]string{} - for _, f := range gen.Files { - if !options.NeedGenFile(f) { - continue - } - opts := f.Desc.Options().(*descriptorpb.FileOptions) - workbook := proto.GetExtension(opts, tableaupb.E_Workbook).(*tableaupb.WorkbookOptions) - if workbook == nil { - continue - } - protofiles = append(protofiles, f.GeneratedFilenamePrefix) - var messagers []string - for _, message := range f.Messages { - opts, ok := message.Desc.Options().(*descriptorpb.MessageOptions) - if !ok { - gen.Error(errors.New("get message options failed")) - } - worksheet, ok := proto.GetExtension(opts, tableaupb.E_Worksheet).(*tableaupb.WorksheetOptions) - if !ok { - gen.Error(errors.New("get worksheet extension failed")) - } - if worksheet != nil { - messagerName := string(message.Desc.Name()) - messagers = append(messagers, messagerName) - } - } - // sort messagers in one file to keep in order - sort.Strings(messagers) - fileMessagers[f.GeneratedFilenamePrefix] = messagers - } - // sort all files to keep in order - sort.Strings(protofiles) - return -} +var tpl = template.Must(template.New("").Funcs(template.FuncMap{ + "toSnake": strcase.ToSnake, +}).ParseFS(efs, "embed/templates/*")) // generateHub generates related hub files. func generateHub(gen *protogen.Plugin) { - if *shards <= 1 { - protofiles, fileMessagers := getAllOrderedFilesAndMessagers(gen) - - hppFilename := "hub." + pcExt + ".h" - g1 := gen.NewGeneratedFile(hppFilename, "") - helper.GenerateCommonHeader(gen, g1, version) - g1.P() - g1.P(hubHpp) - generateHubHppTplSpec(g1, protofiles, fileMessagers) - g1.P(msgContainerHpp) - generateHubHppMsgContainerMembers(g1, protofiles, fileMessagers) - g1.P(registryHpp) - g1.P(bottomHpp) - - cppFilename := "hub." + pcExt + ".cc" - g2 := gen.NewGeneratedFile(cppFilename, "") - helper.GenerateCommonHeader(gen, g2, version) - g2.P() - g2.P(hubCppHeader) - generateHubCppHeader(g2, protofiles) - g2.P(hubCpp) - generateHubCppTplSpec(g2, protofiles, fileMessagers) - g2.P(msgContainerCpp) - generateHubCppMsgContainerCtor(g2, protofiles, fileMessagers) - g2.P(registryCpp) - generateHubCppRegistry(g2, protofiles, fileMessagers) - g2.P(bottomCpp) - } else { - // sharding - generateShardedHub(gen) + protofiles := helper.ParseProtoFiles(gen) + // detect real shard num + realShardNum := *shards + if realShardNum > len(protofiles) { + realShardNum = len(protofiles) } -} - -func generateHubHppTplSpec(g *protogen.GeneratedFile, protofiles []string, fileMessagers map[string][]string) { - for _, proto := range protofiles { - for _, messager := range fileMessagers[proto] { - g.P("class ", messager, ";") - g.P("template <>") - g.P("const std::shared_ptr<", messager, "> Hub::Get<", messager, ">() const;") - g.P() - } + if realShardNum <= 1 { + realShardNum = 0 } -} - -func generateHubHppMsgContainerMembers(g *protogen.GeneratedFile, protofiles []string, fileMessagers map[string][]string) { - for _, proto := range protofiles { - for _, messager := range fileMessagers[proto] { - g.P(helper.Indent(1), "std::shared_ptr<", messager, "> ", strcase.ToSnake(messager), "_;") - } + shards := make([]int, realShardNum) + for i := range shards { + shards[i] = i } -} - -func generateHubCppHeader(g *protogen.GeneratedFile, protofiles []string) { - for _, proto := range protofiles { - g.P(`#include "`, proto, ".", pcExt, `.h"`) + type Param struct { + Shards []int + Protofiles helper.ProtoFiles } - g.P() -} - -func generateHubCppTplSpec(g *protogen.GeneratedFile, protofiles []string, fileMessagers map[string][]string) { - for _, proto := range protofiles { - for _, messager := range fileMessagers[proto] { - g.P("template <>") - g.P("const std::shared_ptr<", messager, "> Hub::Get<", messager, ">() const {;") - g.P(helper.Indent(1), "return GetMessagerContainer()->", strcase.ToSnake(messager), "_;") - g.P("}") - g.P() - } + params := &Param{Shards: shards, Protofiles: protofiles} + // generate hub hpp + hppFilename := "hub." + pcExt + ".h" + g1 := gen.NewGeneratedFile(hppFilename, "") + helper.GenerateCommonHeader(gen, g1, version) + g1.P() + if err := tpl.Lookup("hub.pc.h.tpl").Execute(g1, params); err != nil { + panic(err) } -} - -func generateHubCppMsgContainerCtor(g *protogen.GeneratedFile, protofiles []string, fileMessagers map[string][]string) { - for _, proto := range protofiles { - for _, messager := range fileMessagers[proto] { - g.P(helper.Indent(1), strcase.ToSnake(messager), "_ = std::dynamic_pointer_cast<", messager, `>(GetMessager(`, messager, `::Name()));`) - } + // generate hub cpp + cppFilename := "hub." + pcExt + ".cc" + g2 := gen.NewGeneratedFile(cppFilename, "") + helper.GenerateCommonHeader(gen, g2, version) + g2.P() + if err := tpl.Lookup("hub.pc.cc.tpl").Execute(g2, params); err != nil { + panic(err) } -} - -func generateHubCppRegistry(g *protogen.GeneratedFile, protofiles []string, fileMessagers map[string][]string) { - for _, proto := range protofiles { - for _, messager := range fileMessagers[proto] { - g.P(helper.Indent(2), "Register<", messager, ">();") + // generate shards + for i := 0; i < realShardNum; i++ { + shardSize := int(math.Ceil(float64(len(protofiles)) / float64((realShardNum)))) + begin := i * shardSize + end := (i + 1) * shardSize + if end > len(protofiles) { + end = len(protofiles) + } + type Param struct { + Shard int + Protofiles helper.ProtoFiles + } + params := &Param{Shard: i, Protofiles: protofiles[begin:end]} + cppFilename := "hub_shard" + strconv.Itoa(i) + "." + pcExt + ".cc" + g := gen.NewGeneratedFile(cppFilename, "") + helper.GenerateCommonHeader(gen, g, version) + g.P() + if err := tpl.Lookup("hub_shard.pc.cc.tpl").Execute(g, params); err != nil { + panic(err) } } } - -const hubHpp = `#pragma once -#include -#include -#include -#include -#include -#include - -#include "load.pc.h" -#include "scheduler.pc.h" - -namespace tableau { -class MessagerContainer; -class Hub; - -using MessagerMap = std::unordered_map>; -// FilterFunc filter in messagers if returned value is true. -// NOTE: name is the protobuf message name, e.g.: "message ItemConf{...}". -using Filter = std::function; -// MessagerContainerProvider provides a custom MessagerContainer for hub. -// If not specified, the hub's default MessagerContainer will be used. -// NOTE: This func must return non-nil MessagerContainer. -using MessagerContainerProvider = std::function(const Hub&)>; - -struct HubOptions { - // Filter can only filter in certain specific messagers based on the - // condition that you provide. - Filter filter; - // Provide custom MessagerContainer. For keeping configuration access - // consistent in a coroutine or a transaction. - MessagerContainerProvider provider; -}; - -class Hub { - public: - Hub(); - - // InitOnce inits the hub only once, and the subsequent calls will not take effect. - void InitOnce(std::shared_ptr options); - - /***** Synchronous Loading *****/ - // Load fills messages (in MessagerContainer) from files in the specified directory and format. - bool Load(const std::filesystem::path& dir, Format fmt = Format::kJSON, - std::shared_ptr options = nullptr); - - /***** Asynchronous Loading *****/ - // Load configs into temp MessagerContainer, and you should call LoopOnce() in you app's main loop, - // in order to take the temp MessagerContainer into effect. - bool AsyncLoad(const std::filesystem::path& dir, Format fmt = Format::kJSON, - std::shared_ptr options = nullptr); - int LoopOnce(); - // You'd better initialize the scheduler in the main thread. - void InitScheduler(); - - /***** MessagerMap *****/ - std::shared_ptr GetMessagerMap() const; - void SetMessagerMap(std::shared_ptr msger_map); - - /***** MessagerContainer *****/ - // This function is exposed only for use in MessagerContainerProvider. - std::shared_ptr GetMessagerContainer() const { return msger_container_; } - - /***** Access APIs *****/ - template - const std::shared_ptr Get() const; - - template - const U* Get(Args... args) const; - - template - const U* GetOrderedMap(Args... args) const; - - // GetLastLoadedTime returns the time when hub's msger_container_ was last set. - inline std::time_t GetLastLoadedTime() const; - - private: - std::shared_ptr InternalLoad(const std::filesystem::path& dir, Format fmt = Format::kJSON, - std::shared_ptr options = nullptr) const; - std::shared_ptr NewMessagerMap() const; - std::shared_ptr GetMessagerContainerWithProvider() const; - const std::shared_ptr GetMessager(const std::string& name) const; - - bool Postprocess(std::shared_ptr msger_map); - - private: - // For thread-safe guarantee during configuration updating. - std::mutex mutex_; - // All messagers' container. - std::shared_ptr msger_container_; - // Loading scheduler. - internal::Scheduler* sched_ = nullptr; - // Init once - std::once_flag init_once_; - // Hub options - std::shared_ptr options_; -}; - -template -const std::shared_ptr Hub::Get() const { - auto msg = GetMessager(T::Name()); - return std::dynamic_pointer_cast(msg); -} - -template -const U* Hub::Get(Args... args) const { - auto msger = Get(); - return msger ? msger->Get(args...) : nullptr; -} - -template -const U* Hub::GetOrderedMap(Args... args) const { - auto msger = Get(); - return msger ? msger->GetOrderedMap(args...) : nullptr; -} -` - -const msgContainerHpp = `class MessagerContainer { - friend class Hub; - - public: - MessagerContainer(std::shared_ptr msger_map = nullptr); - - private: - std::shared_ptr msger_map_; - std::time_t last_loaded_time_; - - private: - const std::shared_ptr GetMessager(const std::string& name) const;` - -const registryHpp = `}; - -using MessagerGenerator = std::function()>; -// messager name -> messager generator -using Registrar = std::unordered_map; -class Registry { - friend class Hub; - - public: - static void Init(); - - template - static void Register(); - - private:` - -const bottomHpp = ` static std::once_flag once; - static Registrar registrar; -}; - -template -void Registry::Register() { - registrar[T::Name()] = []() { return std::make_shared(); }; -} -} // namespace tableau` - -const hubCppHeader = `#include "hub.pc.h" - -#include "load.pc.h" -#include "logger.pc.h" -#include "util.pc.h"` - -const hubCpp = ` -namespace tableau { -std::once_flag Registry::once; -Registrar Registry::registrar; - -Hub::Hub() { tableau::Registry::Init(); } - -void Hub::InitOnce(std::shared_ptr options) { - std::call_once(init_once_, [&]() { options_ = options; }); -} - -bool Hub::Load(const std::filesystem::path& dir, Format fmt /* = Format::kJSON */, - std::shared_ptr options /* = nullptr */) { - auto msger_map = InternalLoad(dir, fmt, options); - if (!msger_map) { - return false; - } - bool ok = Postprocess(msger_map); - if (!ok) { - return false; - } - SetMessagerMap(msger_map); - return true; -} - -bool Hub::AsyncLoad(const std::filesystem::path& dir, Format fmt /* = Format::kJSON */, - std::shared_ptr options /* = nullptr */) { - auto msger_map = InternalLoad(dir, fmt, options); - if (!msger_map) { - return false; - } - bool ok = Postprocess(msger_map); - if (!ok) { - return false; - } - sched_->Dispatch(std::bind(&Hub::SetMessagerMap, this, msger_map)); - return true; -} - -int Hub::LoopOnce() { return sched_->LoopOnce(); } - -void Hub::InitScheduler() { - sched_ = new internal::Scheduler(); - sched_->Current(); -} - -std::shared_ptr Hub::InternalLoad(const std::filesystem::path& dir, Format fmt /* = Format::kJSON */, - std::shared_ptr options /* = nullptr */) const { - // intercept protobuf error logs - auto old_handler = google::protobuf::SetLogHandler(util::ProtobufLogHandler); - auto msger_map = NewMessagerMap(); - options = options ? options : std::make_shared(); - for (auto iter : *msger_map) { - auto&& name = iter.first; - ATOM_DEBUG("loading %s", name.c_str()); - auto mopts = options->ParseMessagerOptionsByName(name); - bool ok = iter.second->Load(dir, fmt, mopts); - if (!ok) { - ATOM_ERROR("load %s failed: %s", name.c_str(), GetErrMsg().c_str()); - // restore to old protobuf log handler - google::protobuf::SetLogHandler(old_handler); - return nullptr; - } - ATOM_DEBUG("loaded %s", name.c_str()); - } - - // restore to old protobuf log handler - google::protobuf::SetLogHandler(old_handler); - return msger_map; -} - -std::shared_ptr Hub::NewMessagerMap() const { - std::shared_ptr msger_map = std::make_shared(); - for (auto&& it : Registry::registrar) { - if (!options_ || !options_->filter || options_->filter(it.first)) { - (*msger_map)[it.first] = it.second(); - } - } - return msger_map; -} - -std::shared_ptr Hub::GetMessagerMap() const { return GetMessagerContainerWithProvider()->msger_map_; } - -void Hub::SetMessagerMap(std::shared_ptr msger_map) { - // replace with thread-safe guarantee. - std::unique_lock lock(mutex_); - msger_container_ = std::make_shared(msger_map); -} - -const std::shared_ptr Hub::GetMessager(const std::string& name) const { - return GetMessagerContainerWithProvider()->GetMessager(name); -} - -std::shared_ptr Hub::GetMessagerContainerWithProvider() const { - if (options_ && options_->provider) { - return options_->provider(*this); - } - return msger_container_; -} - -bool Hub::Postprocess(std::shared_ptr msger_map) { - // create a temporary hub with messager container for post process - Hub tmp_hub; - tmp_hub.SetMessagerMap(msger_map); - - // messager-level postprocess - for (auto iter : *msger_map) { - auto msger = iter.second; - bool ok = msger->ProcessAfterLoadAll(tmp_hub); - if (!ok) { - SetErrMsg("hub call ProcessAfterLoadAll failed, messager: " + iter.first); - return false; - } - } - return true; -} - -std::time_t Hub::GetLastLoadedTime() const { return GetMessagerContainerWithProvider()->last_loaded_time_; }` - -const msgContainerCpp = ` -MessagerContainer::MessagerContainer(std::shared_ptr msger_map /* = nullptr*/) - : msger_map_(msger_map ? msger_map : std::make_shared()), last_loaded_time_(std::time(nullptr)) {` - -const registryCpp = `} - -const std::shared_ptr MessagerContainer::GetMessager(const std::string& name) const { - if (msger_map_) { - auto it = msger_map_->find(name); - if (it != msger_map_->end()) { - return it->second; - } - } - return nullptr; -} - -void Registry::Init() { - std::call_once(once, []() {` - -const bottomCpp = ` }); -} -} // namespace tableau` diff --git a/cmd/protoc-gen-cpp-tableau-loader/shard.go b/cmd/protoc-gen-cpp-tableau-loader/shard.go deleted file mode 100644 index 0d9121dd..00000000 --- a/cmd/protoc-gen-cpp-tableau-loader/shard.go +++ /dev/null @@ -1,127 +0,0 @@ -package main - -import ( - "math" - "strconv" - - "github.com/iancoleman/strcase" - "github.com/tableauio/loader/cmd/protoc-gen-cpp-tableau-loader/helper" - "google.golang.org/protobuf/compiler/protogen" -) - -// generateShardedHub generates related hub files. -func generateShardedHub(gen *protogen.Plugin) { - protofiles, fileMessagers := getAllOrderedFilesAndMessagers(gen) - - // detect real shard num - realShardNum := *shards - if realShardNum > len(protofiles) { - realShardNum = len(protofiles) - } - shardSize := int(math.Ceil(float64(len(protofiles)) / float64((realShardNum)))) - - hppFilename := "hub." + pcExt + ".h" - g1 := gen.NewGeneratedFile(hppFilename, "") - helper.GenerateCommonHeader(gen, g1, version) - g1.P() - g1.P(hubHpp) - generateHubHppTplSpec(g1, protofiles, fileMessagers) - g1.P(msgContainerHpp) - generateShardedHubHppMsgContainerShards(g1, realShardNum) - generateHubHppMsgContainerMembers(g1, protofiles, fileMessagers) - g1.P(registryHpp) - generateShardedHubHppRegistryShards(g1, realShardNum) - g1.P(bottomHpp) - - cppFilename := "hub." + pcExt + ".cc" - g2 := gen.NewGeneratedFile(cppFilename, "") - helper.GenerateCommonHeader(gen, g2, version) - g2.P() - g2.P(hubCppHeader) - g2.P(hubCpp) - g2.P(msgContainerCpp) - generateShardedHubCppMsgContainerShards(g2, realShardNum) - g2.P(registryCpp) - generateShardedHubCppRegistryShards(g2, realShardNum) - g2.P(bottomCpp) - - for i := 0; i < realShardNum; i++ { - cursor := (i + 1) * shardSize - if cursor > len(protofiles) { - cursor = len(protofiles) - } - cppFilename := "hub_shard" + strconv.Itoa(i) + "." + pcExt + ".cc" - g := gen.NewGeneratedFile(cppFilename, "") - helper.GenerateCommonHeader(gen, g, version) - g.P() - shardedProtofiles := protofiles[i*shardSize : cursor] - generateShardedHubCppFileContent(g, i, shardedProtofiles, fileMessagers) - } -} - -func generateShardedHubHppMsgContainerShards(g *protogen.GeneratedFile, shardNum int) { - for i := 0; i < shardNum; i++ { - g.P(helper.Indent(1), "void InitShard", i, "();") - } - g.P() - g.P(" private:") -} - -func generateShardedHubHppRegistryShards(g *protogen.GeneratedFile, shardNum int) { - for i := 0; i < shardNum; i++ { - g.P(helper.Indent(1), "static void InitShard", i, "();") - } - g.P() - g.P(" private:") -} - -func generateShardedHubCppMsgContainerShards(g *protogen.GeneratedFile, shardNum int) { - for i := 0; i < shardNum; i++ { - g.P(helper.Indent(1), "InitShard", i, "();") - } -} - -func generateShardedHubCppRegistryShards(g *protogen.GeneratedFile, shardNum int) { - for i := 0; i < shardNum; i++ { - g.P(helper.Indent(2), "InitShard", i, "();") - } -} - -func generateShardedHubCppFileContent(g *protogen.GeneratedFile, shardIndex int, protofiles []string, fileMessagers map[string][]string) { - g.P(`#include "`, "hub.", pcExt, `.h"`) - g.P() - for _, proto := range protofiles { - g.P(`#include "`, proto, ".", pcExt, `.h"`) - } - g.P() - g.P("namespace ", *namespace, " {") - - for _, proto := range protofiles { - for _, messager := range fileMessagers[proto] { - g.P("template <>") - g.P("const std::shared_ptr<", messager, "> Hub::Get<", messager, ">() const {") - g.P(helper.Indent(1), "return GetMessagerContainerWithProvider()->", strcase.ToSnake(messager), "_;") - g.P("}") - g.P() - } - } - - g.P("void MessagerContainer::InitShard", shardIndex, "() {") - for _, proto := range protofiles { - for _, messager := range fileMessagers[proto] { - g.P(helper.Indent(1), strcase.ToSnake(messager), "_ = std::dynamic_pointer_cast<", messager, `>(GetMessager(`, messager, `::Name()));`) - } - } - g.P("}") - g.P() - - g.P("void Registry::InitShard", shardIndex, "() {") - for _, proto := range protofiles { - for _, messager := range fileMessagers[proto] { - g.P(helper.Indent(1), "Register<", messager, ">();") - } - } - g.P("}") - - g.P("} // namespace ", *namespace) -} diff --git a/test/cpp-tableau-loader/src/protoconf/hub.pc.h b/test/cpp-tableau-loader/src/protoconf/hub.pc.h index 17645aca..712ff670 100644 --- a/test/cpp-tableau-loader/src/protoconf/hub.pc.h +++ b/test/cpp-tableau-loader/src/protoconf/hub.pc.h @@ -164,16 +164,14 @@ class MessagerContainer { public: MessagerContainer(std::shared_ptr msger_map = nullptr); - private: - std::shared_ptr msger_map_; - std::time_t last_loaded_time_; - private: const std::shared_ptr GetMessager(const std::string& name) const; void InitShard0(); void InitShard1(); private: + std::shared_ptr msger_map_; + std::time_t last_loaded_time_; std::shared_ptr hero_base_conf_; std::shared_ptr hero_conf_; std::shared_ptr item_conf_; diff --git a/test/cpp-tableau-loader/src/protoconf/hub_shard0.pc.cc b/test/cpp-tableau-loader/src/protoconf/hub_shard0.pc.cc index e25cad93..966c0dbf 100644 --- a/test/cpp-tableau-loader/src/protoconf/hub_shard0.pc.cc +++ b/test/cpp-tableau-loader/src/protoconf/hub_shard0.pc.cc @@ -5,8 +5,10 @@ #include "hub.pc.h" +// clang-format off #include "hero_conf.pc.h" #include "item_conf.pc.h" +// clang-format on namespace tableau { template <> diff --git a/test/cpp-tableau-loader/src/protoconf/hub_shard1.pc.cc b/test/cpp-tableau-loader/src/protoconf/hub_shard1.pc.cc index b63c90b1..b0ed56e7 100644 --- a/test/cpp-tableau-loader/src/protoconf/hub_shard1.pc.cc +++ b/test/cpp-tableau-loader/src/protoconf/hub_shard1.pc.cc @@ -5,8 +5,10 @@ #include "hub.pc.h" +// clang-format off #include "patch_conf.pc.h" #include "test_conf.pc.h" +// clang-format on namespace tableau { template <> From 7e07f7dad74d5d40fa2b56ed71da290ecec1a594 Mon Sep 17 00:00:00 2001 From: Kybxd <627940450@qq.com> Date: Thu, 4 Sep 2025 15:11:03 +0800 Subject: [PATCH 2/6] feat: gen go hub files by tpl --- cmd/protoc-gen-go-tableau-loader/embed.go | 6 + .../embed/templates/hub.pc.go.tpl | 335 ++++++++++++++++ cmd/protoc-gen-go-tableau-loader/hub.go | 356 +----------------- 3 files changed, 348 insertions(+), 349 deletions(-) create mode 100644 cmd/protoc-gen-go-tableau-loader/embed.go create mode 100644 cmd/protoc-gen-go-tableau-loader/embed/templates/hub.pc.go.tpl diff --git a/cmd/protoc-gen-go-tableau-loader/embed.go b/cmd/protoc-gen-go-tableau-loader/embed.go new file mode 100644 index 00000000..368c8c5c --- /dev/null +++ b/cmd/protoc-gen-go-tableau-loader/embed.go @@ -0,0 +1,6 @@ +package main + +import "embed" + +//go:embed embed/* +var efs embed.FS diff --git a/cmd/protoc-gen-go-tableau-loader/embed/templates/hub.pc.go.tpl b/cmd/protoc-gen-go-tableau-loader/embed/templates/hub.pc.go.tpl new file mode 100644 index 00000000..b1928436 --- /dev/null +++ b/cmd/protoc-gen-go-tableau-loader/embed/templates/hub.pc.go.tpl @@ -0,0 +1,335 @@ +import ( + "fmt" + "os" + "sync" + "sync/atomic" + "time" + + "github.com/pkg/errors" + "github.com/pmezard/go-difflib/difflib" + "github.com/tableauio/tableau/format" + "github.com/tableauio/tableau/load" + "github.com/tableauio/tableau/store" + "google.golang.org/protobuf/proto" +) + +type Messager interface { + // Name returns the unique message name. + Name() string + // GetStats returns stats info. + GetStats() *Stats + // Load fills message from file in the specified directory and format. + Load(dir string, fmt format.Format, opts *load.MessagerOptions) error + // Store writes message to file in the specified directory and format. + Store(dir string, fmt format.Format, options ...store.Option) error + // processAfterLoad is invoked after this messager loaded. + processAfterLoad() error + // ProcessAfterLoadAll is invoked after all messagers loaded. + ProcessAfterLoadAll(hub *Hub) error + // Message returns the inner message data. + Message() proto.Message + // Messager returns the current messager. + Messager() Messager + // originalMessage returns the original inner message data. + originalMessage() proto.Message + // enableBackup tells each messager to backup original inner message data. + enableBackup() +} + +type Options struct { + // Filter can only filter in certain specific messagers based on the + // condition that you provide. + // + // Default: nil. + Filter FilterFunc + + // MutableCheck enables the mutable check of the loaded config, + // and specifies its interval and mutable handler. + // + // Default: nil. + MutableCheck *MutableCheck +} + +// FilterFunc filter in messagers if returned value is true. +// +// NOTE: name is the protobuf message name, e.g.: "message ItemConf{...}". +type FilterFunc func(name string) bool + +type MutableCheck struct { + // Interval is the gap duration between two checks. + // Default: 60s. + Interval time.Duration + // OnMutate is called when encouters mutations, with messager's name, + // original message and current message. + OnMutate func(name string, original, current proto.Message) +} + +// Option is the functional option type. +type Option func(*Options) + +// newDefault returns a default Options. +func newDefault() *Options { + return &Options{} +} + +// ParseOptions parses functional options and merge them to default Options. +func ParseOptions(setters ...Option) *Options { + // Default Options + opts := newDefault() + for _, setter := range setters { + setter(opts) + } + return opts +} + +// Filter can only filter in certain specific messagers based on the +// condition that you provide. +func Filter(filter FilterFunc) Option { + return func(opts *Options) { + opts.Filter = filter + } +} + +// WithMutableCheck enables the mutable check with given params. +func WithMutableCheck(check *MutableCheck) Option { + return func(opts *Options) { + opts.MutableCheck = check + } +} + +type Stats struct { + Duration time.Duration // total load time consuming. +} + +type UnimplementedMessager struct { + Stats Stats + backup bool +} + +func (x *UnimplementedMessager) Name() string { + return "" +} + +func (x *UnimplementedMessager) GetStats() *Stats { + return &x.Stats +} + +func (x *UnimplementedMessager) Load(dir string, format format.Format, opts *load.MessagerOptions) error { + return nil +} + +func (x *UnimplementedMessager) Store(dir string, format format.Format, options ...store.Option) error { + return nil +} + +func (x *UnimplementedMessager) processAfterLoad() error { + return nil +} + +func (x *UnimplementedMessager) ProcessAfterLoadAll(hub *Hub) error { + return nil +} + +func (x *UnimplementedMessager) Message() proto.Message { + return nil +} + +func (x *UnimplementedMessager) Messager() Messager { + return nil +} + +func (x *UnimplementedMessager) enableBackup() { + x.backup = true +} + +func (x *UnimplementedMessager) originalMessage() proto.Message { + return nil +} + +type MessagerMap = map[string]Messager +type MessagerGenerator = func() Messager +type Registrar struct { + Generators map[string]MessagerGenerator +} + +func NewRegistrar() *Registrar { + return &Registrar{ + Generators: map[string]MessagerGenerator{}, + } +} + +func (r *Registrar) Register(gen MessagerGenerator) { + if _, ok := r.Generators[gen().Name()]; ok { + panic("register duplicate messager: " + gen().Name()) + } + r.Generators[gen().Name()] = gen +} + +var registrarSingleton *Registrar +var once sync.Once + +func getRegistrar() *Registrar { + once.Do(func() { + registrarSingleton = NewRegistrar() + }) + return registrarSingleton +} + +func Register(gen MessagerGenerator) { + getRegistrar().Register(gen) +} + +func BoolToInt(ok bool) int { + if ok { + return 1 + } + return 0 +} + +// Hub is the messager manager. +type Hub struct { + messagerContainer atomic.Pointer[messagerContainer] + opts *Options +} + +func NewHub(options ...Option) *Hub { + hub := &Hub{} + hub.messagerContainer.Store(&messagerContainer{}) + hub.opts = ParseOptions(options...) + if hub.opts.MutableCheck != nil { + go hub.mutableCheck() + } + return hub +} + +// NewMessagerMap creates a new MessagerMap. +func (h *Hub) NewMessagerMap() MessagerMap { + messagerMap := MessagerMap{} + for name, gen := range getRegistrar().Generators { + if h.opts.Filter == nil || h.opts.Filter(name) { + messager := gen() + if h.opts.MutableCheck != nil { + messager.enableBackup() + } + messagerMap[name] = messager + } + } + return messagerMap +} + +// GetMessagerMap returns hub's inner field messagerMap. +func (h *Hub) GetMessagerMap() MessagerMap { + return h.messagerContainer.Load().messagerMap +} + +// SetMessagerMap sets hub's inner field messagerMap. +func (h *Hub) SetMessagerMap(messagerMap MessagerMap) { + h.messagerContainer.Store(newMessagerContainer(messagerMap)) +} + +// GetMessager finds and returns the specified Messenger in hub. +func (h *Hub) GetMessager(name string) Messager { + return h.GetMessagerMap()[name] +} + +// Load fills messages from files in the specified directory and format. +func (h *Hub) Load(dir string, format format.Format, options ...load.Option) error { + messagerMap := h.NewMessagerMap() + opts := load.ParseOptions(options...) + for name, msger := range messagerMap { + mopts := opts.ParseMessagerOptionsByName(name) + if err := msger.Load(dir, format, mopts); err != nil { + return errors.WithMessagef(err, "failed to load: %v", name) + } + } + // create a temporary hub with messager container for post process + tmpHub := &Hub{} + tmpHub.SetMessagerMap(messagerMap) + for name, msger := range messagerMap { + if err := msger.ProcessAfterLoadAll(tmpHub); err != nil { + return errors.WithMessagef(err, "failed to process messager %s after load all", name) + } + } + h.SetMessagerMap(messagerMap) + return nil +} + +// Store stores protobuf messages to files in the specified directory and format. +// Available formats: JSON, Bin, and Text. +func (h *Hub) Store(dir string, format format.Format, options ...store.Option) error { + opts := store.ParseOptions(options...) + for name, msger := range h.GetMessagerMap() { + if opts.Filter == nil || opts.Filter(name) { + if err := msger.Store(dir, format, options...); err != nil { + return errors.WithMessagef(err, "failed to store: %v", name) + } + } + } + return nil +} + +// mutableCheck checks if the messagers are mutable or not. +func (h *Hub) mutableCheck() { + interval := h.opts.MutableCheck.Interval + if interval == 0 { + interval = time.Minute + } + handler := h.opts.MutableCheck.OnMutate + if handler == nil { + handler = h.onMutateDefault + } + for { + time.Sleep(interval) + messagerMap := h.GetMessagerMap() + for name, msger := range messagerMap { + time.Sleep(time.Second) + if !proto.Equal(msger.originalMessage(), msger.Message()) { + handler(name, msger.originalMessage(), msger.Message()) + } + } + } +} + +func (h *Hub) onMutateDefault(name string, original, current proto.Message) { + originalText, _ := store.MarshalToText(original, true) + currentText, _ := store.MarshalToText(current, true) + diff := difflib.UnifiedDiff{ + A: difflib.SplitLines(string(originalText)), + B: difflib.SplitLines(string(currentText)), + FromFile: "Original", + ToFile: "Current", + Context: 3, + } + text, _ := difflib.GetUnifiedDiffString(diff) + fmt.Fprintf(os.Stderr, + "==== %s DIFF BEGIN ====\n%s==== %s DIFF END ====\n", + name, text, name) +} + +// GetLastLoadedTime returns the time when hub's messagerMap was last set. +func (h *Hub) GetLastLoadedTime() time.Time { + return h.messagerContainer.Load().loadedTime +} + +type messagerContainer struct { + messagerMap MessagerMap + loadedTime time.Time + // all messagers as fields for fast access +{{ range . }} {{ toLowerCamel . }} *{{ . }} +{{ end }}} + +func newMessagerContainer(messagerMap MessagerMap) *messagerContainer { + messagerContainer := &messagerContainer{ + messagerMap: messagerMap, + loadedTime: time.Now(), + } +{{ range . }} messagerContainer.{{ toLowerCamel . }}, _ = messagerMap[(&{{ . }}{}).Name()].(*{{ . }}) +{{ end }} return messagerContainer +} + +// Auto-generated getters below +{{ range . }} +func (h *Hub) Get{{ . }}() *{{ . }} { + return h.messagerContainer.Load().{{ toLowerCamel . }} +} +{{ end }} \ No newline at end of file diff --git a/cmd/protoc-gen-go-tableau-loader/hub.go b/cmd/protoc-gen-go-tableau-loader/hub.go index 7c178dc9..49106974 100644 --- a/cmd/protoc-gen-go-tableau-loader/hub.go +++ b/cmd/protoc-gen-go-tableau-loader/hub.go @@ -2,11 +2,16 @@ package main import ( "path/filepath" + "text/template" "github.com/iancoleman/strcase" "google.golang.org/protobuf/compiler/protogen" ) +var tpl = template.Must(template.New("").Funcs(template.FuncMap{ + "toLowerCamel": strcase.ToLowerCamel, +}).ParseFS(efs, "embed/templates/*")) + // generateHub generates related hub files. func generateHub(gen *protogen.Plugin) { filename := filepath.Join("hub." + pcExt + ".go") @@ -15,354 +20,7 @@ func generateHub(gen *protogen.Plugin) { g.P() g.P("package ", *pkg) g.P() - g.P(staticHubContent) - g.P() - - // generate messager container type - g.P("type messagerContainer struct {") - g.P("messagerMap MessagerMap") - g.P("loadedTime time.Time") - g.P("// all messagers as fields for fast access") - for _, messager := range messagers { - g.P(strcase.ToLowerCamel(messager), " *", messager) - } - g.P("}") - g.P() - - // generate messager container constructor - g.P("func newMessagerContainer(messagerMap MessagerMap) *messagerContainer {") - g.P("messagerContainer := &messagerContainer{") - g.P("messagerMap: messagerMap,") - g.P("loadedTime: time.Now(),") - g.P("}") - for _, messager := range messagers { - g.P("messagerContainer.", strcase.ToLowerCamel(messager), `, _ = messagerMap[(&`, messager, `{}).Name()].(*`, messager, ")") - } - g.P("return messagerContainer") - g.P("}") - g.P() - - // generate getters - g.P("// Auto-generated getters below") - g.P() - for _, messager := range messagers { - g.P("func (h *Hub) Get", messager, "() *", messager, " {") - g.P("return h.messagerContainer.Load().", strcase.ToLowerCamel(messager)) - g.P("}") - g.P() - } -} - -const staticHubContent = `import ( - "fmt" - "os" - "sync" - "sync/atomic" - "time" - - "github.com/pkg/errors" - "github.com/pmezard/go-difflib/difflib" - "github.com/tableauio/tableau/format" - "github.com/tableauio/tableau/load" - "github.com/tableauio/tableau/store" - "google.golang.org/protobuf/proto" -) - -type Messager interface { - // Name returns the unique message name. - Name() string - // GetStats returns stats info. - GetStats() *Stats - // Load fills message from file in the specified directory and format. - Load(dir string, fmt format.Format, opts *load.MessagerOptions) error - // Store writes message to file in the specified directory and format. - Store(dir string, fmt format.Format, options ...store.Option) error - // processAfterLoad is invoked after this messager loaded. - processAfterLoad() error - // ProcessAfterLoadAll is invoked after all messagers loaded. - ProcessAfterLoadAll(hub *Hub) error - // Message returns the inner message data. - Message() proto.Message - // Messager returns the current messager. - Messager() Messager - // originalMessage returns the original inner message data. - originalMessage() proto.Message - // enableBackup tells each messager to backup original inner message data. - enableBackup() -} - -type Options struct { - // Filter can only filter in certain specific messagers based on the - // condition that you provide. - // - // Default: nil. - Filter FilterFunc - - // MutableCheck enables the mutable check of the loaded config, - // and specifies its interval and mutable handler. - // - // Default: nil. - MutableCheck *MutableCheck -} - -// FilterFunc filter in messagers if returned value is true. -// -// NOTE: name is the protobuf message name, e.g.: "message ItemConf{...}". -type FilterFunc func(name string) bool - -type MutableCheck struct { - // Interval is the gap duration between two checks. - // Default: 60s. - Interval time.Duration - // OnMutate is called when encouters mutations, with messager's name, - // original message and current message. - OnMutate func(name string, original, current proto.Message) -} - -// Option is the functional option type. -type Option func(*Options) - -// newDefault returns a default Options. -func newDefault() *Options { - return &Options{} -} - -// ParseOptions parses functional options and merge them to default Options. -func ParseOptions(setters ...Option) *Options { - // Default Options - opts := newDefault() - for _, setter := range setters { - setter(opts) - } - return opts -} - -// Filter can only filter in certain specific messagers based on the -// condition that you provide. -func Filter(filter FilterFunc) Option { - return func(opts *Options) { - opts.Filter = filter - } -} - -// WithMutableCheck enables the mutable check with given params. -func WithMutableCheck(check *MutableCheck) Option { - return func(opts *Options) { - opts.MutableCheck = check - } -} - -type Stats struct { - Duration time.Duration // total load time consuming. -} - -type UnimplementedMessager struct { - Stats Stats - backup bool -} - -func (x *UnimplementedMessager) Name() string { - return "" -} - -func (x *UnimplementedMessager) GetStats() *Stats { - return &x.Stats -} - -func (x *UnimplementedMessager) Load(dir string, format format.Format, opts *load.MessagerOptions) error { - return nil -} - -func (x *UnimplementedMessager) Store(dir string, format format.Format, options ...store.Option) error { - return nil -} - -func (x *UnimplementedMessager) processAfterLoad() error { - return nil -} - -func (x *UnimplementedMessager) ProcessAfterLoadAll(hub *Hub) error { - return nil -} - -func (x *UnimplementedMessager) Message() proto.Message { - return nil -} - -func (x *UnimplementedMessager) Messager() Messager { - return nil -} - -func (x *UnimplementedMessager) enableBackup() { - x.backup = true -} - -func (x *UnimplementedMessager) originalMessage() proto.Message { - return nil -} - -type MessagerMap = map[string]Messager -type MessagerGenerator = func() Messager -type Registrar struct { - Generators map[string]MessagerGenerator -} - -func NewRegistrar() *Registrar { - return &Registrar{ - Generators: map[string]MessagerGenerator{}, - } -} - -func (r *Registrar) Register(gen MessagerGenerator) { - if _, ok := r.Generators[gen().Name()]; ok { - panic("register duplicate messager: " + gen().Name()) - } - r.Generators[gen().Name()] = gen -} - -var registrarSingleton *Registrar -var once sync.Once - -func getRegistrar() *Registrar { - once.Do(func() { - registrarSingleton = NewRegistrar() - }) - return registrarSingleton -} - -func Register(gen MessagerGenerator) { - getRegistrar().Register(gen) -} - -func BoolToInt(ok bool) int { - if ok { - return 1 - } - return 0 -} - -// Hub is the messager manager. -type Hub struct { - messagerContainer atomic.Pointer[messagerContainer] - opts *Options -} - -func NewHub(options ...Option) *Hub { - hub := &Hub{} - hub.messagerContainer.Store(&messagerContainer{}) - hub.opts = ParseOptions(options...) - if hub.opts.MutableCheck != nil { - go hub.mutableCheck() + if err := tpl.Lookup("hub.pc.go.tpl").Execute(g, messagers); err != nil { + panic(err) } - return hub -} - -// NewMessagerMap creates a new MessagerMap. -func (h *Hub) NewMessagerMap() MessagerMap { - messagerMap := MessagerMap{} - for name, gen := range getRegistrar().Generators { - if h.opts.Filter == nil || h.opts.Filter(name) { - messager := gen() - if h.opts.MutableCheck != nil { - messager.enableBackup() - } - messagerMap[name] = messager - } - } - return messagerMap -} - -// GetMessagerMap returns hub's inner field messagerMap. -func (h *Hub) GetMessagerMap() MessagerMap { - return h.messagerContainer.Load().messagerMap -} - -// SetMessagerMap sets hub's inner field messagerMap. -func (h *Hub) SetMessagerMap(messagerMap MessagerMap) { - h.messagerContainer.Store(newMessagerContainer(messagerMap)) -} - -// GetMessager finds and returns the specified Messenger in hub. -func (h *Hub) GetMessager(name string) Messager { - return h.GetMessagerMap()[name] -} - -// Load fills messages from files in the specified directory and format. -func (h *Hub) Load(dir string, format format.Format, options ...load.Option) error { - messagerMap := h.NewMessagerMap() - opts := load.ParseOptions(options...) - for name, msger := range messagerMap { - mopts := opts.ParseMessagerOptionsByName(name) - if err := msger.Load(dir, format, mopts); err != nil { - return errors.WithMessagef(err, "failed to load: %v", name) - } - } - // create a temporary hub with messager container for post process - tmpHub := &Hub{} - tmpHub.SetMessagerMap(messagerMap) - for name, msger := range messagerMap { - if err := msger.ProcessAfterLoadAll(tmpHub); err != nil { - return errors.WithMessagef(err, "failed to process messager %s after load all", name) - } - } - h.SetMessagerMap(messagerMap) - return nil -} - -// Store stores protobuf messages to files in the specified directory and format. -// Available formats: JSON, Bin, and Text. -func (h *Hub) Store(dir string, format format.Format, options ...store.Option) error { - opts := store.ParseOptions(options...) - for name, msger := range h.GetMessagerMap() { - if opts.Filter == nil || opts.Filter(name) { - if err := msger.Store(dir, format, options...); err != nil { - return errors.WithMessagef(err, "failed to store: %v", name) - } - } - } - return nil -} - -// mutableCheck checks if the messagers are mutable or not. -func (h *Hub) mutableCheck() { - interval := h.opts.MutableCheck.Interval - if interval == 0 { - interval = time.Minute - } - handler := h.opts.MutableCheck.OnMutate - if handler == nil { - handler = h.onMutateDefault - } - for { - time.Sleep(interval) - messagerMap := h.GetMessagerMap() - for name, msger := range messagerMap { - time.Sleep(time.Second) - if !proto.Equal(msger.originalMessage(), msger.Message()) { - handler(name, msger.originalMessage(), msger.Message()) - } - } - } -} - -func (h *Hub) onMutateDefault(name string, original, current proto.Message) { - originalText, _ := store.MarshalToText(original, true) - currentText, _ := store.MarshalToText(current, true) - diff := difflib.UnifiedDiff{ - A: difflib.SplitLines(string(originalText)), - B: difflib.SplitLines(string(currentText)), - FromFile: "Original", - ToFile: "Current", - Context: 3, - } - text, _ := difflib.GetUnifiedDiffString(diff) - fmt.Fprintf(os.Stderr, - "==== %s DIFF BEGIN ====\n%s==== %s DIFF END ====\n", - name, text, name) -} - -// GetLastLoadedTime returns the time when hub's messagerMap was last set. -func (h *Hub) GetLastLoadedTime() time.Time { - return h.messagerContainer.Load().loadedTime } -` From 02bad7ea13453173bcaf0712532aff3afa658cde Mon Sep 17 00:00:00 2001 From: Kybxd <627940450@qq.com> Date: Thu, 4 Sep 2025 15:14:04 +0800 Subject: [PATCH 3/6] feat: use global package extensions --- cmd/protoc-gen-cpp-tableau-loader/hub.go | 7 ++++--- cmd/protoc-gen-cpp-tableau-loader/main.go | 2 -- cmd/protoc-gen-cpp-tableau-loader/messager.go | 13 +++++++------ cmd/protoc-gen-go-tableau-loader/errors.go | 5 ++--- cmd/protoc-gen-go-tableau-loader/helper.go | 3 --- cmd/protoc-gen-go-tableau-loader/hub.go | 4 ++-- cmd/protoc-gen-go-tableau-loader/messager.go | 4 ++-- internal/extensions/extensions.go | 4 ++++ 8 files changed, 21 insertions(+), 21 deletions(-) create mode 100644 internal/extensions/extensions.go diff --git a/cmd/protoc-gen-cpp-tableau-loader/hub.go b/cmd/protoc-gen-cpp-tableau-loader/hub.go index 9d120d32..cbdb2d68 100644 --- a/cmd/protoc-gen-cpp-tableau-loader/hub.go +++ b/cmd/protoc-gen-cpp-tableau-loader/hub.go @@ -7,6 +7,7 @@ import ( "github.com/iancoleman/strcase" "github.com/tableauio/loader/cmd/protoc-gen-cpp-tableau-loader/helper" + "github.com/tableauio/loader/internal/extensions" "google.golang.org/protobuf/compiler/protogen" ) @@ -35,7 +36,7 @@ func generateHub(gen *protogen.Plugin) { } params := &Param{Shards: shards, Protofiles: protofiles} // generate hub hpp - hppFilename := "hub." + pcExt + ".h" + hppFilename := "hub." + extensions.PC + ".h" g1 := gen.NewGeneratedFile(hppFilename, "") helper.GenerateCommonHeader(gen, g1, version) g1.P() @@ -43,7 +44,7 @@ func generateHub(gen *protogen.Plugin) { panic(err) } // generate hub cpp - cppFilename := "hub." + pcExt + ".cc" + cppFilename := "hub." + extensions.PC + ".cc" g2 := gen.NewGeneratedFile(cppFilename, "") helper.GenerateCommonHeader(gen, g2, version) g2.P() @@ -63,7 +64,7 @@ func generateHub(gen *protogen.Plugin) { Protofiles helper.ProtoFiles } params := &Param{Shard: i, Protofiles: protofiles[begin:end]} - cppFilename := "hub_shard" + strconv.Itoa(i) + "." + pcExt + ".cc" + cppFilename := "hub_shard" + strconv.Itoa(i) + "." + extensions.PC + ".cc" g := gen.NewGeneratedFile(cppFilename, "") helper.GenerateCommonHeader(gen, g, version) g.P() diff --git a/cmd/protoc-gen-cpp-tableau-loader/main.go b/cmd/protoc-gen-cpp-tableau-loader/main.go index 9c21ad00..d8e96799 100644 --- a/cmd/protoc-gen-cpp-tableau-loader/main.go +++ b/cmd/protoc-gen-cpp-tableau-loader/main.go @@ -10,8 +10,6 @@ import ( ) const version = "0.10.0" -const pcExt = "pc" // protoconf file extension -const pbExt = "pb" // protobuf file extension // specify protobuf namespace var namespace *string diff --git a/cmd/protoc-gen-cpp-tableau-loader/messager.go b/cmd/protoc-gen-cpp-tableau-loader/messager.go index 88326f21..7228a4fa 100644 --- a/cmd/protoc-gen-cpp-tableau-loader/messager.go +++ b/cmd/protoc-gen-cpp-tableau-loader/messager.go @@ -4,6 +4,7 @@ import ( "strings" "github.com/tableauio/loader/cmd/protoc-gen-cpp-tableau-loader/helper" + "github.com/tableauio/loader/internal/extensions" "github.com/tableauio/loader/internal/index" "github.com/tableauio/loader/internal/options" "github.com/tableauio/tableau/proto/tableaupb" @@ -22,7 +23,7 @@ func generateMessager(gen *protogen.Plugin, file *protogen.File) { // generateHppFile generates a header file corresponding to a protobuf file. func generateHppFile(gen *protogen.Plugin, file *protogen.File) *protogen.GeneratedFile { - filename := file.GeneratedFilenamePrefix + "." + pcExt + ".h" + filename := file.GeneratedFilenamePrefix + "." + extensions.PC + ".h" g := gen.NewGeneratedFile(filename, "") helper.GenerateFileHeader(gen, file, g, version) g.P() @@ -32,7 +33,7 @@ func generateHppFile(gen *protogen.Plugin, file *protogen.File) *protogen.Genera // generateCppFile generates loader files related to protoconf files. func generateCppFile(gen *protogen.Plugin, file *protogen.File) *protogen.GeneratedFile { - filename := file.GeneratedFilenamePrefix + "." + pcExt + ".cc" + filename := file.GeneratedFilenamePrefix + "." + extensions.PC + ".cc" g := gen.NewGeneratedFile(filename, "") helper.GenerateFileHeader(gen, file, g, version) g.P() @@ -46,9 +47,9 @@ func generateHppFileContent(file *protogen.File, g *protogen.GeneratedFile) { g.P("#include ") g.P("#include ") g.P() - g.P(`#include "`, "load.", pcExt, `.h"`) - g.P(`#include "`, "util.", pcExt, `.h"`) - g.P(`#include "`, file.GeneratedFilenamePrefix, ".", pbExt, `.h"`) + g.P(`#include "`, "load.", extensions.PC, `.h"`) + g.P(`#include "`, "util.", extensions.PC, `.h"`) + g.P(`#include "`, file.GeneratedFilenamePrefix, ".", extensions.PB, `.h"`) g.P() g.P("namespace ", *namespace, " {") @@ -136,7 +137,7 @@ func genHppMapGetters(depth int, keys []helper.MapKey, g *protogen.GeneratedFile // generateCppFileContent generates type implementations. func generateCppFileContent(file *protogen.File, g *protogen.GeneratedFile) { - g.P(`#include "`, file.GeneratedFilenamePrefix, ".", pcExt, `.h"`) + g.P(`#include "`, file.GeneratedFilenamePrefix, ".", extensions.PC, `.h"`) g.P() g.P(`#include "hub.pc.h"`) g.P(`#include "util.pc.h"`) diff --git a/cmd/protoc-gen-go-tableau-loader/errors.go b/cmd/protoc-gen-go-tableau-loader/errors.go index 52554f8d..3dd13be7 100644 --- a/cmd/protoc-gen-go-tableau-loader/errors.go +++ b/cmd/protoc-gen-go-tableau-loader/errors.go @@ -1,14 +1,13 @@ package main import ( - "path/filepath" - + "github.com/tableauio/loader/internal/extensions" "google.golang.org/protobuf/compiler/protogen" ) // generateError generates related error files. func generateError(gen *protogen.Plugin) { - filename := filepath.Join("errors." + pcExt + ".go") + filename := "errors." + extensions.PC + ".go" g := gen.NewGeneratedFile(filename, "") generateCommonHeader(gen, g) g.P() diff --git a/cmd/protoc-gen-go-tableau-loader/helper.go b/cmd/protoc-gen-go-tableau-loader/helper.go index 92d82123..a158013b 100644 --- a/cmd/protoc-gen-go-tableau-loader/helper.go +++ b/cmd/protoc-gen-go-tableau-loader/helper.go @@ -6,9 +6,6 @@ import ( "google.golang.org/protobuf/compiler/protogen" ) -const pcExt = "pc" // protoconf file extension -const pbExt = "pb" // protobuf file extension - func generateFileHeader(gen *protogen.Plugin, file *protogen.File, g *protogen.GeneratedFile) { generateCommonHeader(gen, g) if file.Proto.GetOptions().GetDeprecated() { diff --git a/cmd/protoc-gen-go-tableau-loader/hub.go b/cmd/protoc-gen-go-tableau-loader/hub.go index 49106974..cb5a76f4 100644 --- a/cmd/protoc-gen-go-tableau-loader/hub.go +++ b/cmd/protoc-gen-go-tableau-loader/hub.go @@ -1,10 +1,10 @@ package main import ( - "path/filepath" "text/template" "github.com/iancoleman/strcase" + "github.com/tableauio/loader/internal/extensions" "google.golang.org/protobuf/compiler/protogen" ) @@ -14,7 +14,7 @@ var tpl = template.Must(template.New("").Funcs(template.FuncMap{ // generateHub generates related hub files. func generateHub(gen *protogen.Plugin) { - filename := filepath.Join("hub." + pcExt + ".go") + filename := "hub." + extensions.PC + ".go" g := gen.NewGeneratedFile(filename, "") generateCommonHeader(gen, g) g.P() diff --git a/cmd/protoc-gen-go-tableau-loader/messager.go b/cmd/protoc-gen-go-tableau-loader/messager.go index ab8f19a6..db49e4ad 100644 --- a/cmd/protoc-gen-go-tableau-loader/messager.go +++ b/cmd/protoc-gen-go-tableau-loader/messager.go @@ -2,9 +2,9 @@ package main import ( "fmt" - "path/filepath" "github.com/tableauio/loader/cmd/protoc-gen-go-tableau-loader/helper" + "github.com/tableauio/loader/internal/extensions" "github.com/tableauio/loader/internal/index" "github.com/tableauio/loader/internal/options" "github.com/tableauio/tableau/proto/tableaupb" @@ -33,7 +33,7 @@ var messagers []string // generateMessager generates a protoconf file corresponding to the protobuf file. // Each wrapped struct type implement the Messager interface. func generateMessager(gen *protogen.Plugin, file *protogen.File) { - filename := filepath.Join(file.GeneratedFilenamePrefix + "." + pcExt + ".go") + filename := file.GeneratedFilenamePrefix + "." + extensions.PC + ".go" g := gen.NewGeneratedFile(filename, "") generateFileHeader(gen, file, g) g.P() diff --git a/internal/extensions/extensions.go b/internal/extensions/extensions.go new file mode 100644 index 00000000..499c8f63 --- /dev/null +++ b/internal/extensions/extensions.go @@ -0,0 +1,4 @@ +package extensions + +const PC = "pc" // protoconf file extension +const PB = "pb" // protobuf file extension From 9f685506db6c2531287a7c02a4b6978a6e44f1e0 Mon Sep 17 00:00:00 2001 From: Kybxd <627940450@qq.com> Date: Thu, 4 Sep 2025 17:04:23 +0800 Subject: [PATCH 4/6] fix: split shard --- cmd/protoc-gen-cpp-tableau-loader/hub.go | 32 +++++++++++++++++------- 1 file changed, 23 insertions(+), 9 deletions(-) diff --git a/cmd/protoc-gen-cpp-tableau-loader/hub.go b/cmd/protoc-gen-cpp-tableau-loader/hub.go index cbdb2d68..16a102d3 100644 --- a/cmd/protoc-gen-cpp-tableau-loader/hub.go +++ b/cmd/protoc-gen-cpp-tableau-loader/hub.go @@ -1,7 +1,6 @@ package main import ( - "math" "strconv" "text/template" @@ -52,18 +51,12 @@ func generateHub(gen *protogen.Plugin) { panic(err) } // generate shards - for i := 0; i < realShardNum; i++ { - shardSize := int(math.Ceil(float64(len(protofiles)) / float64((realShardNum)))) - begin := i * shardSize - end := (i + 1) * shardSize - if end > len(protofiles) { - end = len(protofiles) - } + for i, shard := range splitShards(protofiles, realShardNum) { type Param struct { Shard int Protofiles helper.ProtoFiles } - params := &Param{Shard: i, Protofiles: protofiles[begin:end]} + params := &Param{Shard: i, Protofiles: shard} cppFilename := "hub_shard" + strconv.Itoa(i) + "." + extensions.PC + ".cc" g := gen.NewGeneratedFile(cppFilename, "") helper.GenerateCommonHeader(gen, g, version) @@ -73,3 +66,24 @@ func generateHub(gen *protogen.Plugin) { } } } + +func splitShards(protofiles helper.ProtoFiles, shardNum int) []helper.ProtoFiles { + if shardNum <= 1 { + // no need to split + return nil + } else { + cursor := 0 + shards := []helper.ProtoFiles{} + for i := 0; i < shardNum; i++ { + shardSize := len(protofiles) / shardNum + if i < len(protofiles)%shardNum { + shardSize++ + } + begin := cursor + end := cursor + shardSize + shards = append(shards, protofiles[begin:end]) + cursor = end + } + return shards + } +} From 75cfba2a229aff873da49caa0e079d5e61b5162d Mon Sep 17 00:00:00 2001 From: Kybxd <627940450@qq.com> Date: Thu, 4 Sep 2025 17:28:10 +0800 Subject: [PATCH 5/6] feat: use filename to lookup tpl --- cmd/protoc-gen-cpp-tableau-loader/hub.go | 7 ++++--- cmd/protoc-gen-go-tableau-loader/hub.go | 2 +- 2 files changed, 5 insertions(+), 4 deletions(-) diff --git a/cmd/protoc-gen-cpp-tableau-loader/hub.go b/cmd/protoc-gen-cpp-tableau-loader/hub.go index 16a102d3..59788e9f 100644 --- a/cmd/protoc-gen-cpp-tableau-loader/hub.go +++ b/cmd/protoc-gen-cpp-tableau-loader/hub.go @@ -39,7 +39,7 @@ func generateHub(gen *protogen.Plugin) { g1 := gen.NewGeneratedFile(hppFilename, "") helper.GenerateCommonHeader(gen, g1, version) g1.P() - if err := tpl.Lookup("hub.pc.h.tpl").Execute(g1, params); err != nil { + if err := tpl.Lookup(hppFilename+".tpl").Execute(g1, params); err != nil { panic(err) } // generate hub cpp @@ -47,7 +47,7 @@ func generateHub(gen *protogen.Plugin) { g2 := gen.NewGeneratedFile(cppFilename, "") helper.GenerateCommonHeader(gen, g2, version) g2.P() - if err := tpl.Lookup("hub.pc.cc.tpl").Execute(g2, params); err != nil { + if err := tpl.Lookup(cppFilename+".tpl").Execute(g2, params); err != nil { panic(err) } // generate shards @@ -57,11 +57,12 @@ func generateHub(gen *protogen.Plugin) { Protofiles helper.ProtoFiles } params := &Param{Shard: i, Protofiles: shard} + cppTplname := "hub_shard" + "." + extensions.PC + ".cc" cppFilename := "hub_shard" + strconv.Itoa(i) + "." + extensions.PC + ".cc" g := gen.NewGeneratedFile(cppFilename, "") helper.GenerateCommonHeader(gen, g, version) g.P() - if err := tpl.Lookup("hub_shard.pc.cc.tpl").Execute(g, params); err != nil { + if err := tpl.Lookup(cppTplname+".tpl").Execute(g, params); err != nil { panic(err) } } diff --git a/cmd/protoc-gen-go-tableau-loader/hub.go b/cmd/protoc-gen-go-tableau-loader/hub.go index cb5a76f4..87de8ce9 100644 --- a/cmd/protoc-gen-go-tableau-loader/hub.go +++ b/cmd/protoc-gen-go-tableau-loader/hub.go @@ -20,7 +20,7 @@ func generateHub(gen *protogen.Plugin) { g.P() g.P("package ", *pkg) g.P() - if err := tpl.Lookup("hub.pc.go.tpl").Execute(g, messagers); err != nil { + if err := tpl.Lookup(filename+".tpl").Execute(g, messagers); err != nil { panic(err) } } From 7af8e8aecd3fff98fffb3ca29f72e6f70c219cab Mon Sep 17 00:00:00 2001 From: Kybxd <627940450@qq.com> Date: Fri, 5 Sep 2025 17:20:30 +0800 Subject: [PATCH 6/6] feat: remove clang format off --- .../embed/templates/hub.pc.cc.tpl | 4 +--- .../embed/templates/hub_shard.pc.cc.tpl | 4 +--- test/cpp-tableau-loader/src/protoconf/hub_shard0.pc.cc | 2 -- test/cpp-tableau-loader/src/protoconf/hub_shard1.pc.cc | 2 -- 4 files changed, 2 insertions(+), 10 deletions(-) diff --git a/cmd/protoc-gen-cpp-tableau-loader/embed/templates/hub.pc.cc.tpl b/cmd/protoc-gen-cpp-tableau-loader/embed/templates/hub.pc.cc.tpl index 23db0150..26da2c77 100644 --- a/cmd/protoc-gen-cpp-tableau-loader/embed/templates/hub.pc.cc.tpl +++ b/cmd/protoc-gen-cpp-tableau-loader/embed/templates/hub.pc.cc.tpl @@ -4,10 +4,8 @@ #include "logger.pc.h" #include "util.pc.h" {{ if not .Shards }} -// clang-format off {{ range .Protofiles }}#include "{{ toSnake .Name }}.pc.h" -{{ end }}// clang-format on -{{ end }} +{{ end }}{{ end }} namespace tableau { std::once_flag Registry::once; Registrar Registry::registrar; diff --git a/cmd/protoc-gen-cpp-tableau-loader/embed/templates/hub_shard.pc.cc.tpl b/cmd/protoc-gen-cpp-tableau-loader/embed/templates/hub_shard.pc.cc.tpl index db20e654..4bc3dafc 100644 --- a/cmd/protoc-gen-cpp-tableau-loader/embed/templates/hub_shard.pc.cc.tpl +++ b/cmd/protoc-gen-cpp-tableau-loader/embed/templates/hub_shard.pc.cc.tpl @@ -1,9 +1,7 @@ #include "hub.pc.h" -// clang-format off {{ range .Protofiles }}#include "{{ toSnake .Name }}.pc.h" -{{ end }}// clang-format on - +{{ end }} namespace tableau {{ "{" }}{{ range .Protofiles }}{{ range .Messagers }} template <> const std::shared_ptr<{{ . }}> Hub::Get<{{ . }}>() const { diff --git a/test/cpp-tableau-loader/src/protoconf/hub_shard0.pc.cc b/test/cpp-tableau-loader/src/protoconf/hub_shard0.pc.cc index 966c0dbf..e25cad93 100644 --- a/test/cpp-tableau-loader/src/protoconf/hub_shard0.pc.cc +++ b/test/cpp-tableau-loader/src/protoconf/hub_shard0.pc.cc @@ -5,10 +5,8 @@ #include "hub.pc.h" -// clang-format off #include "hero_conf.pc.h" #include "item_conf.pc.h" -// clang-format on namespace tableau { template <> diff --git a/test/cpp-tableau-loader/src/protoconf/hub_shard1.pc.cc b/test/cpp-tableau-loader/src/protoconf/hub_shard1.pc.cc index b0ed56e7..b63c90b1 100644 --- a/test/cpp-tableau-loader/src/protoconf/hub_shard1.pc.cc +++ b/test/cpp-tableau-loader/src/protoconf/hub_shard1.pc.cc @@ -5,10 +5,8 @@ #include "hub.pc.h" -// clang-format off #include "patch_conf.pc.h" #include "test_conf.pc.h" -// clang-format on namespace tableau { template <>