Oat++框架中打印枚举值的最优实现方法是什么?
Oat++枚举值打印优化方案
我正在撰写面向学生的Oat++框架相关文章,遇到了枚举值打印的问题。目前使用的打印代码如下:
std::cout << "Genre: " << book->genre.getEntryByValue(*book->genre).name.std_str() << std::endl;
这种写法繁琐且不够专业,加上Oat++基于模板定义驱动,从源码中查找更合适的方法也比较困难。
完整客户端代码
ClientComponent.hpp
#include <oatpp/parser/json/mapping/ObjectMapper.hpp> #include <oatpp-libressl/Config.hpp> #include <oatpp-libressl/client/ConnectionProvider.hpp> #include <oatpp/core/macro/component.hpp> constexpr const int listeningPortHttps = 8443; constexpr const char listeningAddress[] = "localhost"; class ClientComponent { public: OATPP_CREATE_COMPONENT(std::shared_ptr<oatpp::network::ClientConnectionProvider>, sslClientConnectionProvider)([] { auto config = oatpp::libressl::Config::createShared(); tls_config_insecure_noverifycert(config->getTLSConfig()); tls_config_insecure_noverifyname(config->getTLSConfig()); return oatpp::libressl::client::ConnectionProvider::createShared(config, {listeningAddress, listeningPortHttps}); }()); };
BooksDto.hpp
#include <oatpp/core/macro/codegen.hpp> #include <oatpp-swagger/Controller.hpp> #include OATPP_CODEGEN_BEGIN(DTO) ENUM(BookGenre, v_int32, VALUE(UNKNOWN, 0, "unkown book genre"), VALUE(FICTION, 1, "fiction"), VALUE(NONFICTION, 1, "non-fiction"), VALUE(FANTASY, 1, "fantasy"), VALUE(BIOGRAPHY, 1, "biography"), VALUE(SCIFI, 1, "scifi") ); class BookDto : public oatpp::DTO { DTO_INIT(BookDto, DTO /* Extends */) DTO_FIELD(UInt32, id); DTO_FIELD(String, title, "title"); DTO_FIELD(String, author, "author"); DTO_FIELD(Enum<BookGenre>::AsString, genre, "genre"); }; #include OATPP_CODEGEN_END(DTO)
Client.hpp
// #pragma once #include <oatpp/web/client/ApiClient.hpp> #include OATPP_CODEGEN_BEGIN(ApiClient) //<- Begin Codegen class BookClient : public oatpp::web::client::ApiClient { public: API_CLIENT_INIT(BookClient) API_CALL("POST", "/books", createBook, BODY_DTO(Object<BookDto>, bookDto), HEADER(String, authorization, "Authorization")) API_CALL("GET", "/books", getBooks) API_CALL("GET", "/books/{id}", getBookById, PATH(UInt64, id)) API_CALL("PUT", "/books/{id}", updateBook, PATH(UInt64, id), BODY_DTO(Object<BookDto>, bookDto), HEADER(String, authorization, "Authorization")) API_CALL("DELETE", "/books/{id}", deleteBook, PATH(UInt64, id), HEADER(String, authorization, "Authorization")) }; #include OATPP_CODEGEN_END(ApiClient) //<- End Codegen
main.cpp
#include <iostream> #include <oatpp/network/tcp/client/ConnectionProvider.hpp> #include <oatpp/web/client/HttpRequestExecutor.hpp> constexpr const char* USER = "my_user"; constexpr const char* PASSWORD = "my-secret-password"; void addBook(std::string_view basicAuthHeader, const char* title, const char* author, BookGenre genre, auto& bookApiClient); void printBooks(auto& bookApiClient, auto& objectMapper); void updateBook(std::string_view basicAuthHeader, v_uint64 id, const char* title, const char* author, BookGenre genre, auto& bookApiClient); void deleteBook(std::string_view basicAuthHeader, v_uint64 id, auto& bookApiClient); void printBookById(v_uint64 id, auto& bookApiClient, auto& objectMapper); void runClient(); std::string base64_encode(const std::string &in); // ignore this function std::string createBasicAuthHeader(const std::string& user, const std::string& password) { return "Basic " + base64_encode(user + ":" + password); } ///////////////// main() int main() { oatpp::base::Environment::init(); runClient(); oatpp::base::Environment::destroy(); } void runClient() { std::shared_ptr<oatpp::web::client::HttpRequestExecutor> httpRequestExecutor; constexpr bool httpsConnection = true; if constexpr(httpsConnection) { ClientComponent clientComponent; OATPP_COMPONENT(std::shared_ptr<oatpp::network::ClientConnectionProvider>, sslClientConnectionProvider); httpRequestExecutor = oatpp::web::client::HttpRequestExecutor::createShared(sslClientConnectionProvider); } else { /// for HTTP: auto connectionProvider = oatpp::network::tcp::client::ConnectionProvider::createShared({"localhost", 8000, oatpp::network::Address::IP_4}); httpRequestExecutor = oatpp::web::client::HttpRequestExecutor::createShared(connectionProvider); } auto jsonObjectMapper = oatpp::parser::json::mapping::ObjectMapper::createShared(); auto bookApiClient = BookClient::createShared(httpRequestExecutor, jsonObjectMapper); std::string basicAuthHeader = createBasicAuthHeader(USER, PASSWORD); addBook(basicAuthHeader, "Opus magnum C++11", "Jerzy Grebosz", BookGenre::FICTION, bookApiClient); addBook(basicAuthHeader, "Skuteczny nowoczesny C++. 42 sposoby lepszego poslugiwania się jezykami C++11 i C++14", "Scott Meyers", BookGenre::NONFICTION, bookApiClient); addBook(basicAuthHeader, "C++17 STL. Receptury", "Jacek Galowicz", BookGenre::NONFICTION, bookApiClient); printBooks(bookApiClient, jsonObjectMapper); std::cout << std::endl; updateBook(basicAuthHeader, 1, "Misja w nadprzestrzen C++14/17", "Jerzy Grebosz", BookGenre::FANTASY, bookApiClient); printBookById(1, bookApiClient, jsonObjectMapper); deleteBook(basicAuthHeader, 1, bookApiClient); printBooks(bookApiClient, jsonObjectMapper); auto rawData = bookApiClient->getBooks()->readBodyToString(); OATPP_LOGD(BookClient::TAG, "[doGet] data='%s'", rawData->c_str()); } void addBook(std::string_view basicAuthHeader, const char* title, const char* author, BookGenre genre, auto& bookApiClient) { auto book = BookDto::createShared(); book->title = title; book->author = author; book->genre = genre; auto response = bookApiClient->createBook(book, basicAuthHeader.data()); const BookClient::Status status{response->getStatusCode(), "creation book error"}; OATPP_ASSERT_HTTP(response->getStatusCode() == 200, status, std::string("Failed to create book: ") + title); } void printBooks(auto& bookApiClient, auto& objectMapper) { auto response = bookApiClient->getBooks(); const BookClient::Status status{response->getStatusCode(), "getting books error"}; OATPP_ASSERT_HTTP(response->getStatusCode() == 200, status, "Failed to get books"); auto books = response->template readBodyToDto<oatpp::List<oatpp::Object<BookDto>>>(objectMapper); for (const auto& book : *books) { std::cout << "Book ID: " << book->id << std::endl; std::cout << "Title: " << book->title->c_str() << std::endl; std::cout << "Author: " << book->author->c_str() << std::endl; std::cout << "Genre: " << book->genre.getEntryByValue(*book->genre).name.std_str() << std::endl; std::cout << "--------" << std::endl; } } void updateBook(std::string_view basicAuthHeader, v_uint64 id, const char* title, const char* author, BookGenre genre, auto& bookApiClient) { auto book = BookDto::createShared(); book->title = title; book->author = author; book->genre = genre; auto response = bookApiClient->updateBook(id, book, basicAuthHeader.data()); const BookClient::Status status{response->getStatusCode(), "updating book error"}; OATPP_ASSERT_HTTP(response->getStatusCode() == 200, status, std::string("Failed to update book: ") + title); } void deleteBook(std::string_view basicAuthHeader, v_uint64 id, auto& bookApiClient) { auto response = bookApiClient->deleteBook(id, basicAuthHeader.data()); const BookClient::Status status{response->getStatusCode(), "deleting book error"}; OATPP_ASSERT_HTTP(response->getStatusCode() == 200, status, std::string("Failed to delete book with ID: ") + std::to_string(id)); } void printBookById(v_uint64 id, auto& bookApiClient, auto& objectMapper) { auto response = bookApiClient->getBookById(id); const BookClient::Status status{response->getStatusCode(), "getting book by id error"}; OATPP_ASSERT_HTTP(response->getStatusCode() == 200, status, "Failed to get book by ID"); auto book = response->template readBodyToDto<oatpp::Object<BookDto>>(objectMapper); std::cout << "Book ID: " << book->id << std::endl; std::cout << "Title: " << book->title->c_str() << std::endl; std::cout << "Author: " << book->author->c_str() << std::endl; std::cout << "Genre: " << book->genre.getEntryByValue(*book->genre).name.std_str() << std::endl; std::cout << "--------" << std::endl; } std::string base64_encode(const std::string &in) // ignore this function { std::string out; int val = 0, valb = -6; for (unsigned char c : in) { val = (val << 8) + c; valb += 8; while (valb >= 0) { out.push_back("ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"[(val >> valb) & 0x3F]); valb -= 6; } } if (valb > -6) out.push_back("ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"[((val << 8) >> (valb + 8)) & 0x3F]); while (out.size() % 4) out.push_back('='); return out; }
优化方案
方案1:使用Enum自带的toString()方法
Oat++的Enum类内置了直接获取枚举名称的方法,无需手动调用getEntryByValue。修改打印代码为:
std::cout << "Genre: " << book->genre.toString()->c_str() << std::endl;
toString()会直接返回对应枚举值的名称字符串(如"fiction"、"non-fiction"),代码更简洁易读。
方案2:重载std::ostream运算符(更符合C++习惯)
如果想让枚举值可以直接通过std::cout打印,无需每次调用方法,可在BooksDto.hpp的OATPP_CODEGEN_END(DTO)之后添加运算符重载:
#include OATPP_CODEGEN_END(DTO) // 新增ostream运算符重载 std::ostream& operator<<(std::ostream& os, const oatpp::Enum<BookGenre>::AsString& genre) { os << genre.toString()->c_str(); return os; }
之后打印时只需写:
std::cout << "Genre: " << book->genre << std::endl;
这种方式更贴合C++编码习惯,代码更简洁,也更适合作为学生示例。
内容的提问来源于stack exchange,提问作者baziorek
相关产品推荐
相关产品推荐

