You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.20 04:33:09