Using folly (instead of boost promise/future) for coroutines
This commit is contained in:
parent
46a8b84869
commit
f4c656ef89
@ -6,31 +6,24 @@ set(CMAKE_CXX_STANDARD 20)
|
|||||||
|
|
||||||
find_package(PkgConfig REQUIRED)
|
find_package(PkgConfig REQUIRED)
|
||||||
find_package(nlohmann_json REQUIRED)
|
find_package(nlohmann_json REQUIRED)
|
||||||
find_package(Boost 1.70 COMPONENTS thread REQUIRED)
|
find_package(folly REQUIRED)
|
||||||
|
|
||||||
pkg_check_modules(GTKMM REQUIRED gtkmm-4.0)
|
pkg_check_modules(GTKMM REQUIRED gtkmm-4.0)
|
||||||
pkg_check_modules(GLIBMM REQUIRED glibmm-2.68)
|
pkg_check_modules(GLIBMM REQUIRED glibmm-2.68)
|
||||||
pkg_check_modules(LIBSOUP REQUIRED libsoup-2.4)
|
pkg_check_modules(LIBSOUP REQUIRED libsoup-2.4)
|
||||||
pkg_check_modules(LIBADWAITA REQUIRED libadwaita-1)
|
#pkg_check_modules(LIBADWAITA REQUIRED libadwaita-1)
|
||||||
|
|
||||||
include_directories(${GTKMM_INCLUDE_DIRS}
|
include_directories(${GTKMM_INCLUDE_DIRS}
|
||||||
${GLIBMM_INCLUDE_DIRS}
|
${GLIBMM_INCLUDE_DIRS}
|
||||||
${LIBSOUP_INCLUDE_DIRS}
|
${LIBSOUP_INCLUDE_DIRS})
|
||||||
${Boost_INCLUDE_DIR}
|
|
||||||
${LIBADWAITA_INCLUDE_DIRS})
|
|
||||||
|
|
||||||
link_directories(${GTKMM_LIBRARY_DIRS}
|
link_directories(${GTKMM_LIBRARY_DIRS}
|
||||||
${GLIBMM_LIBRARY_DIRS}
|
${GLIBMM_LIBRARY_DIRS}
|
||||||
${LIBSOUP_LIBRARY_DIRS}
|
${LIBSOUP_LIBRARY_DIRS})
|
||||||
${Boost_LIBRARY_DIRS}
|
|
||||||
${LIBADWAITA_LIBRARY_DIRS})
|
|
||||||
|
|
||||||
add_definitions(-DBOOST_THREAD_PROVIDES_FUTURE -DBOOST_THREAD_PROVIDES_FUTURE_CONTINUATION -DBOOST_THREAD_PROVIDES_PROMISE_LAZY -DBOOST_THREAD_VERSION=4)
|
add_executable(autocat_gnome main.cpp gui/MainWindow.cpp gui/MainWindow.h gui/LoginWindow.cpp gui/LoginWindow.h services/Api.cpp services/Api.h models/User.cpp models/User.h services/Settings.cpp services/Settings.h gui/TitleBar.cpp gui/TitleBar.h coro/Coro.h)
|
||||||
|
|
||||||
add_executable(autocat_gnome main.cpp gui/MainWindow.cpp gui/MainWindow.h gui/LoginWindow.cpp gui/LoginWindow.h services/Api.cpp services/Api.h coro/Task.h models/User.cpp models/User.h services/Settings.cpp services/Settings.h app/App.cpp app/App.h gui/TitleBar.cpp gui/TitleBar.h)
|
|
||||||
target_link_libraries(autocat_gnome ${GTKMM_LIBRARIES}
|
target_link_libraries(autocat_gnome ${GTKMM_LIBRARIES}
|
||||||
${GLIBMM_LIBRARIES}
|
${GLIBMM_LIBRARIES}
|
||||||
${LIBSOUP_LIBRARIES}
|
${LIBSOUP_LIBRARIES}
|
||||||
nlohmann_json::nlohmann_json
|
nlohmann_json::nlohmann_json
|
||||||
${Boost_LIBRARIES}
|
Folly::folly)
|
||||||
${LIBADWAITA_LIBRARIES})
|
|
||||||
|
|||||||
48
app/App.cpp
48
app/App.cpp
@ -1,48 +0,0 @@
|
|||||||
//
|
|
||||||
// Created by selim on 02.02.2022.
|
|
||||||
//
|
|
||||||
|
|
||||||
#include "App.h"
|
|
||||||
#include "../gui/MainWindow.h"
|
|
||||||
|
|
||||||
static void activateApp(GtkApplication *gtkApp, void* data) {
|
|
||||||
auto app = reinterpret_cast<App*>(data);
|
|
||||||
|
|
||||||
int i = 0;
|
|
||||||
|
|
||||||
auto win = std::make_shared<MainWindow>();
|
|
||||||
app->setWindow(win);
|
|
||||||
|
|
||||||
// GtkWidget *window = gtk_application_window_new (gtkApp);
|
|
||||||
// GtkWidget *label = gtk_label_new ("Hello World");
|
|
||||||
//
|
|
||||||
// gtk_window_set_title (GTK_WINDOW (window), "Hello");
|
|
||||||
// gtk_window_set_default_size (GTK_WINDOW (window), 200, 200);
|
|
||||||
// gtk_window_set_child (GTK_WINDOW (window), label);
|
|
||||||
// gtk_window_present (GTK_WINDOW (window));
|
|
||||||
}
|
|
||||||
|
|
||||||
App::App(): _app(nullptr), _window(nullptr) {
|
|
||||||
_app = adw_application_new("pro.aliencat.aliencat", G_APPLICATION_FLAGS_NONE);
|
|
||||||
g_signal_connect_data(_app, "activate", G_CALLBACK(activateApp), this, nullptr, G_CONNECT_AFTER);
|
|
||||||
}
|
|
||||||
|
|
||||||
App::~App() {
|
|
||||||
glib_autoptr_cleanup_AdwApplication(&_app);
|
|
||||||
}
|
|
||||||
|
|
||||||
int App::run(int argc, char **argv) {
|
|
||||||
return g_application_run(G_APPLICATION(_app), argc, argv);
|
|
||||||
}
|
|
||||||
|
|
||||||
void App::setWindow(std::shared_ptr<Gtk::Window> window) {
|
|
||||||
gtk_application_add_window(GTK_APPLICATION(_app), window->gobj());
|
|
||||||
|
|
||||||
if(_window) {
|
|
||||||
_window->hide();
|
|
||||||
gtk_application_remove_window(GTK_APPLICATION(_app), _window->gobj());
|
|
||||||
}
|
|
||||||
|
|
||||||
window->show();
|
|
||||||
_window = window;
|
|
||||||
}
|
|
||||||
25
app/App.h
25
app/App.h
@ -1,25 +0,0 @@
|
|||||||
//
|
|
||||||
// Created by selim on 02.02.2022.
|
|
||||||
//
|
|
||||||
|
|
||||||
#ifndef AUTOCAT_GNOME_APP_H
|
|
||||||
#define AUTOCAT_GNOME_APP_H
|
|
||||||
|
|
||||||
#include <gtkmm/window.h>
|
|
||||||
#include <adwaita.h>
|
|
||||||
#include <memory>
|
|
||||||
|
|
||||||
class App {
|
|
||||||
private:
|
|
||||||
AdwApplication* _app;
|
|
||||||
std::shared_ptr<Gtk::Window> _window;
|
|
||||||
|
|
||||||
public:
|
|
||||||
explicit App();
|
|
||||||
~App();
|
|
||||||
int run(int argc, char** argv);
|
|
||||||
void setWindow(std::shared_ptr<Gtk::Window> window);
|
|
||||||
};
|
|
||||||
|
|
||||||
|
|
||||||
#endif //AUTOCAT_GNOME_APP_H
|
|
||||||
36
coro/Coro.h
Normal file
36
coro/Coro.h
Normal file
@ -0,0 +1,36 @@
|
|||||||
|
//
|
||||||
|
// Created by selim on 20.04.2022.
|
||||||
|
//
|
||||||
|
|
||||||
|
#ifndef AUTOCAT_GNOME_CORO_H
|
||||||
|
#define AUTOCAT_GNOME_CORO_H
|
||||||
|
|
||||||
|
#include <coroutine>
|
||||||
|
#include <exception>
|
||||||
|
#include <iostream>
|
||||||
|
|
||||||
|
template<typename... Args>
|
||||||
|
struct std::coroutine_traits<void, Args...> {
|
||||||
|
struct promise_type {
|
||||||
|
void get_return_object() noexcept {}
|
||||||
|
|
||||||
|
std::suspend_never initial_suspend() const noexcept {
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
std::suspend_never final_suspend() const noexcept {
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
|
||||||
|
void return_void() noexcept {}
|
||||||
|
|
||||||
|
void unhandled_exception() noexcept {
|
||||||
|
try {
|
||||||
|
std::rethrow_exception(std::current_exception());
|
||||||
|
} catch (std::exception& ex) {
|
||||||
|
std::cout << "Unhandled exception (in void coroutine) detected: " << ex.what() << std::endl;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif //AUTOCAT_GNOME_CORO_H
|
||||||
147
coro/Task.h
147
coro/Task.h
@ -1,147 +0,0 @@
|
|||||||
//
|
|
||||||
// Created by selim on 03.01.2022.
|
|
||||||
//
|
|
||||||
|
|
||||||
#ifndef AUTOCAT_GNOME_TASK_H
|
|
||||||
#define AUTOCAT_GNOME_TASK_H
|
|
||||||
|
|
||||||
#include <coroutine>
|
|
||||||
#include <iostream>
|
|
||||||
#include <optional>
|
|
||||||
#include <exception>
|
|
||||||
|
|
||||||
#include <boost/thread/future.hpp>
|
|
||||||
#include <boost/exception/errinfo_nested_exception.hpp>
|
|
||||||
#include <utility>
|
|
||||||
#include <glibmm.h>
|
|
||||||
|
|
||||||
class FutureException: public virtual std::exception, public virtual boost::exception {
|
|
||||||
private:
|
|
||||||
std::string _message;
|
|
||||||
|
|
||||||
public:
|
|
||||||
explicit FutureException(const char* msg): _message(msg) {}
|
|
||||||
explicit FutureException(std::string msg): _message(std::move(msg)) {}
|
|
||||||
|
|
||||||
const char* what() const noexcept {
|
|
||||||
return _message.c_str();
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
// Enable the use of std::future<T> as a coroutine type
|
|
||||||
// by using a std::promise<T> as the promise type.
|
|
||||||
template <typename T, typename... Args>
|
|
||||||
requires(!std::is_void_v<T> && !std::is_reference_v<T>)
|
|
||||||
struct std::coroutine_traits<boost::future<T>, Args...> {
|
|
||||||
struct promise_type : boost::promise<T> {
|
|
||||||
boost::future<T> get_return_object() noexcept {
|
|
||||||
return this->get_future();
|
|
||||||
}
|
|
||||||
|
|
||||||
std::suspend_never initial_suspend() const noexcept {
|
|
||||||
return {};
|
|
||||||
}
|
|
||||||
std::suspend_never final_suspend() const noexcept {
|
|
||||||
return {};
|
|
||||||
}
|
|
||||||
|
|
||||||
void return_value(const T &value)
|
|
||||||
noexcept(std::is_nothrow_copy_constructible_v<T>) {
|
|
||||||
this->set_value(value);
|
|
||||||
}
|
|
||||||
void return_value(T &&value)
|
|
||||||
noexcept(std::is_nothrow_move_constructible_v<T>) {
|
|
||||||
this->set_value(std::move(value));
|
|
||||||
}
|
|
||||||
void unhandled_exception() noexcept {
|
|
||||||
this->set_exception(boost::current_exception());
|
|
||||||
}
|
|
||||||
};
|
|
||||||
};
|
|
||||||
|
|
||||||
// Same for std::future<void>.
|
|
||||||
template <typename... Args>
|
|
||||||
struct std::coroutine_traits<boost::future<void>, Args...> {
|
|
||||||
struct promise_type : boost::promise<void> {
|
|
||||||
boost::future<void> get_return_object() noexcept {
|
|
||||||
return this->get_future();
|
|
||||||
}
|
|
||||||
|
|
||||||
std::suspend_never initial_suspend() const noexcept {
|
|
||||||
return {};
|
|
||||||
}
|
|
||||||
std::suspend_never final_suspend() const noexcept {
|
|
||||||
return {};
|
|
||||||
}
|
|
||||||
|
|
||||||
void return_void() noexcept {
|
|
||||||
this->set_value();
|
|
||||||
}
|
|
||||||
void unhandled_exception() noexcept {
|
|
||||||
this->set_exception(boost::current_exception());
|
|
||||||
}
|
|
||||||
};
|
|
||||||
};
|
|
||||||
|
|
||||||
template<typename... Args>
|
|
||||||
struct std::coroutine_traits<void, Args...> {
|
|
||||||
struct promise_type {
|
|
||||||
void get_return_object() noexcept {}
|
|
||||||
|
|
||||||
std::suspend_never initial_suspend() const noexcept {
|
|
||||||
return {};
|
|
||||||
}
|
|
||||||
std::suspend_never final_suspend() const noexcept {
|
|
||||||
return {};
|
|
||||||
}
|
|
||||||
|
|
||||||
void return_void() noexcept {}
|
|
||||||
|
|
||||||
void unhandled_exception() noexcept {
|
|
||||||
try {
|
|
||||||
boost::rethrow_exception(boost::current_exception());
|
|
||||||
} catch (std::exception& ex) {
|
|
||||||
std::cout << "Unhandled exception (in void coroutine) detected: " << ex.what() << std::endl;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
};
|
|
||||||
};
|
|
||||||
|
|
||||||
template <typename T>
|
|
||||||
auto operator co_await(boost::future<T> future) noexcept requires(!std::is_reference_v<T>) {
|
|
||||||
|
|
||||||
struct awaiter : public boost::future<T> {
|
|
||||||
|
|
||||||
bool await_ready() const noexcept {
|
|
||||||
return this->is_ready();
|
|
||||||
}
|
|
||||||
void await_suspend(std::coroutine_handle<> cont) {
|
|
||||||
this->then([this, cont](boost::future<T>&& fut){
|
|
||||||
|
|
||||||
// future was moved out of awaiter to this lambda
|
|
||||||
// so, we need to bring it back
|
|
||||||
this->swap(fut);
|
|
||||||
|
|
||||||
// Resume coroutine on the main thread (i.e. GMainContext)
|
|
||||||
g_idle_add_full(G_PRIORITY_DEFAULT_IDLE, awaiter::resumeContinuation, (gpointer)&cont, nullptr);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
T await_resume() {
|
|
||||||
return this->get();
|
|
||||||
}
|
|
||||||
|
|
||||||
static int resumeContinuation(void* data) {
|
|
||||||
auto handler = reinterpret_cast<std::coroutine_handle<>*>(data);
|
|
||||||
if(*handler) {
|
|
||||||
handler->resume();
|
|
||||||
}
|
|
||||||
|
|
||||||
return G_SOURCE_REMOVE;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
return awaiter{std::move(future)};
|
|
||||||
}
|
|
||||||
|
|
||||||
#endif //AUTOCAT_GNOME_TASK_H
|
|
||||||
@ -9,6 +9,8 @@
|
|||||||
#include <gtkmm/application.h>
|
#include <gtkmm/application.h>
|
||||||
#include <gtkmm/box.h>
|
#include <gtkmm/box.h>
|
||||||
#include <iostream>
|
#include <iostream>
|
||||||
|
#include <folly/experimental/coro/Task.h>
|
||||||
|
#include <folly/executors/IOThreadPoolExecutor.h>
|
||||||
|
|
||||||
LoginWindow::LoginWindow() {
|
LoginWindow::LoginWindow() {
|
||||||
set_title("Login");
|
set_title("Login");
|
||||||
@ -48,7 +50,7 @@ void LoginWindow::loginClicked() {
|
|||||||
_spinner.start();
|
_spinner.start();
|
||||||
|
|
||||||
try {
|
try {
|
||||||
auto user = co_await Api::login(email, password);
|
User user = co_await Api::login(email, password).scheduleOn(folly::getGlobalIOExecutor());
|
||||||
auto app = this->get_application();
|
auto app = this->get_application();
|
||||||
auto mainWindow = new MainWindow();
|
auto mainWindow = new MainWindow();
|
||||||
mainWindow->show();
|
mainWindow->show();
|
||||||
|
|||||||
1
main.cpp
1
main.cpp
@ -1,7 +1,6 @@
|
|||||||
#include "gui/MainWindow.h"
|
#include "gui/MainWindow.h"
|
||||||
#include "gui/LoginWindow.h"
|
#include "gui/LoginWindow.h"
|
||||||
#include "services/Settings.h"
|
#include "services/Settings.h"
|
||||||
#include "app/App.h"
|
|
||||||
#include <gtkmm/application.h>
|
#include <gtkmm/application.h>
|
||||||
#include <glibmm.h>
|
#include <glibmm.h>
|
||||||
#include <gtkmm.h>
|
#include <gtkmm.h>
|
||||||
|
|||||||
@ -5,6 +5,7 @@
|
|||||||
#include "Api.h"
|
#include "Api.h"
|
||||||
#include "Settings.h"
|
#include "Settings.h"
|
||||||
#include <memory>
|
#include <memory>
|
||||||
|
#include <folly/futures/Promise.h>
|
||||||
|
|
||||||
template<class...Args>
|
template<class...Args>
|
||||||
struct Callback {
|
struct Callback {
|
||||||
@ -30,37 +31,41 @@ const std::string Api::_baseUrl = "https://vps.aliencat.pro:8443/";
|
|||||||
SoupSession* Api::_session = soup_session_new();
|
SoupSession* Api::_session = soup_session_new();
|
||||||
|
|
||||||
template<typename T>
|
template<typename T>
|
||||||
boost::future<T> Api::post(const std::string &method, const nlohmann::json& params) {
|
folly::Future<T> Api::post(const std::string &method, const nlohmann::json& params) {
|
||||||
std::string url = _baseUrl + method;
|
std::string url = _baseUrl + method;
|
||||||
auto msg = soup_message_new(SOUP_METHOD_POST, url.c_str());
|
auto msg = soup_message_new(SOUP_METHOD_POST, url.c_str());
|
||||||
|
|
||||||
auto promise = std::make_shared<boost::promise<T>>();
|
auto promise = std::make_shared<folly::Promise<T>>();
|
||||||
auto callback = voidify<SoupSession*, SoupMessage*>([&, promise](SoupSession* session, SoupMessage* message) {
|
auto callback = voidify<SoupSession*, SoupMessage*>([&, promise](SoupSession* session, SoupMessage* message) {
|
||||||
if(message->status_code >= 200 && message->status_code < 300) {
|
if(message->status_code >= 200 && message->status_code < 300) {
|
||||||
auto responseString = std::string(message->response_body->data, message->response_body->length);
|
auto responseString = std::string(message->response_body->data, message->response_body->length);
|
||||||
auto json = nlohmann::json::parse(responseString);
|
auto json = nlohmann::json::parse(responseString);
|
||||||
if(json["success"].get<bool>()) {
|
if(json["success"].get<bool>()) {
|
||||||
std::cout << "response: " << responseString << std::endl;
|
//std::cout << "response: " << responseString << std::endl;
|
||||||
auto user = json["data"].get<T>();
|
auto user = json["data"].get<T>();
|
||||||
promise->set_value(user);
|
promise->setValue(user);
|
||||||
} else {
|
} else {
|
||||||
auto error = json["error"].get<std::string>();
|
auto error = json["error"].get<std::string>();
|
||||||
promise->set_exception(FutureException(error));
|
promise->setException(std::runtime_error(error));
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
promise->set_exception(FutureException(message->reason_phrase));
|
promise->setException(std::runtime_error(message->reason_phrase));
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
auto jsonStr = params.dump();
|
auto jsonStr = params.dump();
|
||||||
|
|
||||||
soup_message_set_request(msg, "application/json", SOUP_MEMORY_COPY, jsonStr.c_str(), jsonStr.size());
|
soup_message_set_request(msg, "application/json",
|
||||||
|
SOUP_MEMORY_COPY,
|
||||||
|
jsonStr.c_str(),
|
||||||
|
jsonStr.size());
|
||||||
|
|
||||||
soup_session_queue_message(_session, msg, callback.function, callback.state);
|
soup_session_queue_message(_session, msg, callback.function, callback.state);
|
||||||
|
|
||||||
return promise->get_future();
|
return promise->getFuture();
|
||||||
}
|
}
|
||||||
|
|
||||||
boost::future<User> Api::login(std::string email, std::string password) {
|
fc::Task<User> Api::login(std::string email, std::string password) {
|
||||||
|
|
||||||
nlohmann::json params = {
|
nlohmann::json params = {
|
||||||
{ "email", email },
|
{ "email", email },
|
||||||
|
|||||||
@ -8,9 +8,13 @@
|
|||||||
#include <string>
|
#include <string>
|
||||||
#include <libsoup/soup.h>
|
#include <libsoup/soup.h>
|
||||||
#include <nlohmann/json.hpp>
|
#include <nlohmann/json.hpp>
|
||||||
|
#include <folly/futures/Future.h>
|
||||||
|
#include <folly/experimental/coro/Task.h>
|
||||||
|
|
||||||
#include "../models/User.h"
|
#include "../models/User.h"
|
||||||
#include "../coro/Task.h"
|
#include "../coro/Coro.h"
|
||||||
|
|
||||||
|
namespace fc = folly::coro;
|
||||||
|
|
||||||
class Api {
|
class Api {
|
||||||
private:
|
private:
|
||||||
@ -19,10 +23,10 @@ private:
|
|||||||
|
|
||||||
private:
|
private:
|
||||||
template<typename T>
|
template<typename T>
|
||||||
static boost::future<T> post(const std::string& method, const nlohmann::json& params);
|
static folly::Future<T> post(const std::string& method, const nlohmann::json& params);
|
||||||
|
|
||||||
public:
|
public:
|
||||||
static boost::future<User> login(std::string email, std::string password);
|
static fc::Task<User> login(std::string email, std::string password);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user