I want to make an article about Oat++ framework for students. Unfortunately I have a problem with printing an enum value. I was experimenting with code and currently I found a way:
std::cout << "Genre: " << book->genre.getEntryByValue(*book->genre).name.std_str() << std::endl;
But I think that this is not a professional way. Unfortunately coda is templated-define-driven so it is really hard to find out something from code.
Here is the full client code:
////////// file: 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});
}());
};
////////// file: 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)
////////// file: 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
////////// file: 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;
}
getEntryByValye()
is a static method of oatpp::Enum
so you don't need an object instance to use it:
auto enumEntry = oatpp::Enum<BookGenre>::getEntryByValue(BookGenre::NONFICTION);
OATPP_LOGd("TEST-ENUM", "name: '{}'", enumEntry.name.toString())
Here is a complete example to play with (Oat++ 1.4.0
):
#include "oatpp/Types.hpp"
#include "oatpp/base/Log.hpp"
#include "oatpp/macro/codegen.hpp"
#include OATPP_CODEGEN_BEGIN(DTO)
ENUM(BookGenre, v_int32,
// | name | int-value | qualifier | description
VALUE(UNKNOWN, 0, "unknown", "unkown book genre"),
VALUE(FICTION, 1, "fiction", "fiction book genre"),
VALUE(NONFICTION, 2, "non_fiction", "non-fiction book genre"),
VALUE(FANTASY, 3, "fantasy", "fantasy book genre"),
VALUE(BIOGRAPHY, 4, "biography", "biography book genre"),
VALUE(SCIFI, 5, "scifi", "scifi book genre")
);
#include OATPP_CODEGEN_END(DTO)
void runTests() {
auto enumEntry = oatpp::Enum<BookGenre>::getEntryByValue(BookGenre::NONFICTION);
OATPP_LOGd("TEST-ENUM", "unqualifiedName: '{}', name: '{}', description: '{}'",
enumEntry.unqualifiedName.toString(), enumEntry.name.toString(), enumEntry.description.toString())
}
int main() {
oatpp::Environment::init();
runTests();
oatpp::Environment::destroy();
return 0;
}