How to check a valid boost asio CompletionToken? - c++

I'm writing Boost.Asio style async function that takes CompletionToken argument.
The argument can be function, function object, lambda expression, future, awaitable, etc.
CompletionToken is template parameter. If I don't restrict the argument, the function could match unexpected parameter such as int. So I want to write some restriction using std::enable_if. (My environment is C++17).
If CompletionToken takes parameters, then I can use std::is_invocable for checking.
See f1 in my example code.
However, I got a problem. If CompletionToken takes no parameters, then boost::asio::use_future makes an error.
See f2 in my example code.
After some of try and error, I got my solution. That is concatenating std::is_invocable and use_future_t checking by OR (||).
See f3 in my example code.
But it is not so elegant. In addition, I'm not sure other features supported by Boost.Asio e.g.) use_awaitable_t requires similar direct matching check.
I tried to find Boost.Asio provides type traits or predicate such as is_completion_token, but I couldn't find it.
Is there any better way to checking CompletionToken?
Godbolt link https://godbolt.org/z/sPeMo1GEK
Complete Code:
#include <type_traits>
#include <boost/asio.hpp>
// Callable T takes one argument
template <
typename T,
std::enable_if_t<std::is_invocable_v<T, int>>* = nullptr
>
void f1(T) {
}
// Callable T takes no argument
template <
typename T,
std::enable_if_t<std::is_invocable_v<T>>* = nullptr
>
void f2(T) {
}
template <template <typename...> typename, typename>
struct is_instance_of : std::false_type {};
template <template <typename...> typename T, typename U>
struct is_instance_of<T, T<U>> : std::true_type {};
// Callable T takes no argument
template <
typename T,
std::enable_if_t<
std::is_invocable_v<T> ||
is_instance_of<boost::asio::use_future_t, T>::value
>* = nullptr
>
void f3(T) {
}
int main() {
// no error
f1([](int){});
f1(boost::asio::use_future);
// same rule as f1 but use_future got compile error
f2([](){});
f2(boost::asio::use_future); // error
// a little complecated typechecking, then no error
f3([](){});
f3(boost::asio::use_future);
}
Outputs:
Output of x86-64 clang 13.0.1 (Compiler #1)
<source>:45:5: error: no matching function for call to 'f2'
f2(boost::asio::use_future); // error
^~
<source>:17:6: note: candidate template ignored: requirement 'std::is_invocable_v<boost::asio::use_future_t<std::allocator<void>>>' was not satisfied [with T = boost::asio::use_future_t<>]
void f2(T) {
^
1 error generated.

If you have c++20 concepts, look below. Otherwise, read on.
When you want to correctly implement the async-result protocol using Asio, you would use the async_result trait, or the async_initiate as documented here.
This should be a reliable key for SFINAE. The template arguments to async_result include the token and the completion signature(s):
Live On Compiler Explorer
#include <boost/asio.hpp>
#include <iostream>
using boost::asio::async_result;
template <typename Token,
typename R = typename async_result<std::decay_t<Token>, void(int)>::return_type>
void f1(Token&&) {
std::cout << __PRETTY_FUNCTION__ << "\n";
}
template <typename Token,
typename R = typename async_result<std::decay_t<Token>, void()>::return_type>
void f2(Token&&) {
std::cout << __PRETTY_FUNCTION__ << "\n";
}
int main() {
auto cb1 = [](int) {};
f1(cb1);
f1(boost::asio::use_future);
f1(boost::asio::use_awaitable);
f1(boost::asio::detached);
f1(boost::asio::as_tuple(boost::asio::use_awaitable));
auto cb2 = []() {};
f2(cb2);
f2(boost::asio::use_future);
f2(boost::asio::use_awaitable);
f2(boost::asio::detached);
f2(boost::asio::as_tuple(boost::asio::use_awaitable));
}
Already prints
void f1(Token&&) [with Token = main()::<lambda(int)>&; R = void]
void f1(Token&&) [with Token = const boost::asio::use_future_t<>&; R = std::future<int>]
void f1(Token&&) [with Token = const boost::asio::use_awaitable_t<>&; R = boost::asio::awaitable<int, boost::asio::any_io_executor>]
void f1(Token&&) [with Token = const boost::asio::detached_t&; R = void]
void f1(Token&&) [with Token = boost::asio::as_tuple_t<boost::asio::use_awaitable_t<> >; R = boost::asio::awaitable<std::tuple<int>, boost::asio::any_io_executor>]
void f2(Token&&) [with Token = main()::<lambda()>&; R = void]
void f2(Token&&) [with Token = const boost::asio::use_future_t<>&; R = std::future<void>]
void f2(Token&&) [with Token = const boost::asio::use_awaitable_t<>&; R = boost::asio::awaitable<void, boost::asio::any_io_executor>]
void f2(Token&&) [with Token = const boost::asio::detached_t&; R = void]
void f2(Token&&) [with Token = boost::asio::as_tuple_t<boost::asio::use_awaitable_t<> >; R = boost::asio::awaitable<std::tuple<>, boost::asio::any_io_executor>]
C++20 Concepts
Now keep in mind the above is "too lax" due to partial template instantiation. Some parts of async_result aren't actually used. That means that f2(cb1); will actually compile.
The linked docs even include the C++20 completion_token_for<Sig> concept that allows you to be precise at no effort: Live On Compiler Explorer
template <boost::asio::completion_token_for<void(int)> Token> void f1(Token&&) {
std::cout << __PRETTY_FUNCTION__ << "\n";
}
template <boost::asio::completion_token_for<void()> Token> void f2(Token&&) {
std::cout << __PRETTY_FUNCTION__ << "\n";
}
Otherwise
In practice you would always follow the Asio recipe, and that guarantees that all parts are used. Apart from the example in the documentation, you can search existing answers
Example:
template <typename Token>
typename asio::async_result<std::decay_t<Token>, void(error_code, int)>::return_type
async_f1(Token&& token) {
auto init = [](auto completion) {
auto timer =
std::make_shared<asio::steady_timer>(boost::asio::system_executor{}, 1s);
std::thread(
[timer](auto completion) {
error_code ec;
timer->wait(ec);
std::move(completion)(ec, 42);
},
std::move(completion))
.detach();
};
return asio::async_result<std::decay_t<Token>, void(error_code, int)>::initiate(
init, std::forward<Token>(token));
}

Related

Incomplete type `std::variant<...>` used in nested name specifier

I wrote the following code into a file named main.cpp.
It involves the curiously recurring template pattern (CRTP) with the standard type std::variant.
#include <string>
#include <variant>
#include <vector>
template<typename T>
struct either {
std::vector<T> arg;
};
template<typename T>
struct maybe_either: std::variant<T, either<maybe_either<T>>> {
template<typename U>
maybe_either(U&& v):
std::variant<T, either<maybe_either<T>>>(std::forward<U>(v)) {
}
};
struct var {
std::string name;
};
int main(int, char**) {
auto expression = maybe_either<var>(either<maybe_either<var>>{});
std::visit([&](auto&& v) {
using T = std::decay_t<decltype (v)>;
if constexpr (std::is_same_v<T, var>) {
// ...
} else if constexpr (std::is_same_v<T, either<maybe_either<var>>>) {
// ...
}
}, expression);
return 0;
}
When compiling it with the following command line, I get the error message below:
$ g++ -c -std=c++17 main.cpp
In file included from main.cpp:2:0:
/usr/include/c++/7/variant: In instantiation of ‘constexpr const size_t std::variant_size_v<maybe_either<var> >’:
/usr/include/c++/7/variant:702:10: required from ‘struct std::__detail::__variant::__gen_vtable<void, main(int, char**)::<lambda(auto:1&&)>&&, maybe_either<var>&>’
/usr/include/c++/7/variant:1255:23: required from ‘constexpr decltype(auto) std::visit(_Visitor&&, _Variants&& ...) [with _Visitor = main(int, char**)::<lambda(auto:1&&)>; _Variants = {maybe_either<var>&}]’
main.cpp:32:18: required from here
/usr/include/c++/7/variant:97:29: error: incomplete type ‘std::variant_size<maybe_either<var> >’ used in nested name specifier
inline constexpr size_t variant_size_v = variant_size<_Variant>::value;
^~~~~~~~~~~~~~
/usr/include/c++/7/variant: In instantiation of ‘constexpr const auto std::__detail::__variant::__gen_vtable<void, main(int, char**)::<lambda(auto:1&&)>&&, maybe_either<var>&>::_S_vtable’:
/usr/include/c++/7/variant:711:29: required from ‘struct std::__detail::__variant::__gen_vtable<void, main(int, char**)::<lambda(auto:1&&)>&&, maybe_either<var>&>’
/usr/include/c++/7/variant:1255:23: required from ‘constexpr decltype(auto) std::visit(_Visitor&&, _Variants&& ...) [with _Visitor = main(int, char**)::<lambda(auto:1&&)>; _Variants = {maybe_either<var>&}]’
main.cpp:32:18: required from here
/usr/include/c++/7/variant:711:49: error: ‘_S_apply’ was not declared in this scope
static constexpr auto _S_vtable = _S_apply();
~~~~~~~~^~
My class maybe_either derived from std::variant<...> can be used normally in other contexts, but when I call std::visit(...) on it, it fails to compile. What is wrong?
This is basically LWG3052 which I'm trying to address in P2162.
maybe_either<T> isn't a specialization of std::variant - it inherits from one. And std::visit is currently underspecified. It's not at all clear what kinds of "variants" are allowed to be visited.
libstdc++ implements the original suggested direction in that library issue, which is only specializations of std::variant (of which you are not). libc++, on the other hand, allows types that inherit from std::variant to be visited, so it accepts your example.
The intent is that the example as-is will become well-formed eventually. But until then, you'll have to ensure that the visit you do is directly on a std::variant. You can do so by adding your own member or non-member visit that does this cast for you, so the callers don't have to do it themselves.
For example, this:
template<typename T>
struct maybe_either: std::variant<T, either<maybe_either<T>>> {
using base = typename maybe_either::variant;
template<typename U>
maybe_either(U&& v):
std::variant<T, either<maybe_either<T>>>(std::forward<U>(v)) {
}
template <typename F>
decltype(auto) visit(F&& f) & {
return std::visit(std::forward<F>(f), static_cast<base&>(*this));
}
};
allows this to work:
int main(int, char**) {
auto expression = maybe_either<var>(either<maybe_either<var>>{});
expression.visit([&](auto&& v) {
using T = std::decay_t<decltype (v)>;
if constexpr (std::is_same_v<T, var>) {
// ...
} else if constexpr (std::is_same_v<T, either<maybe_either<var>>>) {
// ...
}
});
return 0;
}

Generic Exponential Backoff Retry Mechanism C++11

I have written a generic exponential backoff retry loop in C++11. I'm using std::function to pass the callable to retry loop. callable will be retried if isRetriable function returns true.
#include <algorithm>
#include <cassert>
#include <chrono>
#include <functional>
#include <iostream>
#include <thread>
constexpr int64_t max_backoff_milliseconds = 30000; // 30 seconds
template <class R, class... Args>
R Retry(int max_retry_count, int64_t initial_dealy_milliseconds,
const std::function<bool(R)> &isRetriable,
const std::function<R(Args...)> &callable, Args &&... args)
{
int retry_count = 0;
while (true) {
auto status = callable(std::forward<Args>(args)...);
if (!IsRetriable(status)) {
return status;
}
if (retry_count >= max_retry_count) {
// Return status and abort retry
return status;
}
int64_t delay_milliseconds = 0;
if (initial_dealy_milliseconds > 0) {
delay_milliseconds =
std::min(initial_dealy_milliseconds << retry_count,
max_backoff_milliseconds);
}
std::cout << "Callable execution failed. Retry Count:"
<< retry_count + 1 << std::endl;
std::this_thread::sleep_for(
std::chrono::milliseconds(delay_milliseconds));
retry_count++;
}
}
bool isRetriable(int status) {
if (status == 5)
return true;
return false;
}
int foo(int x, int y) {
static int a = 1;
a += (x + y);
return a / 6;
}
int main() {
auto result = Retry(1000, 100, isRetriable, foo, 1, 3);
std::cout << result << std::endl;
return 0;
}
When I compile it, I'm getting below error:
prog.cpp: In function ‘int main()’:
prog.cpp:50:71: error: no matching function for call to ‘Retry(int,
int, bool (&)(int), int (&)(int, int), int, int)’
auto result = Retry<int, int, int>(1000, 100, isRetriable, foo, 1, 3);
^
prog.cpp:11:3: note: candidate: template<class R, class ... Args> R
Retry(int, int64_t, const std::function<bool(R)>&, const
std::function<_Res(_ArgTypes ...)>&, Args&& ...)
R Retry(int max_retry_count,
^~~~~
prog.cpp:11:3: note: template argument deduction/substitution failed:
prog.cpp:50:71: note: mismatched types ‘const
std::function<int(_ArgTypes ...)>’ and ‘int(int, int)’
auto result = Retry<int, int, int>(1000, 100, isRetriable, foo, 1, 3);
^
Could someone explain to me why I have this error?
I'm sure there's a good duplicate for this but...
Here's a shorter reproduction:
template <typename T> void foo(std::function<bool(T)> ) { }
bool maybe(int ) { return false; }
foo(maybe); // error: no matching function call to 'foo(bool (&)(int))'
You may ask - what?! maybe is something that's callable with some T that returns bool. But that's not how template deduction works. In order to deduce std::function<bool(T)> against an argument, that argument needs to be a std::function. maybe isn't a std::function, it's just a function, so that deduction fails. Any kind of deduction with a different kind of expression will also fail:
foo([](int ) { return true; }); // also error
Basically, trying to deduce a std::function is almost always the wrong thing to do. First, it's wrong because it won't work. Second, it's wrong because even if it did work, you're incurring type erasure in a context in which you probably don't need it.
What you want to do instead is deduce arbitrary callables, and then determine what these other arguments are based on those callables. The return type of callable is just what you get when you call callable with Args..., and you want to ensure that isRetriable is a predicate on that type.
One approach to that is:
template <typename Predicate, typename Callable, typename... Args,
// figure out what the callable returns
typename R = std::decay_t<std::invoke_result_t<Callable&, Args...>>,
// require that Predicate is actually a Predicate
std::enable_if_t<
std::is_convertible_v<std::invoke_result_t<Predicate&, R>, bool>,
int> = 0>
R Retry(int max_retry_count, int64_t initial_dealy_milliseconds,
Predicate&& isRetriable,
Callable&& callable,
Args&&... args)
{
// ....
}

Constraints not satisfied for template template concept requiring static template method

I'm trying to implement Functor and various other category-theoretic concepts using C++ concepts, but am getting compile errors:
http://coliru.stacked-crooked.com/a/e8b6eb387229bddf
Here's my full code (I know that requiring fmap<int, int> does not verify fmap for any two types, and I plan to change it to fmap<int, std::string> or something to achieve a slightly stronger test -- or instead, possibly alter the Functor concept so that it takes in addition to F, two types T and U and verifies the existence of fmap<T, U>, but that's all after I figure out how to fix the error that I'm getting):
#include <functional>
#include <iostream>
#include <vector>
// empty Functor_Impl struct - specialize for each functor
template<template<class> class F> struct Functor_Impl {};
// std::vector Functor implementation
template<>
struct Functor_Impl<std::vector> {
template<class T, class U>
static std::vector<U> fmap(std::vector<T> x, std::function<U(T)> f) {
std::vector<U> out;
out.reserve(x.size());
for (int i = 0; i < x.size(); i++) {
out.push_back(f(x[i]));
}
return out;
}
};
// Functor concept requires Functor_Impl<F> to have fmap
template<template<class> class F>
concept bool Functor = requires(F<int> x) {
{Functor_Impl<F>::template fmap<int, int>(x)} -> F<int>;
};
// Test function using constraint.
template<template<class> class F, class T>
requires Functor<F>
F<T> mult_by_2(F<T> a) {
return Functor_Impl<F>::template fmap<T, T>(a, [](T x) {
return x * 2;
});
}
int main() {
std::vector<int> x = {1, 2, 3};
std::vector<int> x2 = mult_by_2(x);
for (int i = 0; i < x2.size(); i++) {
std::cout << x2[i] << std::endl;
}
}
And the compile error:
lol#foldingmachinebox:~/p/website-editor$ g++ foo.cpp -std=c++17 -fconcepts -o foo
foo.cpp: In function ‘int main()’:
foo.cpp:39:38: error: cannot call function ‘F<T> mult_by_2(F<T>) [with F = std::vector; T = int]’
std::vector<int> x2 = mult_by_2(x);
^
foo.cpp:31:6: note: constraints not satisfied
F<T> mult_by_2(F<T> a) {
^~~~~~~~~
foo.cpp:24:14: note: within ‘template<template<class> class F> concept const bool Functor<F> [with F = std::vector]’
concept bool Functor = requires(F<int> x) {
^~~~~~~
foo.cpp:24:14: note: with ‘std::vector<int> x’
foo.cpp:24:14: note: the required expression ‘Functor_Impl<F>::fmap<int, int>(x)’ would be ill-formed
I'm guessing that my syntax for the concept itself is wrong - that it's treating a variable as a function, or vice versa, since I'm not very familiar with the concept syntax, and in addition some of the example code on cppreference.com does not compile under GCC's implementation (e.g. concept EqualityComparable does not compile, it must be changed to concept bool EqualityComparable).
If I remove requires Functor<F> from the mult_by_2 function declaration, then the code compiles and runs.
The problem is exactly what the error message says: Functor_Impl<F>::template fmap<int, int>(x) is not a valid expression. Functor_Impl<std::vector>::fmap has two parameters, not one.

Convert constexpr struct to a runtime one

I'm trying to use a template class (here Foo), with a basic type like:
hana::tuple<hana::pair<hana::type<int>, Runtime>> with Runtime a class which obiviously can't be constepxr.
But the type can be construct in several way that's why I use:
hana::tuple<hana::pair<hana::type<int>, hana::type<Runtime>>> to do the work at compile time.
So the question is basically how convert from the first tuple type to the second one. I wonder if there is something in hana that could help me. Or even better, some tips on that kind of "conversion".
namespace hana = boost::hana;
using namespace hana::literals;
struct Runtime { std::vector<int> data; };
template < typename T >
struct Foo {
T data;
};
constexpr decltype(auto) convertMap(auto storageMap) {
return hana::make_type(hana::transform(
storageMap,
[] (auto pair) {
return hana::make_pair(
hana::first(pair),
typename decltype(hana::typeid_(hana::second(pair)))::type {});
}));
}
int main() {
constexpr auto map = hana::make_tuple(
hana::make_pair(hana::type_c<int>, hana::type_c<Runtime>)
);
constexpr auto result = convertMap(map);
static_assert(result ==
hana::type_c<hana::tuple<hana::pair<hana::type<int>, Runtime>>>);
Foo<typename decltype(result)::type> test;
}
As you can see I tried some c++1z convertMap with hana::transform and lambdas, but the second tuple can't be constexpr, so I can't pass it to hana::make_type hoping to get a hana::type_c.
test.cpp: In function ‘int main()’:
test.cpp:70:41: error: ‘constexpr decltype(auto) convertMap(auto:27) [with auto:27 = boost::hana::tuple<boost::hana::pair<boost::hana::type_impl<int>::_, boost::hana::type_impl<Runtime>::_> >]’ called in a constant expression
constexpr auto result = convertMap(map);
^
test.cpp:53:27: note: ‘constexpr decltype(auto) convertMap(auto:27) [with auto:27 = boost::hana::tuple<boost::hana::pair<boost::hana::type_impl<int>::_, boost::hana::type_impl<Runtime>::_> >]’ is not usable as a constexpr function because:
constexpr decltype(auto) convertMap(auto storageMap) {
^~~~~~~~~~
test.cpp:53:27: error: temporary of non-literal type ‘boost::hana::tuple<boost::hana::pair<boost::hana::type_impl<int>::_, Runtime> >’ in a constant expression
In file included from /usr/include/boost/hana/detail/struct_macros.hpp:29:0,
from /usr/include/boost/hana/adapt_adt.hpp:15,
from lib/hana/include/boost/hana.hpp:59,
from test.cpp:1:
/usr/include/boost/hana/tuple.hpp:68:12: note: ‘boost::hana::tuple<boost::hana::pair<boost::hana::type_impl<int>::_, Runtime> >’ is not literal because:
struct tuple
^~~~~
/usr/include/boost/hana/tuple.hpp:68:12: note: ‘boost::hana::tuple<boost::hana::pair<boost::hana::type_impl<int>::_, Runtime> >’ has a non-trivial destructor
All you care about is a type - you can therefore hide the computation of the type in a non-constexpr implementation function, and afterwards call it with decltype(...){} to "force constexpr":
template <typename T>
decltype(auto) convertMapImpl(T storageMap)
{
return hana::make_type(hana::transform(storageMap, [](auto pair) {
return hana::make_pair(hana::first(pair),
typename decltype(hana::typeid_(hana::second(pair)))::type{});
}));
}
template <typename T>
constexpr decltype(auto) convertMap(T storageMap)
{
return decltype(convertMapImpl(storageMap)){};
}
live wandbox example
Also note that using auto in a function signature is a gcc extension - you should use a template parameter instead to be standard-compliant.

using std::result_of to determine the return type of a template argument

I think the snippet of code is self explanatory, but basically the template function ExecFunc should be able to execute another function and return its result. I know I can achieve similar results using decltype instead of result_of, but this question is to understand why what I've written does not work: the snippet does not compile on gcc v4.9.2.
This is what I have:
#include <type_traits>
int f(int i)
{
return i;
}
template<class F, class T>
auto ExecFunc(F f, T arg) -> typename std::result_of<F()>::type
{
return f(arg);
}
int main() {
auto a = ExecFunc(f, 3);
return 0;
}
and this is the compiler output:
prova.cpp: In function ‘int main()’:
prova.cpp:15:26: error: no matching function for call to ‘ExecFunc(int (&)(int), int)’
auto a = ExecFunc(f, 3);
^
prova.cpp:15:26: note: candidate is:
prova.cpp:9:6: note: template<class F, class T> typename std::result_of<F()>::type ExecFunc(F, T)
auto ExecFunc(F f, T arg) -> typename std::result_of<F()>::type
^
prova.cpp:9:6: note: template argument deduction/substitution failed:
prova.cpp: In substitution of ‘template<class F, class T> typename std::result_of<F()>::type ExecFunc(F, T) [with F = int (*)(int); T = int]’:
prova.cpp:15:26: required from here
prova.cpp:9:6: error: no type named ‘type’ in ‘class std::result_of<int (*())(int)>’
N.B.
this question might look like a duplicate of this one but the accepted solution doesn't work for me (at least, as far as I can tell I have incorporated the solution in my code).
The function you have is int f(int i) but you are calling F() which is unknown. std::result_of<F()>::type should be std::result_of<F(T)>::type.
Live Example
The problem is with the parameter of result_of, it should be:
-> typename std::result_of<F(T)>::type
This is the perfect time to use decltype
template<class F, class T>
auto ExecFunc(F f, T arg) -> decltype(f(arg))