Skip to content

Commit

Permalink
refactor: dpp::awaitable now lazily starts on co_await, dpp::async no…
Browse files Browse the repository at this point in the history
…w for parallel awaitable requests
  • Loading branch information
Mishura4 committed Aug 10, 2023
1 parent 282c0e0 commit 4374c9c
Show file tree
Hide file tree
Showing 11 changed files with 392 additions and 260 deletions.
8 changes: 5 additions & 3 deletions buildtools/classes/Generator/CoroGenerator.php
Original file line number Diff line number Diff line change
Expand Up @@ -98,7 +98,9 @@ public function generateHeaderDef(string $returnType, string $currentFunction, s
*/
public function generateCppDef(string $returnType, string $currentFunction, string $parameters, string $noDefaults, string $parameterTypes, string $parameterNames): string
{
return "awaitable<confirmation_callback_t> cluster::co_${currentFunction}($noDefaults) {\n\treturn {this, static_cast<void (cluster::*)($parameterTypes". (!empty($parameterTypes) ? ", " : "") . "command_completion_event_t)>(&cluster::$currentFunction)$parameterNames};\n}\n\n";
if (substr($parameterNames, 0, 2) === ", ")
$parameterNames = substr($parameterNames, 2);
return "awaitable<confirmation_callback_t> cluster::co_${currentFunction}($noDefaults) {\n\treturn [=, this] (auto &&cc) { this->$currentFunction($parameterNames" . (empty($parameterNames) ? "": ", ") . "cc); };\n}\n\n";
}

/**
Expand All @@ -114,7 +116,7 @@ public function getCommentArray(): array
*/
public function saveHeader(string $content): void
{
$content .= "awaitable<http_request_completion_t> co_request(const std::string &url, http_method method, const std::string &postdata = \"\", const std::string &mimetype = \"text/plain\", const std::multimap<std::string, std::string> &headers = {});\n\n";
$content .= "awaitable<http_request_completion_t> co_request(const std::string &url, http_method method, const std::string &postdata = \"\", const std::string &mimetype = \"text/plain\", std::multimap<std::string, std::string> headers = {});\n\n";
file_put_contents('include/dpp/cluster_coro_calls.h', $content);
}

Expand All @@ -123,7 +125,7 @@ public function saveHeader(string $content): void
*/
public function saveCpp(string $cppcontent): void
{
$cppcontent .= "dpp::awaitable<dpp::http_request_completion_t> dpp::cluster::co_request(const std::string &url, http_method method, const std::string &postdata, const std::string &mimetype, const std::multimap<std::string, std::string> &headers) {\n\treturn awaitable<http_request_completion_t>{[&](auto &&cc) { this->request(url, method, cc, postdata, mimetype, headers); }};\n}
$cppcontent .= "dpp::awaitable<dpp::http_request_completion_t> dpp::cluster::co_request(const std::string &url, http_method method, const std::string &postdata, const std::string &mimetype, std::multimap<std::string, std::string> headers) {\n\treturn awaitable<http_request_completion_t>{[=, this, h = std::move(headers)](auto &&cc) { this->request(url, method, cc, postdata, mimetype, h); }};\n}
#endif
";
Expand Down
10 changes: 5 additions & 5 deletions docpages/advanced_reference/coroutines.md
Original file line number Diff line number Diff line change
Expand Up @@ -47,11 +47,11 @@ int main() {

Coroutines can make commands simpler by eliminating callbacks, which can be very handy in the case of complex commands that rely on a lot of different data or steps.

In order to be a coroutine, a function has to return a special type with special functions; D++ offers `dpp::task` which is designed to work seamlessly with asynchronous calls through `dpp::awaitable`, which all the functions starting with `co_` such as `dpp::cluster::co_message_create` return. To turn a function into a coroutine, simply make it return `dpp::task<void>` as seen in the example at line 10.
In order to be a coroutine, a function has to return a special type with special functions; D++ offers `dpp::task` which is designed to work seamlessly with asynchronous calls through `dpp::awaitable`, which all the functions starting with `co_` such as `dpp::cluster::co_message_create` return. To turn a function into a coroutine, simply make it return `dpp::task<void>` as seen in the example at line 10. Inside of a `dpp::task`, someone can use `co_return` in place of `return` to return a value.

When an awaitable is `co_await`-ed, the coroutine suspends (pauses) and returns back to its caller : in other words, the program is free to go and do other things while the data is being retrieved, D++ will resume your coroutine when it has the data you need which will be returned from the `co_await` expression.
When an awaitable is `co_await`-ed, the request is sent, the coroutine suspends (pauses) and returns back to its caller : in other words, the program is free to go and do other things while the data is being retrieved, D++ will resume your coroutine when it has the data you need which will be returned from the `co_await` expression.

Inside of a `dpp::task`, someone can use `co_return` in place of `return`.
Awaitable objects can be wrapped with `dpp::async` : this will send the call immediately but not suspend the coroutine, allowing to execute several requests in parallel. The async object can then be co_awaited later when it is depended on.

\attention As a rule of thumb when making dpp::task objects and in general coroutines, always prefer taking parameters by value and avoid capture : this may be confusing but a coroutine is *not* the lambda creating it, the captures are not bound to it and the code isn't ran inside the lambda. The lambda that returns a dpp::task simply returns a task object containing the code, which goes on to live on its own, separate from the lambda.
Similarly, with reference parameters, the object they reference to might be destroyed while the coroutine is suspended and resumed in another thread, which is why you want to pass by value. See also [lambdas and locals](/lambdas-and-locals.html) except this also applies to parameters in the case of coroutines.
Expand Down Expand Up @@ -87,7 +87,7 @@ int main() {
co_return;
}
// Send a "<bot> is thinking..." message, to wait on later so we can edit
dpp::awaitable thinking = event.co_thinking(false);
dpp::async thinking = event.co_thinking(false);
// Download and co_await the result
dpp::http_request_completion_t response = co_await cluster->co_request(attachment.url, dpp::m_get);
Expand Down Expand Up @@ -180,7 +180,7 @@ int main() {
};
// Send a "<bot> is thinking..." message, to wait on later so we can edit
dpp::awaitable thinking = event.co_thinking(false);
dpp::async thinking = event.co_thinking(false);
// Call our coroutine defined above to retrieve the member requested
std::optional<dpp::guild_member> member = co_await resolve_member(event);
Expand Down
14 changes: 7 additions & 7 deletions include/dpp/cluster.h
Original file line number Diff line number Diff line change
Expand Up @@ -360,12 +360,12 @@ class DPP_EXPORT cluster {

#ifdef DPP_CORO
/**
* @brief Start a one-time timer. Use the co_await keyword on its return value to suspend the coroutine until the timer ends
*
* @param seconds How long to run the timer for
* @return awaitable<timer> co_await-able object holding the timer_handle
* @brief Get an awaitable to wait a certain amount of seconds. Use the co_await keyword on its return value to suspend the coroutine until the timer ends
*
* @param seconds How long to wait for
* @return awaitable<timer> Object that can be co_await-ed to suspend the function for a certain time
*/
awaitable<timer> co_timer(uint64_t seconds);
awaitable<timer> co_sleep(uint64_t seconds);
#endif

/**
Expand Down Expand Up @@ -3251,7 +3251,7 @@ class DPP_EXPORT cluster {
* @param callback Function to call when the API call completes.
* On success the callback will contain a dpp::sticker object in confirmation_callback_t::value. On failure, the value is undefined and confirmation_callback_t::is_error() method will return true. You can obtain full error details with confirmation_callback_t::get_error().
*/
void guild_sticker_create(sticker &s, command_completion_event_t callback = utility::log_error());
void guild_sticker_create(const sticker &s, command_completion_event_t callback = utility::log_error());

/**
* @brief Modify a sticker in a guild
Expand All @@ -3261,7 +3261,7 @@ class DPP_EXPORT cluster {
* @param callback Function to call when the API call completes.
* On success the callback will contain a dpp::sticker object in confirmation_callback_t::value. On failure, the value is undefined and confirmation_callback_t::is_error() method will return true. You can obtain full error details with confirmation_callback_t::get_error().
*/
void guild_sticker_modify(sticker &s, command_completion_event_t callback = utility::log_error());
void guild_sticker_modify(const sticker &s, command_completion_event_t callback = utility::log_error());

/**
* @brief Delete a sticker from a guild
Expand Down
6 changes: 3 additions & 3 deletions include/dpp/cluster_coro_calls.h
Original file line number Diff line number Diff line change
Expand Up @@ -1742,7 +1742,7 @@ awaitable<confirmation_callback_t> co_stage_instance_delete(const snowflake chan
* @return sticker returned object on completion
* \memberof dpp::cluster
*/
awaitable<confirmation_callback_t> co_guild_sticker_create(sticker &s);
awaitable<confirmation_callback_t> co_guild_sticker_create(const sticker &s);

/**
* @brief Delete a sticker from a guild
Expand Down Expand Up @@ -1776,7 +1776,7 @@ awaitable<confirmation_callback_t> co_guild_sticker_get(snowflake id, snowflake
* @return sticker returned object on completion
* \memberof dpp::cluster
*/
awaitable<confirmation_callback_t> co_guild_sticker_modify(sticker &s);
awaitable<confirmation_callback_t> co_guild_sticker_modify(const sticker &s);

/**
* @brief Get all guild stickers
Expand Down Expand Up @@ -2390,5 +2390,5 @@ awaitable<confirmation_callback_t> co_get_webhook_with_token(snowflake webhook_i


/* End of auto-generated definitions */
awaitable<http_request_completion_t> co_request(const std::string &url, http_method method, const std::string &postdata = "", const std::string &mimetype = "text/plain", const std::multimap<std::string, std::string> &headers = {});
awaitable<http_request_completion_t> co_request(const std::string &url, http_method method, const std::string &postdata = "", const std::string &mimetype = "text/plain", std::multimap<std::string, std::string> headers = {});

4 changes: 2 additions & 2 deletions include/dpp/cluster_sync_calls.h
Original file line number Diff line number Diff line change
Expand Up @@ -2135,7 +2135,7 @@ confirmation stage_instance_delete_sync(const snowflake channel_id);
* @warning This function is a blocking (synchronous) call and should only be used from within a separate thread.
* Avoid direct use of this function inside an event handler.
*/
sticker guild_sticker_create_sync(sticker &s);
sticker guild_sticker_create_sync(const sticker &s);

/**
* @brief Delete a sticker from a guild
Expand Down Expand Up @@ -2178,7 +2178,7 @@ sticker guild_sticker_get_sync(snowflake id, snowflake guild_id);
* @warning This function is a blocking (synchronous) call and should only be used from within a separate thread.
* Avoid direct use of this function inside an event handler.
*/
sticker guild_sticker_modify_sync(sticker &s);
sticker guild_sticker_modify_sync(const sticker &s);

/**
* @brief Get all guild stickers
Expand Down
Loading

0 comments on commit 4374c9c

Please sign in to comment.