diff --git a/README.md b/README.md index af2d1f22..23faf9df 100644 --- a/README.md +++ b/README.md @@ -6,16 +6,22 @@ The official config loader for [Tableau](https://github.com/tableauio/tableau). > TODO: [devcontainer](https://code.visualstudio.com/docs/devcontainers/containers) -- Development OS: linux +- Supported OS: Windows, macOS, Linux - Init protobuf: `bash init.sh` +- C++ standard: at least C++17 ## C++ +### Dev at Linux + - Install: **CMake 3.22** or above - Change dir: `cd test/cpp-tableau-loader` - Generate protoconf: `bash ./gen.sh` - Create build dir: `mkdir build && cd build` -- Run cmake: `cmake ../src/`(use c++11) or `cmake -DUSE_CPP17=ON ../src/`(use c++17) +- Run cmake: + - C++17: `cmake ../src/` + - C++20: `cmake ../src/ -DCMAKE_CXX_STANDARD=20` + - clang: `cmake ../src/ -DCMAKE_CXX_COMPILER=clang++` - Build: `make -j8`, then the **bin** dir will be generated at `test/cpp-tableau-loader/bin`. ### References @@ -58,8 +64,11 @@ The official config loader for [Tableau](https://github.com/tableauio/tableau). > [protobufjs: Reflection vs. static code](https://github.com/protobufjs/protobuf.js/blob/master/cli/README.md#reflection-vs-static-code) -If using reflection (`.proto` or `JSON`) but not static code, then [proto3-json-serializer](https://github.com/googleapis/proto3-json-serializer-nodejs) is a good option. +If using reflection (`.proto` or `JSON`) but not static code, and for well-known types support, then [proto3-json-serializer](https://github.com/googleapis/proto3-json-serializer-nodejs) is a good option. This library implements proto3 JSON serialization and deserialization for +[protobuf.js](https://www.npmjs.com/package/protobufjs) protobuf objects +according to the [spec](https://protobuf.dev/programming-guides/proto3/#json). ### References: - [How to Setup a TypeScript + Node.js Project](https://khalilstemmler.com/blogs/typescript/node-starter-project/) +- [proto3-json-serializer](https://github.com/googleapis/proto3-json-serializer-nodejs) diff --git a/cmd/protoc-gen-cpp-tableau-loader/embed/load.pc.cc b/cmd/protoc-gen-cpp-tableau-loader/embed/load.pc.cc index 74467101..0cf2d6c3 100644 --- a/cmd/protoc-gen-cpp-tableau-loader/embed/load.pc.cc +++ b/cmd/protoc-gen-cpp-tableau-loader/embed/load.pc.cc @@ -1,13 +1,203 @@ #include "load.pc.h" -#if __cplusplus >= 201703L #include -#endif #include "logger.pc.h" #include "util.pc.h" namespace tableau { + +// Forward declaration of the PatchMessage function +bool PatchMessage(google::protobuf::Message& dst, const google::protobuf::Message& src); + +std::shared_ptr ParseLoadOptions(std::shared_ptr opts) { + std::shared_ptr new_opts = std::make_shared(); + // set default values + new_opts->mode = LoadMode::kAll; + new_opts->read_func = util::ReadFile; + new_opts->load_func = LoadMessager; + + if (opts == nullptr) { + return new_opts; + } + if (opts->ignore_unknown_fields.has_value()) { + new_opts->ignore_unknown_fields = opts->ignore_unknown_fields; + } + if (!opts->patch_dirs.empty()) { + new_opts->patch_dirs = opts->patch_dirs; + } + if (opts->mode != LoadMode::kNone) { + new_opts->mode = opts->mode; + } + if (opts->read_func != nullptr) { + new_opts->read_func = opts->read_func; + } + if (opts->load_func != nullptr) { + new_opts->load_func = opts->load_func; + } + return new_opts; +} + +std::shared_ptr ParseMessagerOptions(std::shared_ptr opts, + const std::string& name) { + std::shared_ptr mopts; + if (auto iter = opts->messager_options.find(name); iter != opts->messager_options.end()) { + mopts = iter->second; + } else { + mopts = std::make_shared(); + } + if (mopts->ignore_unknown_fields.has_value()) { + mopts->ignore_unknown_fields = opts->ignore_unknown_fields; + } + if (mopts->patch_dirs.empty()) { + mopts->patch_dirs = opts->patch_dirs; + } + if (mopts->mode == LoadMode::kNone) { + mopts->mode = opts->mode; + } + if (mopts->read_func == nullptr) { + mopts->read_func = opts->read_func; + } + if (mopts->load_func == nullptr) { + mopts->load_func = opts->load_func; + } + return mopts; +} + +bool LoadMessagerWithPatch(google::protobuf::Message& msg, const std::string& path, Format fmt, tableau::Patch patch, + std::shared_ptr options /* = nullptr*/) { + if (options == nullptr) { + return LoadMessager(msg, path, fmt, nullptr); + } + if (options->mode == LoadMode::kOnlyMain) { + // ignore patch files when LoadMode::kModeOnlyMain specified + return options->load_func(msg, path, fmt, nullptr); + } + std::string name = util::GetProtoName(msg); + std::vector patch_paths; + if (!options->patch_paths.empty()) { + // patch path specified in PatchPaths, then use it instead of PatchDirs. + patch_paths = options->patch_paths; + } else { + std::string filename = name + util::Format2Ext(fmt); + for (auto&& patch_dir : options->patch_dirs) { + patch_paths.emplace_back((std::filesystem::path(patch_dir) / filename).make_preferred().string()); + } + } + + std::vector existed_patch_paths; + for (auto&& patch_path : patch_paths) { + if (util::ExistsFile(patch_path)) { + existed_patch_paths.emplace_back(patch_path); + } + } + if (existed_patch_paths.empty()) { + if (options->mode == LoadMode::kOnlyPatch) { + // just returns empty message when LoadMode::kModeOnlyPatch specified but no valid patch file provided. + return true; + } + // no valid patch path provided, then just load from the "main" file. + return options->load_func(msg, path, fmt, options); + } + + switch (patch) { + case tableau::PATCH_REPLACE: { + // just use the last "patch" file + std::string& patch_path = existed_patch_paths.back(); + if (!options->load_func(msg, patch_path, util::Ext2Format(util::GetExt(patch_path)), options)) { + return false; + } + break; + } + case tableau::PATCH_MERGE: { + if (options->mode != LoadMode::kOnlyPatch) { + // load msg from the "main" file + if (!options->load_func(msg, path, fmt, options)) { + return false; + } + } + // Create a new instance of the same type of the original message + google::protobuf::Message* patch_msg_ptr = msg.New(); + std::unique_ptr _auto_release(msg.New()); + // load patch_msg from each "patch" file + for (auto&& patch_path : existed_patch_paths) { + if (!options->load_func(*patch_msg_ptr, patch_path, util::Ext2Format(util::GetExt(patch_path)), options)) { + return false; + } + if (!PatchMessage(msg, *patch_msg_ptr)) { + return false; + } + } + break; + } + default: { + SetErrMsg("unknown patch type: " + util::GetPatchName(patch)); + return false; + } + } + ATOM_DEBUG("patched(%s) %s by %s: %s", util::GetPatchName(patch).c_str(), name.c_str(), + ATOM_VECTOR_STR(existed_patch_paths).c_str(), msg.ShortDebugString().c_str()); + return true; +} + +bool LoadMessager(google::protobuf::Message& msg, const std::string& path, Format fmt, + std::shared_ptr options /* = nullptr*/) { + std::string content; + ReadFunc read_func = util::ReadFile; + if (options != nullptr && options->read_func) { + read_func = options->read_func; + } + bool ok = read_func(path, content); + if (!ok) { + return false; + } + switch (fmt) { + case Format::kJSON: { + return util::JSON2Message(content, msg, options); + } + case Format::kText: { + return util::Text2Message(content, msg); + } + case Format::kBin: { + return util::Bin2Message(content, msg); + } + default: { + SetErrMsg("unknown format: " + std::to_string(static_cast(fmt))); + return false; + } + } +} + +bool LoadMessagerInDir(google::protobuf::Message& msg, const std::string& dir, Format fmt, + std::shared_ptr options /* = nullptr*/) { + std::string name = util::GetProtoName(msg); + std::string path; + if (options && !options->path.empty()) { + // path specified in Paths, then use it instead of dir. + path = options->path; + fmt = util::Ext2Format(util::GetExt(path)); + } + if (path.empty()) { + std::string filename = name + util::Format2Ext(fmt); + path = (std::filesystem::path(dir) / filename).make_preferred().string(); + } + + const google::protobuf::Descriptor* descriptor = msg.GetDescriptor(); + if (!descriptor) { + SetErrMsg("failed to get descriptor of message: " + name); + return false; + } + // access the extension directly using the generated identifier + const tableau::WorksheetOptions worksheet_options = descriptor->options().GetExtension(tableau::worksheet); + if (worksheet_options.patch() != tableau::PATCH_NONE) { + return LoadMessagerWithPatch(msg, path, fmt, worksheet_options.patch(), options); + } + if (options) { + return options->load_func(msg, path, fmt, options); + } + return LoadMessager(msg, path, fmt); +} + #ifdef _WIN32 #undef GetMessage #endif @@ -161,148 +351,4 @@ bool PatchMessage(google::protobuf::Message& dst, const google::protobuf::Messag dst_reflection->MutableUnknownFields(&dst)->MergeFrom(src_reflection->GetUnknownFields(src)); return true; } - -bool LoadMessageWithPatch(google::protobuf::Message& msg, const std::string& path, Format fmt, tableau::Patch patch, - std::shared_ptr options /* = nullptr*/) { - if (options == nullptr) { - return LoadMessageByPath(msg, path, fmt, nullptr); - } - if (options->mode == LoadMode::kModeOnlyMain) { - // ignore patch files when LoadMode::kModeOnlyMain specified - return LoadMessageByPath(msg, path, fmt, nullptr); - } - std::string name = util::GetProtoName(msg); - std::vector patch_paths; - auto iter = options->patch_paths.find(name); - if (iter != options->patch_paths.end()) { - // patch path specified in PatchPaths, then use it instead of PatchDirs. - patch_paths = iter->second; - } else { - std::string filename = name + util::Format2Ext(fmt); - for (auto&& patch_dir : options->patch_dirs) { -#if __cplusplus >= 201703L - patch_paths.emplace_back((std::filesystem::path(patch_dir) / filename).make_preferred().string()); -#else - patch_paths.emplace_back(patch_dir + kPathSeperator + filename); -#endif - } - } - - std::vector existed_patch_paths; - for (auto&& patch_path : patch_paths) { - if (util::ExistsFile(patch_path)) { - existed_patch_paths.emplace_back(patch_path); - } - } - if (existed_patch_paths.empty()) { - if (options->mode == LoadMode::kModeOnlyPatch) { - // just returns empty message when LoadMode::kModeOnlyPatch specified but no valid patch file provided. - return true; - } - // no valid patch path provided, then just load from the "main" file. - return LoadMessageByPath(msg, path, fmt, options); - } - - switch (patch) { - case tableau::PATCH_REPLACE: { - // just use the last "patch" file - std::string& patch_path = existed_patch_paths.back(); - if (!LoadMessageByPath(msg, patch_path, util::Ext2Format(util::GetExt(patch_path)), options)) { - return false; - } - break; - } - case tableau::PATCH_MERGE: { - if (options->mode != LoadMode::kModeOnlyPatch) { - // load msg from the "main" file - if (!LoadMessageByPath(msg, path, fmt, options)) { - return false; - } - } - // Create a new instance of the same type of the original message - google::protobuf::Message* patch_msg_ptr = msg.New(); - std::unique_ptr _auto_release(msg.New()); - // load patch_msg from each "patch" file - for (auto&& patch_path : existed_patch_paths) { - if (!LoadMessageByPath(*patch_msg_ptr, patch_path, util::Ext2Format(util::GetExt(patch_path)), options)) { - return false; - } - if (!PatchMessage(msg, *patch_msg_ptr)) { - return false; - } - } - break; - } - default: { - SetErrMsg("unknown patch type: " + util::GetPatchName(patch)); - return false; - } - } - ATOM_DEBUG("patched(%s) %s by %s: %s", util::GetPatchName(patch).c_str(), name.c_str(), - ATOM_VECTOR_STR(existed_patch_paths).c_str(), msg.ShortDebugString().c_str()); - return true; -} - -bool LoadMessageByPath(google::protobuf::Message& msg, const std::string& path, Format fmt, - std::shared_ptr options /* = nullptr*/) { - std::string content; - ReadFunc read_func = util::ReadFile; - if (options != nullptr && options->read_func) { - read_func = options->read_func; - } - bool ok = read_func(path, content); - if (!ok) { - return false; - } - switch (fmt) { - case Format::kJSON: { - return util::JSON2Message(content, msg, options); - } - case Format::kText: { - return util::Text2Message(content, msg); - } - case Format::kBin: { - return util::Bin2Message(content, msg); - } - default: { - SetErrMsg("unknown format: " + std::to_string(static_cast(fmt))); - return false; - } - } -} - -bool LoadMessage(google::protobuf::Message& msg, const std::string& dir, Format fmt, - std::shared_ptr options /* = nullptr*/) { - std::string name = util::GetProtoName(msg); - std::string path; - if (options) { - auto iter = options->paths.find(name); - if (iter != options->paths.end()) { - // path specified in Paths, then use it instead of dir. - path = iter->second; - fmt = util::Ext2Format(util::GetExt(iter->second)); - } - } - if (path.empty()) { - std::string filename = name + util::Format2Ext(fmt); -#if __cplusplus >= 201703L - path = (std::filesystem::path(dir) / filename).make_preferred().string(); -#else - path = dir + kPathSeperator + filename; -#endif - } - - const google::protobuf::Descriptor* descriptor = msg.GetDescriptor(); - if (!descriptor) { - SetErrMsg("failed to get descriptor of message: " + name); - return false; - } - // access the extension directly using the generated identifier - const tableau::WorksheetOptions worksheet_options = descriptor->options().GetExtension(tableau::worksheet); - if (worksheet_options.patch() != tableau::PATCH_NONE) { - return LoadMessageWithPatch(msg, path, fmt, worksheet_options.patch(), options); - } - - return LoadMessageByPath(msg, path, fmt, options); -} } // namespace tableau \ No newline at end of file diff --git a/cmd/protoc-gen-cpp-tableau-loader/embed/load.pc.h b/cmd/protoc-gen-cpp-tableau-loader/embed/load.pc.h index b64ca32b..b37f05a8 100644 --- a/cmd/protoc-gen-cpp-tableau-loader/embed/load.pc.h +++ b/cmd/protoc-gen-cpp-tableau-loader/embed/load.pc.h @@ -1,43 +1,105 @@ #pragma once #include +#include #include +#include #include #include "util.pc.h" namespace tableau { enum class LoadMode { - kModeDefault, - kModeOnlyMain, - kModeOnlyPatch, + kNone, + kAll, // Load all related files + kOnlyMain, // Only load the main file + kOnlyPatch, // Only load the patch files }; +struct MessagerOptions; +class Hub; + // ReadFunc reads the config file and returns its content. using ReadFunc = std::function; +// LoadFunc defines a func which can load message's content based on the given +// path, format, and options. +using LoadFunc = std::function options)>; -struct LoadOptions { - // read_func reads the config file and returns its content. - ReadFunc read_func; +// BaseOptions is the common struct for both global-level and messager-level +// options. +struct BaseOptions { // Whether to ignore unknown JSON fields during parsing. // // Refer https://protobuf.dev/reference/cpp/api-docs/google.protobuf.util.json_util/#JsonParseOptions. - bool ignore_unknown_fields = false; - // Paths maps each messager name to a corresponding config file path. + std::optional ignore_unknown_fields; + // Specify the directory paths for config patching. + std::vector patch_dirs; + // Specify the loading mode for config patching. + // - For LoadOptions, default is LoadMode::kModeAll. + // - For MessagerOptions, inherit from LoadOptions if not set. + LoadMode mode; + // You can specify custom read function to read a config file's content. + // - For LoadOptions, default is util::ReadFile. + // - For MessagerOptions, inherit from LoadOptions if not set. + ReadFunc read_func; + // You can specify custom load function to load a messager's content. + // - For LoadOptions, default is LoadMessage. + // - For MessagerOptions, inherit from LoadOptions if not set. + LoadFunc load_func; +}; + +// LoadOptionsOptions is the options struct, which contains both global-level and +// messager-level options. +struct LoadOptions : public BaseOptions { + // messager_options maps each messager name to a MessageOptions. + // If specified, then the messager will be parsed with the given options + // directly. + std::unordered_map> messager_options; +}; + +// MessagerOptions defines the options for loading a messager. +struct MessagerOptions : public BaseOptions { + // Path maps each messager name to a corresponding config file path. // If specified, then the main messager will be parsed from the file // directly, other than the specified load dir. - std::unordered_map paths; + std::string path; // Patch paths maps each messager name to one or multiple corresponding patch file paths. // If specified, then main messager will be patched. - std::unordered_map> patch_paths; - // Patch dirs specifies the directory paths for config patching. - std::vector patch_dirs; - // Mode specifies the loading mode for config patching. - LoadMode mode = LoadMode::kModeDefault; + std::vector patch_paths; +}; + +class Messager { + public: + struct Stats { + std::chrono::microseconds duration; // total load time consuming. + }; + + public: + virtual ~Messager() = default; + static const std::string& Name() { return kEmpty; } + const Stats& GetStats() { return stats_; } + // Load fills message from file in the specified directory and format. + virtual bool Load(const std::string& dir, Format fmt, std::shared_ptr options = nullptr) = 0; + // Message returns the inner message data. + virtual const google::protobuf::Message* Message() const { return nullptr; } + // callback after all messagers loaded. + virtual bool ProcessAfterLoadAll(const Hub&) { return true; } + + protected: + // callback after this messager loaded. + virtual bool ProcessAfterLoad() { return true; }; + Stats stats_; }; -bool LoadMessageByPath(google::protobuf::Message& msg, const std::string& path, Format fmt = Format::kJSON, - std::shared_ptr options = nullptr); -bool LoadMessage(google::protobuf::Message& msg, const std::string& dir, Format fmt = Format::kJSON, - std::shared_ptr options = nullptr); +// ParseLoadOptions parses load options with default global-level options. +std::shared_ptr ParseLoadOptions(std::shared_ptr opts); +// ParseMessagerOptions parses messager options with both global-level and +// messager-level options taken into consideration. +std::shared_ptr ParseMessagerOptions(std::shared_ptr opts, + const std::string& name); +bool LoadMessager(google::protobuf::Message& msg, const std::string& path, Format fmt = Format::kJSON, + std::shared_ptr options = nullptr); +bool LoadMessagerInDir(google::protobuf::Message& msg, const std::string& dir, Format fmt = Format::kJSON, + std::shared_ptr options = nullptr); } // namespace tableau \ No newline at end of file diff --git a/cmd/protoc-gen-cpp-tableau-loader/embed/messager.pc.h b/cmd/protoc-gen-cpp-tableau-loader/embed/messager.pc.h deleted file mode 100644 index 7d5bda88..00000000 --- a/cmd/protoc-gen-cpp-tableau-loader/embed/messager.pc.h +++ /dev/null @@ -1,36 +0,0 @@ -#pragma once -#include - -#include -#include -#include - -#include "util.pc.h" - -namespace tableau { -class Hub; -struct LoadOptions; - -class Messager { - public: - struct Stats { - std::chrono::microseconds duration; // total load time consuming. - }; - - public: - virtual ~Messager() = default; - static const std::string& Name() { return kEmpty; } - const Stats& GetStats() { return stats_; } - // Load fills message from file in the specified directory and format. - virtual bool Load(const std::string& dir, Format fmt, std::shared_ptr options = nullptr) = 0; - // Message returns the inner message data. - virtual const google::protobuf::Message* Message() const { return nullptr; } - // callback after all messagers loaded. - virtual bool ProcessAfterLoadAll(const Hub& hub) { return true; } - - protected: - // callback after this messager loaded. - virtual bool ProcessAfterLoad() { return true; }; - Stats stats_; -}; -} // namespace tableau \ No newline at end of file diff --git a/cmd/protoc-gen-cpp-tableau-loader/embed/scheduler.pc.cc b/cmd/protoc-gen-cpp-tableau-loader/embed/scheduler.pc.cc index 5a9aca0b..2280b974 100644 --- a/cmd/protoc-gen-cpp-tableau-loader/embed/scheduler.pc.cc +++ b/cmd/protoc-gen-cpp-tableau-loader/embed/scheduler.pc.cc @@ -26,7 +26,7 @@ int Scheduler::LoopOnce() { for (auto&& job : jobs) { job(); } - count += jobs.size(); + count += static_cast(jobs.size()); return count; } diff --git a/cmd/protoc-gen-cpp-tableau-loader/embed/util.pc.cc b/cmd/protoc-gen-cpp-tableau-loader/embed/util.pc.cc index 5ecd56e3..67dc773e 100644 --- a/cmd/protoc-gen-cpp-tableau-loader/embed/util.pc.cc +++ b/cmd/protoc-gen-cpp-tableau-loader/embed/util.pc.cc @@ -3,30 +3,15 @@ #include #include +#include #include #include #include -#if __cplusplus >= 201703L -#include -#else -#ifdef _WIN32 -#include -#include -#else -#include -#endif -#endif - #include "load.pc.h" #include "logger.pc.h" -#include "messager.pc.h" namespace tableau { -#ifdef _WIN32 -#define mkdir(path, mode) _mkdir(path) -#endif - static thread_local std::string g_err_msg; const std::string& GetErrMsg() { return g_err_msg; } void SetErrMsg(const std::string& msg) { g_err_msg = msg; } @@ -38,7 +23,6 @@ const std::string kBinExt = ".bin"; namespace util { int Mkdir(const std::string& path) { -#if __cplusplus >= 201703L std::error_code ec; if (!std::filesystem::create_directories(path, ec)) { if (ec) { @@ -47,46 +31,11 @@ int Mkdir(const std::string& path) { } } return 0; -#else - std::string path_ = path + kPathSeperator; - struct stat info; - for (size_t pos = path_.find(kPathSeperator, 0); pos != std::string::npos; pos = path_.find(kPathSeperator, pos)) { - ++pos; - auto sub_dir = path_.substr(0, pos); - if (stat(sub_dir.c_str(), &info) == 0 && info.st_mode & S_IFDIR) { - continue; - } - int status = mkdir(sub_dir.c_str(), 0755); - if (status != 0) { - std::cerr << "system error: " << strerror(errno) << std::endl; - return -1; - } - } - return 0; -#endif } -std::string GetDir(const std::string& path) { -#if __cplusplus >= 201703L - return std::filesystem::path(path).parent_path().string(); -#else - size_t pos = path.find_last_of(kPathSeperator); - if (pos != std::string::npos) { - return path.substr(0, pos); - } - return kEmpty; -#endif -} +std::string GetDir(const std::string& path) { return std::filesystem::path(path).parent_path().string(); } -bool ExistsFile(const std::string& filename) { -#if __cplusplus >= 201703L - return std::filesystem::exists(filename); -#else - std::ifstream file(filename); - // returns true if the file exists and is accessible - return file.good(); -#endif -} +bool ExistsFile(const std::string& filename) { return std::filesystem::exists(filename); } bool ReadFile(const std::string& filename, std::string& content) { std::ifstream file(filename); @@ -134,11 +83,11 @@ const std::string& Format2Ext(Format fmt) { } bool JSON2Message(const std::string& json, google::protobuf::Message& msg, - std::shared_ptr options /* = nullptr */) { + std::shared_ptr options /* = nullptr */) { google::protobuf::util::Status status; if (options != nullptr) { google::protobuf::util::JsonParseOptions parse_options; - parse_options.ignore_unknown_fields = options->ignore_unknown_fields; + parse_options.ignore_unknown_fields = options->ignore_unknown_fields.value_or(false); status = google::protobuf::util::JsonStringToMessage(json, &msg, parse_options); } else { status = google::protobuf::util::JsonStringToMessage(json, &msg); diff --git a/cmd/protoc-gen-cpp-tableau-loader/embed/util.pc.h b/cmd/protoc-gen-cpp-tableau-loader/embed/util.pc.h index 60debed4..f2ed38c0 100644 --- a/cmd/protoc-gen-cpp-tableau-loader/embed/util.pc.h +++ b/cmd/protoc-gen-cpp-tableau-loader/embed/util.pc.h @@ -11,15 +11,6 @@ namespace tableau { const std::string& GetErrMsg(); void SetErrMsg(const std::string& msg); -#if __cplusplus < 201703L -// Platform-specific path separator -#ifdef _WIN32 -constexpr char kPathSeperator = '\\'; -#else -constexpr char kPathSeperator = '/'; -#endif -#endif - enum class Format { kUnknown, kJSON, @@ -33,7 +24,7 @@ extern const std::string kJSONExt; extern const std::string kTextExt; extern const std::string kBinExt; -struct LoadOptions; +struct MessagerOptions; namespace util { // Combine hash values @@ -41,7 +32,7 @@ namespace util { // References: // - https://stackoverflow.com/questions/2590677/how-do-i-combine-hash-values-in-c0x // - https://stackoverflow.com/questions/17016175/c-unordered-map-using-a-custom-class-type-as-the-key -inline void HashCombine(std::size_t& seed) {} +inline void HashCombine(std::size_t&) {} template inline void HashCombine(std::size_t& seed, const T& v, O... others) { @@ -74,16 +65,16 @@ bool ReadFile(const std::string& filename, std::string& content); std::string GetExt(const std::string& path); // Convert file extension to Format type. // NOTE: ext includes dot ".", such as: -// - kJSONExt:".json" -// - kTextExt".txt" -// - kBinExt".bin" +// - kJSONExt: ".json" +// - kTextExt: ".txt" +// - kBinExt: ".bin" Format Ext2Format(const std::string& ext); // Empty string will be returned if an unsupported enum value has been passed, // and the error message can be obtained by GetErrMsg(). const std::string& Format2Ext(Format fmt); bool JSON2Message(const std::string& json, google::protobuf::Message& msg, - std::shared_ptr options = nullptr); + std::shared_ptr options = nullptr); bool Text2Message(const std::string& text, google::protobuf::Message& msg); bool Bin2Message(const std::string& bin, google::protobuf::Message& msg); diff --git a/cmd/protoc-gen-cpp-tableau-loader/hub.go b/cmd/protoc-gen-cpp-tableau-loader/hub.go index 5e0e468e..63453bed 100644 --- a/cmd/protoc-gen-cpp-tableau-loader/hub.go +++ b/cmd/protoc-gen-cpp-tableau-loader/hub.go @@ -146,7 +146,6 @@ const hubHpp = `#pragma once #include #include "load.pc.h" -#include "messager.pc.h" #include "scheduler.pc.h" namespace tableau { @@ -294,8 +293,8 @@ void Registry::Register() { const hubCppHeader = `#include "hub.pc.h" +#include "load.pc.h" #include "logger.pc.h" -#include "messager.pc.h" #include "util.pc.h"` const hubCpp = ` @@ -348,11 +347,13 @@ std::shared_ptr Hub::InternalLoad(const std::string& dir, Format fm std::shared_ptr options /* = nullptr */) const { // intercept protobuf error logs auto old_handler = google::protobuf::SetLogHandler(util::ProtobufLogHandler); + auto opts = ParseLoadOptions(options); auto msger_map = NewMessagerMap(); for (auto iter : *msger_map) { auto&& name = iter.first; ATOM_DEBUG("loading %s", name.c_str()); - bool ok = iter.second->Load(dir, fmt, options); + auto mopts = ParseMessagerOptions(opts, 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 diff --git a/cmd/protoc-gen-cpp-tableau-loader/messager.go b/cmd/protoc-gen-cpp-tableau-loader/messager.go index fef3c3a3..19f2e3a5 100644 --- a/cmd/protoc-gen-cpp-tableau-loader/messager.go +++ b/cmd/protoc-gen-cpp-tableau-loader/messager.go @@ -45,7 +45,7 @@ func generateHppFileContent(file *protogen.File, g *protogen.GeneratedFile) { g.P("#pragma once") g.P("#include ") g.P() - g.P(`#include "`, "messager.", pcExt, `.h"`) + g.P(`#include "`, "load.", pcExt, `.h"`) g.P(`#include "`, "util.", pcExt, `.h"`) g.P(`#include "`, file.GeneratedFilenamePrefix, ".", pbExt, `.h"`) g.P() @@ -85,7 +85,7 @@ func genHppMessage(file *protogen.File, g *protogen.GeneratedFile, message *prot g.P("class ", message.Desc.Name(), " : public Messager {") g.P(" public:") g.P(helper.Indent(1), "static const std::string& Name() { return kProtoName; }") - g.P(helper.Indent(1), "virtual bool Load(const std::string& dir, Format fmt, std::shared_ptr options = nullptr) override;") + g.P(helper.Indent(1), "virtual bool Load(const std::string& dir, Format fmt, std::shared_ptr options = nullptr) override;") g.P(helper.Indent(1), "const ", cppFullName, "& Data() const { return data_; }") g.P(helper.Indent(1), "const google::protobuf::Message* Message() const override { return &data_; }") g.P() @@ -158,9 +158,9 @@ func genCppMessage(g *protogen.GeneratedFile, message *protogen.Message) { g.P("const std::string ", messagerName, "::kProtoName = ", `"`, messagerName, `";`) g.P() - g.P("bool ", messagerName, "::Load(const std::string& dir, Format fmt, std::shared_ptr options /* = nullptr */) {") + g.P("bool ", messagerName, "::Load(const std::string& dir, Format fmt, std::shared_ptr options /* = nullptr */) {") g.P(helper.Indent(1), "tableau::util::TimeProfiler profiler;") - g.P(helper.Indent(1), "bool loaded = LoadMessage(data_, dir, fmt, options);") + g.P(helper.Indent(1), "bool loaded = LoadMessagerInDir(data_, dir, fmt, options);") g.P(helper.Indent(1), "bool ok = loaded ? ProcessAfterLoad() : false;") g.P(helper.Indent(1), "stats_.duration = profiler.Elapse();") g.P(helper.Indent(1), "return ok;") diff --git a/test/cpp-tableau-loader/gen.bat b/test/cpp-tableau-loader/gen.bat index 0589b935..39b8aa34 100644 --- a/test/cpp-tableau-loader/gen.bat +++ b/test/cpp-tableau-loader/gen.bat @@ -32,7 +32,7 @@ for /R %%f in (*.proto) do ( popd "%PROTOC%" ^ --cpp-tableau-loader_out="%PROTOCONF_OUT%" ^ - --cpp-tableau-loader_opt=paths=source_relative,registry-shards=2 ^ + --cpp-tableau-loader_opt=paths=source_relative,shards=2 ^ --cpp_out="%PROTOCONF_OUT%" ^ --proto_path="%PROTOBUF_PROTO%" ^ --proto_path="%TABLEAU_PROTO%" ^ diff --git a/test/cpp-tableau-loader/src/CMakeLists.txt b/test/cpp-tableau-loader/src/CMakeLists.txt index b827e3d4..81402304 100644 --- a/test/cpp-tableau-loader/src/CMakeLists.txt +++ b/test/cpp-tableau-loader/src/CMakeLists.txt @@ -5,35 +5,22 @@ project(loader) file(GLOB_RECURSE PROTO_SOURCE *.cc) file(GLOB_RECURSE SOURCE *.cpp) -# Add option to switch between C++11 and C++17 -option(USE_CPP17 "Use C++17 standard (otherwise use C++11)" OFF) - -# specify the C++ standard -if(USE_CPP17) - set(CMAKE_CXX_STANDARD 17) - message(STATUS "Using C++17 standard") +# check C++ standard requirement +set(MIN_CXX_STANDARD 17) +if (NOT CMAKE_CXX_STANDARD) + set(CMAKE_CXX_STANDARD ${MIN_CXX_STANDARD}) # default min C++ standard else() - set(CMAKE_CXX_STANDARD 11) - message(STATUS "Using C++11 standard") + if(CMAKE_CXX_STANDARD LESS ${MIN_CXX_STANDARD}) + message(FATAL_ERROR "CMAKE_CXX_STANDARD is less than the minimum required standard: C++${MIN_CXX_STANDARD}.") + endif() endif() set(CMAKE_CXX_STANDARD_REQUIRED True) +message(STATUS "Using C++${CMAKE_CXX_STANDARD} standard") if (MSVC) - if(USE_CPP17) - SET(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} /W4 /DNDEBUG /std:c++17") - else() - SET(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} /W4 /DNDEBUG") - endif() + SET(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} /W4 /DNDEBUG") else() - if(USE_CPP17) - SET(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wall -g -fPIC -std=c++17 -Wno-deprecated -Wno-unused-variable -Wno-sign-compare -Wno-strict-aliasing -fno-strict-aliasing -DNDEBUG") - # For older GCC versions that need explicit linking with stdc++fs - if(CMAKE_CXX_COMPILER_ID STREQUAL "GNU" AND CMAKE_CXX_COMPILER_VERSION VERSION_LESS "9.0") - set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -lstdc++fs") - endif() - else() - SET(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wall -g -fPIC -std=c++11 -Wno-deprecated -Wno-unused-variable -Wno-sign-compare -Wno-strict-aliasing -fno-strict-aliasing -DNDEBUG") - endif() + SET(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wall -g -fPIC -Wno-deprecated -Wno-unused-variable -Wno-sign-compare -Wno-strict-aliasing -fno-strict-aliasing -DNDEBUG") endif() # root dir define @@ -65,7 +52,7 @@ else() SET(COMMON_LIB ${PROTOBUF_LIB}/libprotobuf.a pthread - $<$:stdc++fs> + stdc++fs ) endif() diff --git a/test/cpp-tableau-loader/src/hub/custom/item/custom_item_conf.h b/test/cpp-tableau-loader/src/hub/custom/item/custom_item_conf.h index 5ebb6820..76835861 100644 --- a/test/cpp-tableau-loader/src/hub/custom/item/custom_item_conf.h +++ b/test/cpp-tableau-loader/src/hub/custom/item/custom_item_conf.h @@ -4,8 +4,8 @@ class CustomItemConf : public tableau::Messager { public: static const std::string& Name() { return kCustomName; }; - virtual bool Load(const std::string& dir, tableau::Format fmt, - std::shared_ptr options = nullptr) override { + virtual bool Load(const std::string&, tableau::Format, + std::shared_ptr options = nullptr) override { return true; } virtual bool ProcessAfterLoadAll(const tableau::Hub& hub) override; diff --git a/test/cpp-tableau-loader/src/main.cpp b/test/cpp-tableau-loader/src/main.cpp index 71c3d5e8..d298ff2e 100644 --- a/test/cpp-tableau-loader/src/main.cpp +++ b/test/cpp-tableau-loader/src/main.cpp @@ -73,7 +73,9 @@ bool TestPatch() { // patchconf2 different format std::cout << "-----TestPatch patchconf2 different format" << std::endl; options->patch_dirs = {"../../testdata/patchconf2/"}; - options->patch_paths["PatchMergeConf"] = {"../../testdata/patchconf2/PatchMergeConf.txt"}; + auto mopts = std::make_shared(); + mopts->patch_paths = {"../../testdata/patchconf2/PatchMergeConf.txt"}; + options->messager_options["PatchMergeConf"] = mopts; ok = Hub::Instance().Load("../../testdata/conf/", tableau::Format::kJSON, options); if (!ok) { std::cout << "failed to load with patchconf2" << std::endl; @@ -82,8 +84,10 @@ bool TestPatch() { // multiple patch files std::cout << "-----TestPatch multiple patch files" << std::endl; - options->patch_paths["PatchMergeConf"] = {"../../testdata/patchconf/PatchMergeConf.json", - "../../testdata/patchconf2/PatchMergeConf.json"}; + mopts = std::make_shared(); + mopts->patch_paths = {"../../testdata/patchconf/PatchMergeConf.json", + "../../testdata/patchconf2/PatchMergeConf.json"}; + options->messager_options["PatchMergeConf"] = mopts; ok = Hub::Instance().Load("../../testdata/conf/", tableau::Format::kJSON, options); if (!ok) { std::cout << "failed to load with multiple patch files" << std::endl; @@ -92,9 +96,11 @@ bool TestPatch() { // mode only main std::cout << "-----TestPatch ModeOnlyMain" << std::endl; - options->patch_paths["PatchMergeConf"] = {"../../testdata/patchconf/PatchMergeConf.json", - "../../testdata/patchconf2/PatchMergeConf.json"}; - options->mode = tableau::LoadMode::kModeOnlyMain; + mopts = std::make_shared(); + mopts->patch_paths = {"../../testdata/patchconf/PatchMergeConf.json", + "../../testdata/patchconf2/PatchMergeConf.json"}; + options->messager_options["PatchMergeConf"] = mopts; + options->mode = tableau::LoadMode::kOnlyMain; ok = Hub::Instance().Load("../../testdata/conf/", tableau::Format::kJSON, options); if (!ok) { std::cout << "failed to load with mode only main" << std::endl; @@ -109,9 +115,11 @@ bool TestPatch() { // mode only patch std::cout << "-----TestPatch ModeOnlyPatch" << std::endl; - options->patch_paths["PatchMergeConf"] = {"../../testdata/patchconf/PatchMergeConf.json", - "../../testdata/patchconf2/PatchMergeConf.json"}; - options->mode = tableau::LoadMode::kModeOnlyPatch; + mopts = std::make_shared(); + mopts->patch_paths = {"../../testdata/patchconf/PatchMergeConf.json", + "../../testdata/patchconf2/PatchMergeConf.json"}; + options->messager_options["PatchMergeConf"] = mopts; + options->mode = tableau::LoadMode::kOnlyPatch; ok = Hub::Instance().Load("../../testdata/conf/", tableau::Format::kJSON, options); if (!ok) { std::cout << "failed to load with mode only patch" << std::endl; @@ -125,7 +133,9 @@ int main() { auto options = std::make_shared(); options->ignore_unknown_fields = true; options->patch_dirs = {"../../testdata/patchconf/"}; - options->paths["ItemConf"] = "../../testdata/conf/ItemConf.json"; + auto mopts = std::make_shared(); + mopts->path = "../../testdata/conf/ItemConf.json"; + options->messager_options["ItemConf"] = mopts; bool ok = Hub::Instance().Load("../../testdata/conf/", tableau::Format::kJSON, options); if (!ok) { @@ -185,13 +195,13 @@ int main() { for (auto&& it : *chapter_ordered_map) { std::cout << "---" << it.first << "-----section_ordered_map" << std::endl; - for (auto&& item : it.second.first) { - std::cout << item.first << std::endl; + for (auto&& kv : it.second.first) { + std::cout << kv.first << std::endl; } std::cout << "---" << it.first << " -----section_map" << std::endl; - for (auto&& item : it.second.second->section_map()) { - std::cout << item.first << std::endl; + for (auto&& kv : it.second.second->section_map()) { + std::cout << kv.first << std::endl; } std::cout << "chapter_id: " << it.second.second->chapter_id() << std::endl; diff --git a/test/cpp-tableau-loader/src/protoconf/hero_conf.pc.cc b/test/cpp-tableau-loader/src/protoconf/hero_conf.pc.cc index 16094a7e..d0182838 100644 --- a/test/cpp-tableau-loader/src/protoconf/hero_conf.pc.cc +++ b/test/cpp-tableau-loader/src/protoconf/hero_conf.pc.cc @@ -12,9 +12,9 @@ namespace tableau { const std::string HeroConf::kProtoName = "HeroConf"; -bool HeroConf::Load(const std::string& dir, Format fmt, std::shared_ptr options /* = nullptr */) { +bool HeroConf::Load(const std::string& dir, Format fmt, std::shared_ptr options /* = nullptr */) { tableau::util::TimeProfiler profiler; - bool loaded = LoadMessage(data_, dir, fmt, options); + bool loaded = LoadMessagerInDir(data_, dir, fmt, options); bool ok = loaded ? ProcessAfterLoad() : false; stats_.duration = profiler.Elapse(); return ok; @@ -71,9 +71,9 @@ const HeroConf::Hero_Attr_OrderedMap* HeroConf::GetOrderedMap(const std::string& const std::string HeroBaseConf::kProtoName = "HeroBaseConf"; -bool HeroBaseConf::Load(const std::string& dir, Format fmt, std::shared_ptr options /* = nullptr */) { +bool HeroBaseConf::Load(const std::string& dir, Format fmt, std::shared_ptr options /* = nullptr */) { tableau::util::TimeProfiler profiler; - bool loaded = LoadMessage(data_, dir, fmt, options); + bool loaded = LoadMessagerInDir(data_, dir, fmt, options); bool ok = loaded ? ProcessAfterLoad() : false; stats_.duration = profiler.Elapse(); return ok; diff --git a/test/cpp-tableau-loader/src/protoconf/hero_conf.pc.h b/test/cpp-tableau-loader/src/protoconf/hero_conf.pc.h index 02698c01..e07b07c4 100644 --- a/test/cpp-tableau-loader/src/protoconf/hero_conf.pc.h +++ b/test/cpp-tableau-loader/src/protoconf/hero_conf.pc.h @@ -7,7 +7,7 @@ #pragma once #include -#include "messager.pc.h" +#include "load.pc.h" #include "util.pc.h" #include "hero_conf.pb.h" @@ -15,7 +15,7 @@ namespace tableau { class HeroConf : public Messager { public: static const std::string& Name() { return kProtoName; } - virtual bool Load(const std::string& dir, Format fmt, std::shared_ptr options = nullptr) override; + virtual bool Load(const std::string& dir, Format fmt, std::shared_ptr options = nullptr) override; const protoconf::HeroConf& Data() const { return data_; } const google::protobuf::Message* Message() const override { return &data_; } @@ -46,7 +46,7 @@ class HeroConf : public Messager { class HeroBaseConf : public Messager { public: static const std::string& Name() { return kProtoName; } - virtual bool Load(const std::string& dir, Format fmt, std::shared_ptr options = nullptr) override; + virtual bool Load(const std::string& dir, Format fmt, std::shared_ptr options = nullptr) override; const protoconf::HeroBaseConf& Data() const { return data_; } const google::protobuf::Message* Message() const override { return &data_; } diff --git a/test/cpp-tableau-loader/src/protoconf/hub.pc.cc b/test/cpp-tableau-loader/src/protoconf/hub.pc.cc index 77e2d003..4953d5e1 100644 --- a/test/cpp-tableau-loader/src/protoconf/hub.pc.cc +++ b/test/cpp-tableau-loader/src/protoconf/hub.pc.cc @@ -5,8 +5,8 @@ #include "hub.pc.h" +#include "load.pc.h" #include "logger.pc.h" -#include "messager.pc.h" #include "util.pc.h" namespace tableau { @@ -58,11 +58,13 @@ std::shared_ptr Hub::InternalLoad(const std::string& dir, Format fm std::shared_ptr options /* = nullptr */) const { // intercept protobuf error logs auto old_handler = google::protobuf::SetLogHandler(util::ProtobufLogHandler); + auto opts = ParseLoadOptions(options); auto msger_map = NewMessagerMap(); for (auto iter : *msger_map) { auto&& name = iter.first; ATOM_DEBUG("loading %s", name.c_str()); - bool ok = iter.second->Load(dir, fmt, options); + auto mopts = ParseMessagerOptions(opts, 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 diff --git a/test/cpp-tableau-loader/src/protoconf/hub.pc.h b/test/cpp-tableau-loader/src/protoconf/hub.pc.h index bde26d02..e80c9710 100644 --- a/test/cpp-tableau-loader/src/protoconf/hub.pc.h +++ b/test/cpp-tableau-loader/src/protoconf/hub.pc.h @@ -11,7 +11,6 @@ #include #include "load.pc.h" -#include "messager.pc.h" #include "scheduler.pc.h" namespace tableau { diff --git a/test/cpp-tableau-loader/src/protoconf/item_conf.pc.cc b/test/cpp-tableau-loader/src/protoconf/item_conf.pc.cc index c4ca73ad..a1460ba4 100644 --- a/test/cpp-tableau-loader/src/protoconf/item_conf.pc.cc +++ b/test/cpp-tableau-loader/src/protoconf/item_conf.pc.cc @@ -12,9 +12,9 @@ namespace tableau { const std::string ItemConf::kProtoName = "ItemConf"; -bool ItemConf::Load(const std::string& dir, Format fmt, std::shared_ptr options /* = nullptr */) { +bool ItemConf::Load(const std::string& dir, Format fmt, std::shared_ptr options /* = nullptr */) { tableau::util::TimeProfiler profiler; - bool loaded = LoadMessage(data_, dir, fmt, options); + bool loaded = LoadMessagerInDir(data_, dir, fmt, options); bool ok = loaded ? ProcessAfterLoad() : false; stats_.duration = profiler.Elapse(); return ok; diff --git a/test/cpp-tableau-loader/src/protoconf/item_conf.pc.h b/test/cpp-tableau-loader/src/protoconf/item_conf.pc.h index e5afd07c..308b99e1 100644 --- a/test/cpp-tableau-loader/src/protoconf/item_conf.pc.h +++ b/test/cpp-tableau-loader/src/protoconf/item_conf.pc.h @@ -7,7 +7,7 @@ #pragma once #include -#include "messager.pc.h" +#include "load.pc.h" #include "util.pc.h" #include "item_conf.pb.h" @@ -15,7 +15,7 @@ namespace tableau { class ItemConf : public Messager { public: static const std::string& Name() { return kProtoName; } - virtual bool Load(const std::string& dir, Format fmt, std::shared_ptr options = nullptr) override; + virtual bool Load(const std::string& dir, Format fmt, std::shared_ptr options = nullptr) override; const protoconf::ItemConf& Data() const { return data_; } const google::protobuf::Message* Message() const override { return &data_; } diff --git a/test/cpp-tableau-loader/src/protoconf/load.pc.cc b/test/cpp-tableau-loader/src/protoconf/load.pc.cc index 46659083..d08d3f93 100644 --- a/test/cpp-tableau-loader/src/protoconf/load.pc.cc +++ b/test/cpp-tableau-loader/src/protoconf/load.pc.cc @@ -5,14 +5,204 @@ #include "load.pc.h" -#if __cplusplus >= 201703L #include -#endif #include "logger.pc.h" #include "util.pc.h" namespace tableau { + +// Forward declaration of the PatchMessage function +bool PatchMessage(google::protobuf::Message& dst, const google::protobuf::Message& src); + +std::shared_ptr ParseLoadOptions(std::shared_ptr opts) { + std::shared_ptr new_opts = std::make_shared(); + // set default values + new_opts->mode = LoadMode::kAll; + new_opts->read_func = util::ReadFile; + new_opts->load_func = LoadMessager; + + if (opts == nullptr) { + return new_opts; + } + if (opts->ignore_unknown_fields.has_value()) { + new_opts->ignore_unknown_fields = opts->ignore_unknown_fields; + } + if (!opts->patch_dirs.empty()) { + new_opts->patch_dirs = opts->patch_dirs; + } + if (opts->mode != LoadMode::kNone) { + new_opts->mode = opts->mode; + } + if (opts->read_func != nullptr) { + new_opts->read_func = opts->read_func; + } + if (opts->load_func != nullptr) { + new_opts->load_func = opts->load_func; + } + return new_opts; +} + +std::shared_ptr ParseMessagerOptions(std::shared_ptr opts, + const std::string& name) { + std::shared_ptr mopts; + if (auto iter = opts->messager_options.find(name); iter != opts->messager_options.end()) { + mopts = iter->second; + } else { + mopts = std::make_shared(); + } + if (mopts->ignore_unknown_fields.has_value()) { + mopts->ignore_unknown_fields = opts->ignore_unknown_fields; + } + if (mopts->patch_dirs.empty()) { + mopts->patch_dirs = opts->patch_dirs; + } + if (mopts->mode == LoadMode::kNone) { + mopts->mode = opts->mode; + } + if (mopts->read_func == nullptr) { + mopts->read_func = opts->read_func; + } + if (mopts->load_func == nullptr) { + mopts->load_func = opts->load_func; + } + return mopts; +} + +bool LoadMessagerWithPatch(google::protobuf::Message& msg, const std::string& path, Format fmt, tableau::Patch patch, + std::shared_ptr options /* = nullptr*/) { + if (options == nullptr) { + return LoadMessager(msg, path, fmt, nullptr); + } + if (options->mode == LoadMode::kOnlyMain) { + // ignore patch files when LoadMode::kModeOnlyMain specified + return options->load_func(msg, path, fmt, nullptr); + } + std::string name = util::GetProtoName(msg); + std::vector patch_paths; + if (!options->patch_paths.empty()) { + // patch path specified in PatchPaths, then use it instead of PatchDirs. + patch_paths = options->patch_paths; + } else { + std::string filename = name + util::Format2Ext(fmt); + for (auto&& patch_dir : options->patch_dirs) { + patch_paths.emplace_back((std::filesystem::path(patch_dir) / filename).make_preferred().string()); + } + } + + std::vector existed_patch_paths; + for (auto&& patch_path : patch_paths) { + if (util::ExistsFile(patch_path)) { + existed_patch_paths.emplace_back(patch_path); + } + } + if (existed_patch_paths.empty()) { + if (options->mode == LoadMode::kOnlyPatch) { + // just returns empty message when LoadMode::kModeOnlyPatch specified but no valid patch file provided. + return true; + } + // no valid patch path provided, then just load from the "main" file. + return options->load_func(msg, path, fmt, options); + } + + switch (patch) { + case tableau::PATCH_REPLACE: { + // just use the last "patch" file + std::string& patch_path = existed_patch_paths.back(); + if (!options->load_func(msg, patch_path, util::Ext2Format(util::GetExt(patch_path)), options)) { + return false; + } + break; + } + case tableau::PATCH_MERGE: { + if (options->mode != LoadMode::kOnlyPatch) { + // load msg from the "main" file + if (!options->load_func(msg, path, fmt, options)) { + return false; + } + } + // Create a new instance of the same type of the original message + google::protobuf::Message* patch_msg_ptr = msg.New(); + std::unique_ptr _auto_release(msg.New()); + // load patch_msg from each "patch" file + for (auto&& patch_path : existed_patch_paths) { + if (!options->load_func(*patch_msg_ptr, patch_path, util::Ext2Format(util::GetExt(patch_path)), options)) { + return false; + } + if (!PatchMessage(msg, *patch_msg_ptr)) { + return false; + } + } + break; + } + default: { + SetErrMsg("unknown patch type: " + util::GetPatchName(patch)); + return false; + } + } + ATOM_DEBUG("patched(%s) %s by %s: %s", util::GetPatchName(patch).c_str(), name.c_str(), + ATOM_VECTOR_STR(existed_patch_paths).c_str(), msg.ShortDebugString().c_str()); + return true; +} + +bool LoadMessager(google::protobuf::Message& msg, const std::string& path, Format fmt, + std::shared_ptr options /* = nullptr*/) { + std::string content; + ReadFunc read_func = util::ReadFile; + if (options != nullptr && options->read_func) { + read_func = options->read_func; + } + bool ok = read_func(path, content); + if (!ok) { + return false; + } + switch (fmt) { + case Format::kJSON: { + return util::JSON2Message(content, msg, options); + } + case Format::kText: { + return util::Text2Message(content, msg); + } + case Format::kBin: { + return util::Bin2Message(content, msg); + } + default: { + SetErrMsg("unknown format: " + std::to_string(static_cast(fmt))); + return false; + } + } +} + +bool LoadMessagerInDir(google::protobuf::Message& msg, const std::string& dir, Format fmt, + std::shared_ptr options /* = nullptr*/) { + std::string name = util::GetProtoName(msg); + std::string path; + if (options && !options->path.empty()) { + // path specified in Paths, then use it instead of dir. + path = options->path; + fmt = util::Ext2Format(util::GetExt(path)); + } + if (path.empty()) { + std::string filename = name + util::Format2Ext(fmt); + path = (std::filesystem::path(dir) / filename).make_preferred().string(); + } + + const google::protobuf::Descriptor* descriptor = msg.GetDescriptor(); + if (!descriptor) { + SetErrMsg("failed to get descriptor of message: " + name); + return false; + } + // access the extension directly using the generated identifier + const tableau::WorksheetOptions worksheet_options = descriptor->options().GetExtension(tableau::worksheet); + if (worksheet_options.patch() != tableau::PATCH_NONE) { + return LoadMessagerWithPatch(msg, path, fmt, worksheet_options.patch(), options); + } + if (options) { + return options->load_func(msg, path, fmt, options); + } + return LoadMessager(msg, path, fmt); +} + #ifdef _WIN32 #undef GetMessage #endif @@ -166,148 +356,4 @@ bool PatchMessage(google::protobuf::Message& dst, const google::protobuf::Messag dst_reflection->MutableUnknownFields(&dst)->MergeFrom(src_reflection->GetUnknownFields(src)); return true; } - -bool LoadMessageWithPatch(google::protobuf::Message& msg, const std::string& path, Format fmt, tableau::Patch patch, - std::shared_ptr options /* = nullptr*/) { - if (options == nullptr) { - return LoadMessageByPath(msg, path, fmt, nullptr); - } - if (options->mode == LoadMode::kModeOnlyMain) { - // ignore patch files when LoadMode::kModeOnlyMain specified - return LoadMessageByPath(msg, path, fmt, nullptr); - } - std::string name = util::GetProtoName(msg); - std::vector patch_paths; - auto iter = options->patch_paths.find(name); - if (iter != options->patch_paths.end()) { - // patch path specified in PatchPaths, then use it instead of PatchDirs. - patch_paths = iter->second; - } else { - std::string filename = name + util::Format2Ext(fmt); - for (auto&& patch_dir : options->patch_dirs) { -#if __cplusplus >= 201703L - patch_paths.emplace_back((std::filesystem::path(patch_dir) / filename).make_preferred().string()); -#else - patch_paths.emplace_back(patch_dir + kPathSeperator + filename); -#endif - } - } - - std::vector existed_patch_paths; - for (auto&& patch_path : patch_paths) { - if (util::ExistsFile(patch_path)) { - existed_patch_paths.emplace_back(patch_path); - } - } - if (existed_patch_paths.empty()) { - if (options->mode == LoadMode::kModeOnlyPatch) { - // just returns empty message when LoadMode::kModeOnlyPatch specified but no valid patch file provided. - return true; - } - // no valid patch path provided, then just load from the "main" file. - return LoadMessageByPath(msg, path, fmt, options); - } - - switch (patch) { - case tableau::PATCH_REPLACE: { - // just use the last "patch" file - std::string& patch_path = existed_patch_paths.back(); - if (!LoadMessageByPath(msg, patch_path, util::Ext2Format(util::GetExt(patch_path)), options)) { - return false; - } - break; - } - case tableau::PATCH_MERGE: { - if (options->mode != LoadMode::kModeOnlyPatch) { - // load msg from the "main" file - if (!LoadMessageByPath(msg, path, fmt, options)) { - return false; - } - } - // Create a new instance of the same type of the original message - google::protobuf::Message* patch_msg_ptr = msg.New(); - std::unique_ptr _auto_release(msg.New()); - // load patch_msg from each "patch" file - for (auto&& patch_path : existed_patch_paths) { - if (!LoadMessageByPath(*patch_msg_ptr, patch_path, util::Ext2Format(util::GetExt(patch_path)), options)) { - return false; - } - if (!PatchMessage(msg, *patch_msg_ptr)) { - return false; - } - } - break; - } - default: { - SetErrMsg("unknown patch type: " + util::GetPatchName(patch)); - return false; - } - } - ATOM_DEBUG("patched(%s) %s by %s: %s", util::GetPatchName(patch).c_str(), name.c_str(), - ATOM_VECTOR_STR(existed_patch_paths).c_str(), msg.ShortDebugString().c_str()); - return true; -} - -bool LoadMessageByPath(google::protobuf::Message& msg, const std::string& path, Format fmt, - std::shared_ptr options /* = nullptr*/) { - std::string content; - ReadFunc read_func = util::ReadFile; - if (options != nullptr && options->read_func) { - read_func = options->read_func; - } - bool ok = read_func(path, content); - if (!ok) { - return false; - } - switch (fmt) { - case Format::kJSON: { - return util::JSON2Message(content, msg, options); - } - case Format::kText: { - return util::Text2Message(content, msg); - } - case Format::kBin: { - return util::Bin2Message(content, msg); - } - default: { - SetErrMsg("unknown format: " + std::to_string(static_cast(fmt))); - return false; - } - } -} - -bool LoadMessage(google::protobuf::Message& msg, const std::string& dir, Format fmt, - std::shared_ptr options /* = nullptr*/) { - std::string name = util::GetProtoName(msg); - std::string path; - if (options) { - auto iter = options->paths.find(name); - if (iter != options->paths.end()) { - // path specified in Paths, then use it instead of dir. - path = iter->second; - fmt = util::Ext2Format(util::GetExt(iter->second)); - } - } - if (path.empty()) { - std::string filename = name + util::Format2Ext(fmt); -#if __cplusplus >= 201703L - path = (std::filesystem::path(dir) / filename).make_preferred().string(); -#else - path = dir + kPathSeperator + filename; -#endif - } - - const google::protobuf::Descriptor* descriptor = msg.GetDescriptor(); - if (!descriptor) { - SetErrMsg("failed to get descriptor of message: " + name); - return false; - } - // access the extension directly using the generated identifier - const tableau::WorksheetOptions worksheet_options = descriptor->options().GetExtension(tableau::worksheet); - if (worksheet_options.patch() != tableau::PATCH_NONE) { - return LoadMessageWithPatch(msg, path, fmt, worksheet_options.patch(), options); - } - - return LoadMessageByPath(msg, path, fmt, options); -} } // namespace tableau diff --git a/test/cpp-tableau-loader/src/protoconf/load.pc.h b/test/cpp-tableau-loader/src/protoconf/load.pc.h index 4fc8a035..b9088234 100644 --- a/test/cpp-tableau-loader/src/protoconf/load.pc.h +++ b/test/cpp-tableau-loader/src/protoconf/load.pc.h @@ -6,43 +6,105 @@ #pragma once #include +#include #include +#include #include #include "util.pc.h" namespace tableau { enum class LoadMode { - kModeDefault, - kModeOnlyMain, - kModeOnlyPatch, + kNone, + kAll, // Load all related files + kOnlyMain, // Only load the main file + kOnlyPatch, // Only load the patch files }; +struct MessagerOptions; +class Hub; + // ReadFunc reads the config file and returns its content. using ReadFunc = std::function; +// LoadFunc defines a func which can load message's content based on the given +// path, format, and options. +using LoadFunc = std::function options)>; -struct LoadOptions { - // read_func reads the config file and returns its content. - ReadFunc read_func; +// BaseOptions is the common struct for both global-level and messager-level +// options. +struct BaseOptions { // Whether to ignore unknown JSON fields during parsing. // // Refer https://protobuf.dev/reference/cpp/api-docs/google.protobuf.util.json_util/#JsonParseOptions. - bool ignore_unknown_fields = false; - // Paths maps each messager name to a corresponding config file path. + std::optional ignore_unknown_fields; + // Specify the directory paths for config patching. + std::vector patch_dirs; + // Specify the loading mode for config patching. + // - For LoadOptions, default is LoadMode::kModeAll. + // - For MessagerOptions, inherit from LoadOptions if not set. + LoadMode mode; + // You can specify custom read function to read a config file's content. + // - For LoadOptions, default is util::ReadFile. + // - For MessagerOptions, inherit from LoadOptions if not set. + ReadFunc read_func; + // You can specify custom load function to load a messager's content. + // - For LoadOptions, default is LoadMessage. + // - For MessagerOptions, inherit from LoadOptions if not set. + LoadFunc load_func; +}; + +// LoadOptionsOptions is the options struct, which contains both global-level and +// messager-level options. +struct LoadOptions : public BaseOptions { + // messager_options maps each messager name to a MessageOptions. + // If specified, then the messager will be parsed with the given options + // directly. + std::unordered_map> messager_options; +}; + +// MessagerOptions defines the options for loading a messager. +struct MessagerOptions : public BaseOptions { + // Path maps each messager name to a corresponding config file path. // If specified, then the main messager will be parsed from the file // directly, other than the specified load dir. - std::unordered_map paths; + std::string path; // Patch paths maps each messager name to one or multiple corresponding patch file paths. // If specified, then main messager will be patched. - std::unordered_map> patch_paths; - // Patch dirs specifies the directory paths for config patching. - std::vector patch_dirs; - // Mode specifies the loading mode for config patching. - LoadMode mode = LoadMode::kModeDefault; + std::vector patch_paths; +}; + +class Messager { + public: + struct Stats { + std::chrono::microseconds duration; // total load time consuming. + }; + + public: + virtual ~Messager() = default; + static const std::string& Name() { return kEmpty; } + const Stats& GetStats() { return stats_; } + // Load fills message from file in the specified directory and format. + virtual bool Load(const std::string& dir, Format fmt, std::shared_ptr options = nullptr) = 0; + // Message returns the inner message data. + virtual const google::protobuf::Message* Message() const { return nullptr; } + // callback after all messagers loaded. + virtual bool ProcessAfterLoadAll(const Hub&) { return true; } + + protected: + // callback after this messager loaded. + virtual bool ProcessAfterLoad() { return true; }; + Stats stats_; }; -bool LoadMessageByPath(google::protobuf::Message& msg, const std::string& path, Format fmt = Format::kJSON, - std::shared_ptr options = nullptr); -bool LoadMessage(google::protobuf::Message& msg, const std::string& dir, Format fmt = Format::kJSON, - std::shared_ptr options = nullptr); +// ParseLoadOptions parses load options with default global-level options. +std::shared_ptr ParseLoadOptions(std::shared_ptr opts); +// ParseMessagerOptions parses messager options with both global-level and +// messager-level options taken into consideration. +std::shared_ptr ParseMessagerOptions(std::shared_ptr opts, + const std::string& name); +bool LoadMessager(google::protobuf::Message& msg, const std::string& path, Format fmt = Format::kJSON, + std::shared_ptr options = nullptr); +bool LoadMessagerInDir(google::protobuf::Message& msg, const std::string& dir, Format fmt = Format::kJSON, + std::shared_ptr options = nullptr); } // namespace tableau diff --git a/test/cpp-tableau-loader/src/protoconf/messager.pc.h b/test/cpp-tableau-loader/src/protoconf/messager.pc.h deleted file mode 100644 index 3e8de4e7..00000000 --- a/test/cpp-tableau-loader/src/protoconf/messager.pc.h +++ /dev/null @@ -1,41 +0,0 @@ -// Code generated by protoc-gen-cpp-tableau-loader. DO NOT EDIT. -// versions: -// - protoc-gen-cpp-tableau-loader v0.8.0 -// - protoc v3.19.3 - -#pragma once -#include - -#include -#include -#include - -#include "util.pc.h" - -namespace tableau { -class Hub; -struct LoadOptions; - -class Messager { - public: - struct Stats { - std::chrono::microseconds duration; // total load time consuming. - }; - - public: - virtual ~Messager() = default; - static const std::string& Name() { return kEmpty; } - const Stats& GetStats() { return stats_; } - // Load fills message from file in the specified directory and format. - virtual bool Load(const std::string& dir, Format fmt, std::shared_ptr options = nullptr) = 0; - // Message returns the inner message data. - virtual const google::protobuf::Message* Message() const { return nullptr; } - // callback after all messagers loaded. - virtual bool ProcessAfterLoadAll(const Hub& hub) { return true; } - - protected: - // callback after this messager loaded. - virtual bool ProcessAfterLoad() { return true; }; - Stats stats_; -}; -} // namespace tableau diff --git a/test/cpp-tableau-loader/src/protoconf/patch_conf.pc.cc b/test/cpp-tableau-loader/src/protoconf/patch_conf.pc.cc index 22f8dc1d..66a14069 100644 --- a/test/cpp-tableau-loader/src/protoconf/patch_conf.pc.cc +++ b/test/cpp-tableau-loader/src/protoconf/patch_conf.pc.cc @@ -12,9 +12,9 @@ namespace tableau { const std::string PatchReplaceConf::kProtoName = "PatchReplaceConf"; -bool PatchReplaceConf::Load(const std::string& dir, Format fmt, std::shared_ptr options /* = nullptr */) { +bool PatchReplaceConf::Load(const std::string& dir, Format fmt, std::shared_ptr options /* = nullptr */) { tableau::util::TimeProfiler profiler; - bool loaded = LoadMessage(data_, dir, fmt, options); + bool loaded = LoadMessagerInDir(data_, dir, fmt, options); bool ok = loaded ? ProcessAfterLoad() : false; stats_.duration = profiler.Elapse(); return ok; @@ -22,9 +22,9 @@ bool PatchReplaceConf::Load(const std::string& dir, Format fmt, std::shared_ptr< const std::string PatchMergeConf::kProtoName = "PatchMergeConf"; -bool PatchMergeConf::Load(const std::string& dir, Format fmt, std::shared_ptr options /* = nullptr */) { +bool PatchMergeConf::Load(const std::string& dir, Format fmt, std::shared_ptr options /* = nullptr */) { tableau::util::TimeProfiler profiler; - bool loaded = LoadMessage(data_, dir, fmt, options); + bool loaded = LoadMessagerInDir(data_, dir, fmt, options); bool ok = loaded ? ProcessAfterLoad() : false; stats_.duration = profiler.Elapse(); return ok; @@ -40,9 +40,9 @@ const protoconf::Item* PatchMergeConf::Get(uint32_t id) const { const std::string RecursivePatchConf::kProtoName = "RecursivePatchConf"; -bool RecursivePatchConf::Load(const std::string& dir, Format fmt, std::shared_ptr options /* = nullptr */) { +bool RecursivePatchConf::Load(const std::string& dir, Format fmt, std::shared_ptr options /* = nullptr */) { tableau::util::TimeProfiler profiler; - bool loaded = LoadMessage(data_, dir, fmt, options); + bool loaded = LoadMessagerInDir(data_, dir, fmt, options); bool ok = loaded ? ProcessAfterLoad() : false; stats_.duration = profiler.Elapse(); return ok; diff --git a/test/cpp-tableau-loader/src/protoconf/patch_conf.pc.h b/test/cpp-tableau-loader/src/protoconf/patch_conf.pc.h index 6f0c366a..191fd685 100644 --- a/test/cpp-tableau-loader/src/protoconf/patch_conf.pc.h +++ b/test/cpp-tableau-loader/src/protoconf/patch_conf.pc.h @@ -7,7 +7,7 @@ #pragma once #include -#include "messager.pc.h" +#include "load.pc.h" #include "util.pc.h" #include "patch_conf.pb.h" @@ -15,7 +15,7 @@ namespace tableau { class PatchReplaceConf : public Messager { public: static const std::string& Name() { return kProtoName; } - virtual bool Load(const std::string& dir, Format fmt, std::shared_ptr options = nullptr) override; + virtual bool Load(const std::string& dir, Format fmt, std::shared_ptr options = nullptr) override; const protoconf::PatchReplaceConf& Data() const { return data_; } const google::protobuf::Message* Message() const override { return &data_; } @@ -28,7 +28,7 @@ class PatchReplaceConf : public Messager { class PatchMergeConf : public Messager { public: static const std::string& Name() { return kProtoName; } - virtual bool Load(const std::string& dir, Format fmt, std::shared_ptr options = nullptr) override; + virtual bool Load(const std::string& dir, Format fmt, std::shared_ptr options = nullptr) override; const protoconf::PatchMergeConf& Data() const { return data_; } const google::protobuf::Message* Message() const override { return &data_; } @@ -43,7 +43,7 @@ class PatchMergeConf : public Messager { class RecursivePatchConf : public Messager { public: static const std::string& Name() { return kProtoName; } - virtual bool Load(const std::string& dir, Format fmt, std::shared_ptr options = nullptr) override; + virtual bool Load(const std::string& dir, Format fmt, std::shared_ptr options = nullptr) override; const protoconf::RecursivePatchConf& Data() const { return data_; } const google::protobuf::Message* Message() const override { return &data_; } diff --git a/test/cpp-tableau-loader/src/protoconf/scheduler.pc.cc b/test/cpp-tableau-loader/src/protoconf/scheduler.pc.cc index b375f26b..8fa92e30 100644 --- a/test/cpp-tableau-loader/src/protoconf/scheduler.pc.cc +++ b/test/cpp-tableau-loader/src/protoconf/scheduler.pc.cc @@ -31,7 +31,7 @@ int Scheduler::LoopOnce() { for (auto&& job : jobs) { job(); } - count += jobs.size(); + count += static_cast(jobs.size()); return count; } diff --git a/test/cpp-tableau-loader/src/protoconf/test_conf.pc.cc b/test/cpp-tableau-loader/src/protoconf/test_conf.pc.cc index effa1bd9..c178d100 100644 --- a/test/cpp-tableau-loader/src/protoconf/test_conf.pc.cc +++ b/test/cpp-tableau-loader/src/protoconf/test_conf.pc.cc @@ -12,9 +12,9 @@ namespace tableau { const std::string ActivityConf::kProtoName = "ActivityConf"; -bool ActivityConf::Load(const std::string& dir, Format fmt, std::shared_ptr options /* = nullptr */) { +bool ActivityConf::Load(const std::string& dir, Format fmt, std::shared_ptr options /* = nullptr */) { tableau::util::TimeProfiler profiler; - bool loaded = LoadMessage(data_, dir, fmt, options); + bool loaded = LoadMessagerInDir(data_, dir, fmt, options); bool ok = loaded ? ProcessAfterLoad() : false; stats_.duration = profiler.Elapse(); return ok; @@ -240,9 +240,9 @@ const protoconf::Section::SectionItem* ActivityConf::FindFirstAward(uint32_t id) const std::string ChapterConf::kProtoName = "ChapterConf"; -bool ChapterConf::Load(const std::string& dir, Format fmt, std::shared_ptr options /* = nullptr */) { +bool ChapterConf::Load(const std::string& dir, Format fmt, std::shared_ptr options /* = nullptr */) { tableau::util::TimeProfiler profiler; - bool loaded = LoadMessage(data_, dir, fmt, options); + bool loaded = LoadMessagerInDir(data_, dir, fmt, options); bool ok = loaded ? ProcessAfterLoad() : false; stats_.duration = profiler.Elapse(); return ok; @@ -258,9 +258,9 @@ const protoconf::ChapterConf::Chapter* ChapterConf::Get(uint64_t id) const { const std::string ThemeConf::kProtoName = "ThemeConf"; -bool ThemeConf::Load(const std::string& dir, Format fmt, std::shared_ptr options /* = nullptr */) { +bool ThemeConf::Load(const std::string& dir, Format fmt, std::shared_ptr options /* = nullptr */) { tableau::util::TimeProfiler profiler; - bool loaded = LoadMessage(data_, dir, fmt, options); + bool loaded = LoadMessagerInDir(data_, dir, fmt, options); bool ok = loaded ? ProcessAfterLoad() : false; stats_.duration = profiler.Elapse(); return ok; @@ -288,9 +288,9 @@ const std::string* ThemeConf::Get(const std::string& name, const std::string& pa const std::string TaskConf::kProtoName = "TaskConf"; -bool TaskConf::Load(const std::string& dir, Format fmt, std::shared_ptr options /* = nullptr */) { +bool TaskConf::Load(const std::string& dir, Format fmt, std::shared_ptr options /* = nullptr */) { tableau::util::TimeProfiler profiler; - bool loaded = LoadMessage(data_, dir, fmt, options); + bool loaded = LoadMessagerInDir(data_, dir, fmt, options); bool ok = loaded ? ProcessAfterLoad() : false; stats_.duration = profiler.Elapse(); return ok; diff --git a/test/cpp-tableau-loader/src/protoconf/test_conf.pc.h b/test/cpp-tableau-loader/src/protoconf/test_conf.pc.h index 09aeb632..2b12c0e0 100644 --- a/test/cpp-tableau-loader/src/protoconf/test_conf.pc.h +++ b/test/cpp-tableau-loader/src/protoconf/test_conf.pc.h @@ -7,7 +7,7 @@ #pragma once #include -#include "messager.pc.h" +#include "load.pc.h" #include "util.pc.h" #include "test_conf.pb.h" @@ -15,7 +15,7 @@ namespace tableau { class ActivityConf : public Messager { public: static const std::string& Name() { return kProtoName; } - virtual bool Load(const std::string& dir, Format fmt, std::shared_ptr options = nullptr) override; + virtual bool Load(const std::string& dir, Format fmt, std::shared_ptr options = nullptr) override; const protoconf::ActivityConf& Data() const { return data_; } const google::protobuf::Message* Message() const override { return &data_; } @@ -102,7 +102,7 @@ class ActivityConf : public Messager { class ChapterConf : public Messager { public: static const std::string& Name() { return kProtoName; } - virtual bool Load(const std::string& dir, Format fmt, std::shared_ptr options = nullptr) override; + virtual bool Load(const std::string& dir, Format fmt, std::shared_ptr options = nullptr) override; const protoconf::ChapterConf& Data() const { return data_; } const google::protobuf::Message* Message() const override { return &data_; } @@ -117,7 +117,7 @@ class ChapterConf : public Messager { class ThemeConf : public Messager { public: static const std::string& Name() { return kProtoName; } - virtual bool Load(const std::string& dir, Format fmt, std::shared_ptr options = nullptr) override; + virtual bool Load(const std::string& dir, Format fmt, std::shared_ptr options = nullptr) override; const protoconf::ThemeConf& Data() const { return data_; } const google::protobuf::Message* Message() const override { return &data_; } @@ -133,7 +133,7 @@ class ThemeConf : public Messager { class TaskConf : public Messager { public: static const std::string& Name() { return kProtoName; } - virtual bool Load(const std::string& dir, Format fmt, std::shared_ptr options = nullptr) override; + virtual bool Load(const std::string& dir, Format fmt, std::shared_ptr options = nullptr) override; const protoconf::TaskConf& Data() const { return data_; } const google::protobuf::Message* Message() const override { return &data_; } diff --git a/test/cpp-tableau-loader/src/protoconf/util.pc.cc b/test/cpp-tableau-loader/src/protoconf/util.pc.cc index fb61f76e..98ee62de 100644 --- a/test/cpp-tableau-loader/src/protoconf/util.pc.cc +++ b/test/cpp-tableau-loader/src/protoconf/util.pc.cc @@ -8,30 +8,15 @@ #include #include +#include #include #include #include -#if __cplusplus >= 201703L -#include -#else -#ifdef _WIN32 -#include -#include -#else -#include -#endif -#endif - #include "load.pc.h" #include "logger.pc.h" -#include "messager.pc.h" namespace tableau { -#ifdef _WIN32 -#define mkdir(path, mode) _mkdir(path) -#endif - static thread_local std::string g_err_msg; const std::string& GetErrMsg() { return g_err_msg; } void SetErrMsg(const std::string& msg) { g_err_msg = msg; } @@ -43,7 +28,6 @@ const std::string kBinExt = ".bin"; namespace util { int Mkdir(const std::string& path) { -#if __cplusplus >= 201703L std::error_code ec; if (!std::filesystem::create_directories(path, ec)) { if (ec) { @@ -52,46 +36,11 @@ int Mkdir(const std::string& path) { } } return 0; -#else - std::string path_ = path + kPathSeperator; - struct stat info; - for (size_t pos = path_.find(kPathSeperator, 0); pos != std::string::npos; pos = path_.find(kPathSeperator, pos)) { - ++pos; - auto sub_dir = path_.substr(0, pos); - if (stat(sub_dir.c_str(), &info) == 0 && info.st_mode & S_IFDIR) { - continue; - } - int status = mkdir(sub_dir.c_str(), 0755); - if (status != 0) { - std::cerr << "system error: " << strerror(errno) << std::endl; - return -1; - } - } - return 0; -#endif } -std::string GetDir(const std::string& path) { -#if __cplusplus >= 201703L - return std::filesystem::path(path).parent_path().string(); -#else - size_t pos = path.find_last_of(kPathSeperator); - if (pos != std::string::npos) { - return path.substr(0, pos); - } - return kEmpty; -#endif -} +std::string GetDir(const std::string& path) { return std::filesystem::path(path).parent_path().string(); } -bool ExistsFile(const std::string& filename) { -#if __cplusplus >= 201703L - return std::filesystem::exists(filename); -#else - std::ifstream file(filename); - // returns true if the file exists and is accessible - return file.good(); -#endif -} +bool ExistsFile(const std::string& filename) { return std::filesystem::exists(filename); } bool ReadFile(const std::string& filename, std::string& content) { std::ifstream file(filename); @@ -139,11 +88,11 @@ const std::string& Format2Ext(Format fmt) { } bool JSON2Message(const std::string& json, google::protobuf::Message& msg, - std::shared_ptr options /* = nullptr */) { + std::shared_ptr options /* = nullptr */) { google::protobuf::util::Status status; if (options != nullptr) { google::protobuf::util::JsonParseOptions parse_options; - parse_options.ignore_unknown_fields = options->ignore_unknown_fields; + parse_options.ignore_unknown_fields = options->ignore_unknown_fields.value_or(false); status = google::protobuf::util::JsonStringToMessage(json, &msg, parse_options); } else { status = google::protobuf::util::JsonStringToMessage(json, &msg); diff --git a/test/cpp-tableau-loader/src/protoconf/util.pc.h b/test/cpp-tableau-loader/src/protoconf/util.pc.h index e6b70f90..6d5a94fc 100644 --- a/test/cpp-tableau-loader/src/protoconf/util.pc.h +++ b/test/cpp-tableau-loader/src/protoconf/util.pc.h @@ -16,15 +16,6 @@ namespace tableau { const std::string& GetErrMsg(); void SetErrMsg(const std::string& msg); -#if __cplusplus < 201703L -// Platform-specific path separator -#ifdef _WIN32 -constexpr char kPathSeperator = '\\'; -#else -constexpr char kPathSeperator = '/'; -#endif -#endif - enum class Format { kUnknown, kJSON, @@ -38,7 +29,7 @@ extern const std::string kJSONExt; extern const std::string kTextExt; extern const std::string kBinExt; -struct LoadOptions; +struct MessagerOptions; namespace util { // Combine hash values @@ -46,7 +37,7 @@ namespace util { // References: // - https://stackoverflow.com/questions/2590677/how-do-i-combine-hash-values-in-c0x // - https://stackoverflow.com/questions/17016175/c-unordered-map-using-a-custom-class-type-as-the-key -inline void HashCombine(std::size_t& seed) {} +inline void HashCombine(std::size_t&) {} template inline void HashCombine(std::size_t& seed, const T& v, O... others) { @@ -79,16 +70,16 @@ bool ReadFile(const std::string& filename, std::string& content); std::string GetExt(const std::string& path); // Convert file extension to Format type. // NOTE: ext includes dot ".", such as: -// - kJSONExt:".json" -// - kTextExt".txt" -// - kBinExt".bin" +// - kJSONExt: ".json" +// - kTextExt: ".txt" +// - kBinExt: ".bin" Format Ext2Format(const std::string& ext); // Empty string will be returned if an unsupported enum value has been passed, // and the error message can be obtained by GetErrMsg(). const std::string& Format2Ext(Format fmt); bool JSON2Message(const std::string& json, google::protobuf::Message& msg, - std::shared_ptr options = nullptr); + std::shared_ptr options = nullptr); bool Text2Message(const std::string& text, google::protobuf::Message& msg); bool Bin2Message(const std::string& bin, google::protobuf::Message& msg);