Бэкенд на плюсах для нормальных людей

Данил Сидорук

МТУСИ, 1 2 курс

Плюсы — универсальный язык

А если мы хотим написать бэкенд?

Gin Web Framework

Python

Go

C++

#pragma @RestController path="/"
class TeBkRestController {
public:
	#pragma @GET path="/updates" statusCode="200" ocontentType="application/json"
	std::vector<TeBkWorld> updates(#pragma @QueryParam name="queries" std::string queries) {
      // ...
      DataSourceInterface* sqli = DataSourceManager::getImpl();

      try {          
          // ...
          DataSourceManager::cleanImpl(sqli);
      } catch(const std::exception& e) {
          DataSourceManager::cleanImpl(sqli);
          throw e;
      }
  }
};
Название Звездочки Создание Последнее обновление
Drogon 9.6k 2018 Вчера
Oat++ 6.6k 2018 Неделю назад
Crow 2k 2014 Неделю назад
userver 1.8k --- 1 час назад
Wt
 
1.5k 2017 Май 2023
Cutelyst 848 2015 Июнь 2023
Название Звездочки Создание Последнее обновление
Drogon 9.6k 2018 Вчера
Oat++ 6.6k 2018 Неделю назад
Crow 2k 2014 Неделю назад
userver 1.8k --- 1 час назад
Wt
 
1.5k 2017 Май 2023
Cutelyst 848 2015 Июнь 2023

Drogon

Controllers

Filters

Template engine

ORM

class PastesCtrl : public drogon::HttpController<PastesCtrl> {
public:
  METHOD_LIST_BEGIN
  ADD_METHOD_TO(PastesCtrl::getOne, "/pastes?code={1}", Get);
  METHOD_LIST_END
  Task<HttpResponsePtr> getOne(HttpRequestPtr request, const std::string &code);
};
Task<HttpResponsePtr> PastesCtrl::getOne(HttpRequestPtr request, const std::string &code) {
  auto dbClientPtr = getDbClient();
  try {
    auto result = co_await dbClientPtr->execSqlCoro(kSelectPaste, code);
    if (result.size()) {
      auto response = HttpResponse::newHttpResponse();
      response->setBody(result.front()["content"].as<std::string>());
      co_return response;
    } else {
      co_return notFoundErrorResponse;
    }
  } catch (const DrogonDbException &err) {
    LOG_ERROR << err.base().what();
    co_return databaseErrorResponse;
  }
}

Controllers

Filters

Template engine

ORM

class PingCtrl : public drogon::HttpController<PingCtrl> {
public:
  METHOD_LIST_BEGIN
  ADD_METHOD_TO(PingCtrl::ping, "/ping", Get, "drogon::LocalHostFilter");
  METHOD_LIST_END
  Task<HttpResponsePtr> ping(HttpRequestPtr request);
};
drogon::HttpAppFramework::instance().registerHandler(
    "/", [=](const auto &request) {
      auto parameters = request->getParameters();
      HttpViewData data{{"parameters", parameters}};
      auto response = HttpResponse::newHttpViewResponse("Parameters.csp", data);
      co_return response;
});
<body>
  <h1>Parameters</h1>
  <table>
    <tr>
      <th>name</th>
      <th>value</th>
    </tr>
    <%c++ for(auto it: parameters){%>
    <tr>
      <td>{% it.first %}</td>
      <td>{% it.second %}</td>
    </tr>
    <%c++}%>
  </table>
</body>

Controllers

Filters

Template engine

ORM

http://localhost/?p1=a&p2=b&p3=c

Go To!

Controllers

Filters

Template engine

ORM

name type
id SERIAL
code VARCHAR
content VARCHAR
token VARCHAR

pastes table

model_ctl create model models/ --table=pastes
Mapper<Pastes> mapper(dbClientPtr);

mapper.findByPrimaryKey(id, []() {
	// SUCCESS
}), []() {
	// DATABASE ERROR
}); // non-blocking

Pastes paste(json);
mapper.insertFuture(paste);

paste.setContent("So, let it be...")
mapper.updateFuture(paste);
Название Звездочки Создание Последнее обновление
Drogon 9.6k 2018 Вчера
Oat++ 6.6k 2018 Неделю назад
Crow 2k 2014 Неделю назад
userver 1.8k --- 1 час назад
Wt
 
1.5k 2017 Май 2023
Cutelyst 848 2015 Июнь 2023

Oat++

class PasteController : public ApiController {
  // ...
  ENDPOINT("GET", "pastes", getPaste, QUERY(String, code)) {
    return createResponse(Status::CODE_200, m_userService.getPaste(code));
  }
  // ...
};
class PasteDb : public DbClient {
public:
  // ...
  QUERY(getPasteContent, "SELECT content FROM pastes WHERE code=:code;",
        PREPARE(true), PARAM(oatpp::String, code));
  // ...
};
class GetPasteDTO : public DTO {
  DTO_INIT(GetPasteDTO, DTO)
  DTO_FIELD(String, content);
};
String PasteService::getPaste(const String &code) {
  auto res = m_database->getPasteContent(code);
  auto result = dbResult->fetch<Vector<Object<GetPasteDTO>>>();
  return result[0]->content;
}

Куча макросов

#include OATPP_CODEGEN_BEGIN(ApiController) 
ENDPOINT()
QUERY()
ENDPOINT("GET", "pastes", getPaste, QUERY(String, code)) {
  return createResponse(Status::CODE_200, m_userService.getPaste(code));
}
QUERY(getPasteContent, "SELECT content FROM pastes WHERE code=:code;",
  PREPARE(true), PARAM(oatpp::String, code));

Абсолютная власть DTO (Data Transfer Objects)

class GetPasteDTO : public DTO {
  DTO_INIT(GetPasteDTO, DTO)
  DTO_FIELD(String, content);
};
String PasteService::getPaste(const String &code) {
  auto res = m_database->getPasteContent(code);
  auto result = dbResult->fetch<Vector<Object<GetPasteDTO>>>();
  return result[0]->content;
}
QUERY(getPasteContent, "SELECT content FROM pastes WHERE code=:code;",
  PREPARE(true), PARAM(oatpp::String, code));

Отсутствие единого конфига

Название Звездочки Создание Последнее обновление
Drogon 9.6k 2018 Вчера
Oat++ 6.6k 2018 Неделю назад
Crow 2k 2014 Неделю назад
userver 1.8k --- 1 час назад
Wt
 
1.5k 2017 Май 2023
Cutelyst 848 2015 Июнь 2023

Crow

Мини-мини фреймворк

#include "crow.h"

int main() {
    crow::SimpleApp app;
    CROW_ROUTE(app, "/")([](){
        return "Hello world";
    });
    app.port(8080).run();
}
CROW_ROUTE(app, "/json")
([]{
    crow::json::wvalue x({{"message", "Hello, World!"}});
    return x;
});
CROW_ROUTE(app,"/number/<int>")
([](int count){
    return crow::response(std::to_string(count));
});

Нет даже драйверов для баз данных

Название Звездочки Создание Последнее обновление
Drogon 9.6k 2018 Вчера
Oat++ 6.6k 2018 Неделю назад
Crow 2k 2014 Неделю назад
userver 1.8k --- 1 час назад
Wt
 
1.5k 2017 Май 2023
Cutelyst 848 2015 Июнь 2023

userver

Компоненты

int main(int argc, char* argv[]) {
  auto component_list =
      userver::components::MinimalServerComponentList()
          .Append<userver::components::Postgres>("pastes-database")
          .Append<userver::components::TestsuiteSupport>()
          .Append<userver::clients::dns::Component>()
          .Append<pastebin::PastesController>();

  return userver::utils::DaemonMain(argc, argv, component_list);
}
class PastesController final : public server::handlers::HttpHandlerBase {
 public:
  static constexpr std::string_view kName = "handler-pastes";

  PastesController(const components::ComponentConfig& config,
                   const components::ComponentContext& context);

  std::string HandleRequestThrow(
      const server::http::HttpRequest& request,
      server::request::RequestContext&) const override;

 private:
  std::string CreatePaste(const server::http::HttpRequest&) const;
  std::string GetPaste(std::string_view code,
                       const server::http::HttpRequest&) const;
  std::string UpdatePaste(std::string_view token,
                          const server::http::HttpRequest&) const;
  std::string DeletePaste(std::string_view token,
                          const server::http::HttpRequest&) const;

  storages::postgres::ClusterPtr pg_cluster_;
};
std::string PastesController::GetPaste(std::string_view code, const server::http::HttpRequest&) const {
  storages::postgres::ResultSet res = pg_cluster_->Execute(
      storages::postgres::ClusterHostType::kSlave, kSelectPasteContent, code);
  if (res.IsEmpty()) {
    throw not_found_error;
  }
  return res.AsSingleRow<std::string>();
}

Прекрасные конфиги

handler-pastes:
  path: /pastes
  method: GET,POST,PUT,DELETE
  task_processor: main-task-processor
"POSTGRES_CONNECTION_POOL_SETTINGS": {
  "pastes-database": {
    "max_pool_size": 15,
    "max_queue_size": 200,
    "min_pool_size": 8
  }
}
worker-threads: 4
worker-fs-threads: 2

Хорошая документация

Множество примеров

Production-ready

Название Звездочки Создание Последнее обновление
Drogon 9.6k 2018 Вчера
Oat++ 6.6k 2018 Неделю назад
Crow 2k 2014 Неделю назад
userver 1.8k --- 1 час назад
Wt
 
1.5k 2017 Май 2023
Cutelyst 848 2015 Июнь 2023

Сравнение

Lines Of Code

Место Фреймворк Lines Of Code %
1 userver 162 100
2 Drogon 165 101
3 Oat++ 310 191

Performance

Место Фреймворк RPS %
1 userver 39547 100
2 Drogon 35890 90
3 Oat++ 31512 79