Skip to content
11 changes: 9 additions & 2 deletions CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -14,11 +14,18 @@ set(TARGETS_EXPORT_NAME ${CMAKE_PROJECT_NAME})
include(FetchContent)
FetchContent_Declare(
execution
# SOURCE_DIR <path-to>/execution
# SOURCE_DIR ${CMAKE_SOURCE_DIR}/../execution
GIT_REPOSITORY https://github.com/bemanproject/execution
GIT_TAG fa6d441
GIT_TAG e43fc56
)
FetchContent_MakeAvailable(execution)
FetchContent_Declare(
task
# SOURCE_DIR ${CMAKE_SOURCE_DIR}/../task
GIT_REPOSITORY https://github.com/bemanproject/task
GIT_TAG 0495d7f
)
FetchContent_MakeAvailable(task)

include(CTest)
if(BUILD_TESTING)
Expand Down
4 changes: 3 additions & 1 deletion examples/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
# cmake-format: on

set(EXAMPLES
taps
client
cppcon-2024
empty
Expand All @@ -13,11 +14,12 @@ set(EXAMPLES
server
task
)
set(xEXAMPLES taps)

foreach(EXAMPLE ${EXAMPLES})
set(EXAMPLE_TARGET ${TARGET_PREFIX}.examples.${EXAMPLE})
add_executable(${EXAMPLE_TARGET})
target_sources(${EXAMPLE_TARGET} PRIVATE ${EXAMPLE}.cpp)
target_link_libraries(${EXAMPLE_TARGET} PRIVATE ${TARGET_LIBRARY})
target_link_libraries(${EXAMPLE_TARGET} PRIVATE beman::execution)
target_link_libraries(${EXAMPLE_TARGET} PRIVATE beman::task)
endforeach()
52 changes: 52 additions & 0 deletions examples/taps.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
// examples/taps.cpp -*-C++-*-
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception

#include <beman/execution/execution.hpp>
#include <beman/net/net.hpp>
#include <iostream>
#include <system_error>
#include <string>

namespace ex = beman::execution;
namespace net = beman::net;

// ----------------------------------------------------------------------------

int main(int, char*[]) {
std::cout << std::unitbuf;
net::scope scope;

std::cout << "spawning\n";
ex::spawn(
[]() -> net::task<> {
try {
net::preconnection pre(net::remote_endpoint().with_hostname("localhost").with_port(12345));
auto exp{co_await (net::initiate(pre) | net::detail::into_expected)};
if (!exp) {
std::cout << "initiate failed: " << exp.error().message() << "\n";
co_yield ex::with_error(exp.error());
}
auto socket{std::move(std::get<0>(exp.value()))};

std::string request = "GET / HTTP/1.1\r\nHost: example.com\r\nConnection: close\r\n\r\n";
std::cout << "async_send\n";
co_await net::async_send(socket, net::buffer(request));

char buffer[1024];
std::cout << "reading\n";
for (std::size_t n; (n = co_await net::async_receive(socket, net::buffer(buffer)));) {
std::cout << "read n=" << n << "\n";
std::cout.write(buffer, n);
}
} catch (...) {
std::cout << "exception!\n";
}
}() | ex::upon_error([](const std::error_code& e) noexcept {
std::cout << "Error: " << e.message() << "\n";
}) | ex::then([]() noexcept { std::cout << "running connection DONE!\n"; }),
scope.get_token());

std::cout << "spawned\n";

ex::sync_wait(scope.run());
}
28 changes: 28 additions & 0 deletions include/beman/net/detail/get_io_handle.hpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
// include/beman/net/detail/get_io_handle.hpp -*-C++-*-
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception

#ifndef INCLUDED_INCLUDE_BEMAN_NET_DETAIL_GET_IO_HANDLE
#define INCLUDED_INCLUDE_BEMAN_NET_DETAIL_GET_IO_HANDLE

#include <beman/execution/execution.hpp>

// ----------------------------------------------------------------------------

namespace beman::net::detail {
struct get_io_handle_t : beman::execution::forwarding_query_t {
template <typename Object>
requires requires(Object obj, const get_io_handle_t& gih) { obj.query(gih); }
auto operator()(const Object& obj) const noexcept -> decltype(obj.query(*this)) {
return obj.query(*this);
}
};
} // namespace beman::net::detail

namespace beman::net {
using get_io_handle_t = beman::net::detail::get_io_handle_t;
inline constexpr get_io_handle_t get_io_handle{};
} // namespace beman::net

// ----------------------------------------------------------------------------

#endif
26 changes: 26 additions & 0 deletions include/beman/net/detail/get_scope_token.hpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
// include/beman/net/detail/get_scope_token.hpp -*-C++-*-
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception

#ifndef INCLUDED_INCLUDE_BEMAN_NET_DETAIL_GET_SCOPE_TOKEN
#define INCLUDED_INCLUDE_BEMAN_NET_DETAIL_GET_SCOPE_TOKEN

#include <beman/execution/execution.hpp>

// ----------------------------------------------------------------------------

namespace beman::net::detail {
struct get_scope_token_t : beman::execution::forwarding_query_t {
template <typename Object>
auto operator()(const Object& obj) const -> decltype(obj.query(*this)) {
return obj.query(*this);
}
};
} // namespace beman::net::detail
namespace beman::net {
using get_scope_token_t = beman::net::detail::get_scope_token_t;
inline constexpr get_scope_token_t get_scope_token{};
} // namespace beman::net

// ----------------------------------------------------------------------------

#endif
48 changes: 48 additions & 0 deletions include/beman/net/detail/initiate.hpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
// include/beman/net/detail/initiate.hpp -*-C++-*-
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception

#ifndef INCLUDED_INCLUDE_BEMAN_NET_DETAIL_INITIATE
#define INCLUDED_INCLUDE_BEMAN_NET_DETAIL_INITIATE

#include <beman/net/detail/operations.hpp>
#include <beman/net/detail/preconnection.hpp>
#include <beman/net/detail/internet.hpp>
#include <beman/net/detail/into_expected.hpp>
#include <beman/net/detail/task.hpp>
#include <beman/net/detail/into_expected.hpp>

#include <beman/execution/execution.hpp>
#include <beman/execution/task.hpp>
#include <system_error>
#include <utility>

// ----------------------------------------------------------------------------

namespace beman::net::detail {
class initiate_t {
public:
auto operator()(const preconnection& pre) const -> beman::net::task<beman::net::ip::tcp::socket> {
//-dk:TODO use the destination endpoint from the preconnection
beman::net::ip::tcp::endpoint ep(net::ip::address_v4::loopback(), pre.remote().port());
// beman::net::ip::tcp::endpoint ep(net::ip::address_v4(0x9d'e6'43'b3), 80);
beman::net::ip::tcp::socket client(
(co_await beman::execution::read_env(beman::net::get_io_handle)).get_io_context(), ep);

auto exp{co_await (beman::net::async_connect(client) | beman::net::detail::into_expected |
beman::execution::then([](auto&& e) { return std::move(e); }))};
if (!exp) {
co_yield beman::execution::with_error(exp.error());
}
co_return std::move(client);
}
};
} // namespace beman::net::detail

namespace beman::net {
using initiate_t = beman::net::detail::initiate_t;
inline constexpr initiate_t initiate{};
} // namespace beman::net

// ----------------------------------------------------------------------------

#endif
136 changes: 136 additions & 0 deletions include/beman/net/detail/into_expected.hpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,136 @@
// include/beman/net/detail/into_expected.hpp -*-C++-*-
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception

#ifndef INCLUDED_INCLUDE_BEMAN_NET_DETAIL_INTO_EXPECTED
#define INCLUDED_INCLUDE_BEMAN_NET_DETAIL_INTO_EXPECTED

#include <beman/execution/execution.hpp>
#include <concepts>
#if 202202 <= __cpp_lib_expected
#include <expected>
#endif
#include <variant>
#include <type_traits>

// ----------------------------------------------------------------------------

namespace beman::net::detail {

#if 202202 <= __cpp_lib_expected
template <typename T, typename E>
using expected = std::expected<T, E>;
template <typename T>
using unexpected = std::unexpected<T>;
#else
template <typename T>
class unexpected {
public:
template <typename F>
explicit unexpected(F&& f) noexcept : _error(std::forward<F>(f)) {}
auto error() const& noexcept -> const T& { return this->_error; }
auto error() && noexcept -> T&& { return std::move(this->_error); }

private:
std::remove_cvref_t<T> _error;
};
template <typename T>
unexpected(T&&) -> unexpected<std::remove_cvref_t<T>>;

template <typename T, typename E>
class expected {
public:
template <typename F>
explicit expected(::beman::net::detail::unexpected<F>&& e) noexcept
: _value(std::in_place_index<1>, std::move(e).error()) {}
template <typename F>
explicit expected(const ::beman::net::detail::unexpected<F>& e) noexcept
: _value(std::in_place_index<1>, e.error()) {}
template <typename... S>
explicit expected(S&&... s) noexcept : _value(std::in_place_index<0>, std::forward<S>(s)...) {}

explicit operator bool() const noexcept { return this->_value.index() == 0; }
const T& value() const& { return std::get<0>(this->_value); }
const E& error() const& { return std::get<1>(this->_value); }
T& value() & { return std::get<0>(this->_value); }
E& error() & { return std::get<1>(this->_value); }
T&& value() && { return std::get<0>(std::move(this->_value)); }
E&& error() && { return std::get<1>(std::move(this->_value)); }

private:
std::variant<std::remove_cvref_t<T>, std::remove_cvref_t<E>> _value;
};
#endif

struct into_expected_t : beman::execution::sender_adaptor_closure<into_expected_t> {
template <typename Sender, typename Env>
using expected_t =
beman::net::detail::expected<beman::execution::value_types_of_t<Sender, Env, std::tuple, std::type_identity_t>,
beman::execution::error_types_of_t<Sender, Env, std::type_identity_t>>;

template <beman::execution::sender Sender, beman::execution::receiver Receiver>
struct state {
struct receiver {
using receiver_concept = beman::execution::receiver_t;
using env_t = beman::execution::env_of_t<Receiver>;
Receiver* _receiver;
auto get_env() const noexcept { return beman::execution::get_env(*this->_receiver); }
template <typename... Args>
auto set_value(Args&&... args) && noexcept {
beman::execution::set_value(std::move(*this->_receiver),
expected_t<Sender, env_t>(std::forward<Args>(args)...));
}
template <typename Error>
auto set_error(Error&& error) && noexcept {
beman::execution::set_value(
std::move(*this->_receiver),
expected_t<Sender, env_t>(
beman::net::detail::unexpected<std::remove_cvref_t<Error>>(std::forward<Error>(error))));
}
auto set_stopped() && noexcept { beman::execution::set_stopped(std::move(*this->_receiver)); }
};
using operation_state_concept = beman::execution::operation_state_t;
using inner_state_t = beman::execution::connect_result_t<Sender, receiver>;

Receiver _receiver;
inner_state_t _state;

state(auto&& sndr, Receiver&& r)
: _receiver(std::forward<Receiver>(r)),
_state(beman::execution::connect(std::forward<Sender>(sndr), receiver{&this->_receiver})) {}

auto start() & noexcept -> void { beman::execution::start(this->_state); }
};
template <beman::execution::sender Sender>
struct sender {
using sender_concept = beman::execution::sender_t;

Sender _sender;

template <typename Env>
auto get_completion_signatures(const Env&) const {
return beman::execution::completion_signatures<beman::execution::set_value_t(expected_t<Sender, Env>),
beman::execution::set_stopped_t()>();
}
template <beman::execution::receiver Receiver>
auto connect(Receiver&& receiver) {
return state<Sender, std::remove_cvref_t<Receiver>>(std::move(_sender), std::forward<Receiver>(receiver));
}
};

template <beman::execution::sender Sender>
auto operator()(Sender&& sndr) const {
return sender<std::remove_cvref_t<Sender>>{std::forward<Sender>(sndr)};
}
};

inline constexpr into_expected_t into_expected{};
} // namespace beman::net::detail

namespace beman::net {
using into_expected_t = beman::net::detail::into_expected_t;
inline constexpr into_expected_t into_expected{};
} // namespace beman::net

// ----------------------------------------------------------------------------

#endif
Loading
Loading