Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -49,3 +49,5 @@ CMakeFiles/
*.log
docs/html
docs/latex
docs/code/*.cpp
docs/tutorial.md
1 change: 1 addition & 0 deletions CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -105,6 +105,7 @@ endif()

if(BEMAN_EXECUTION_BUILD_EXAMPLES)
add_subdirectory(examples)
add_subdirectory(docs/code)
endif()

if(NOT BEMAN_EXECUTION_ENABLE_INSTALL OR CMAKE_SKIP_INSTALL_RULES)
Expand Down
11 changes: 6 additions & 5 deletions Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -56,8 +56,8 @@ ifeq (${hostSystemName},Darwin)
export GCC_DIR:=$(shell realpath ${GCC_PREFIX})

export CMAKE_CXX_STDLIB_MODULES_JSON=${GCC_DIR}/lib/gcc/current/libstdc++.modules.json
export CXX:=g++-15
export CXXFLAGS:=-stdlib=libstdc++
# export CXX:=g++-15
export CXXFLAGS:=-stdlib=libstdc++
export GCOV="gcov"
else ifeq (${hostSystemName},Linux)
export LLVM_DIR=/usr/lib/llvm-20
Expand Down Expand Up @@ -105,6 +105,7 @@ run: test
./$(BUILD)/examples/$(EXAMPLE)

doc:
./bin/mk-doc.py docs/*.mds
doxygen docs/Doxyfile

# $(SANITIZERS):
Expand Down Expand Up @@ -171,14 +172,14 @@ cmake-format:
pre-commit run gersemi

clang-format:
pre-commit run $@
# XXX TBD: git clang-format main
# pre-commit run $@
git clang-format main

todo:
bin/mk-todo.py

unstage:
git restore --staged tests/beman/execution/CMakeLists.txt
git restore --staged tests/beman/execution/CMakeLists.txt docs/code/CMakeLists.txt

.PHONY: clean-doc
clean-doc:
Expand Down
78 changes: 78 additions & 0 deletions bin/mk-doc.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,78 @@
#!/usr/bin/python3

import re
import sys

prestart_re = re.compile('\s*<pre name="([^"]*)">')
preend_re = re.compile("\s*</pre>")
append_re = re.compile("list\(APPEND EXAMPLES")
appendp_re = re.compile("list\(APPEND EXAMPLES\)")
paren_re = re.compile("\)")


def update_cmake(list):
text = ""
skipping = False
with open("docs/code/CMakeLists.txt") as cmake:
for line in cmake:
if skipping:
if paren_re.match(line):
skipping = False
text += line
else:
if append_re.match(line):
text += "list(APPEND EXAMPLES\n"
text += "\n".join(list) + "\n"
skipping = not appendp_re.match(line)
if not skipping:
text += ")\n"
else:
text += line

with open("docs/code/CMakeLists.txt", "w") as cmake:
cmake.write(text)


def transform(text, output):
capturing = False
code = ""
name = ""
names = {}
for line in text:
match = prestart_re.match(line)
if match:
print(f"example: {match.group(1)}")
name = match.group(1)
code = ""
output.write(f"```c++\n")
capturing = True
elif capturing and preend_re.match(line):
if name in names:
print(f"skipping code with duplicate name '{name}'")
else:
names[name] = code
with open("docs/code/" + name + ".cpp", "w") as codeout:
codeout.write(code)
capturing = False
output.write(f"```\n")
else:
if capturing:
code += line
output.write(f"{line}")
update_cmake(names.keys())


def process(root):
print(f"processing {root}")
text = []
with open(root + ".mds") as input:
text = input.readlines()
with open(root + ".md", "w") as output:
transform(text, output)


sys.argv.pop(0)
for file in sys.argv:
match = re.match("(.*)\.mds$", file)
if match:
process(match.group(1))
20 changes: 20 additions & 0 deletions docs/code/CMakeLists.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
# gersemi: off
# docs/code/CMakeLists.txt -*-makefile-*-
# SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
# gersemi: on

list(APPEND EXAMPLES)

if(BEMAN_USE_MODULES)
list(APPEND EXAMPLES modules) # modules.cpp
endif()

foreach(EXAMPLE ${EXAMPLES})
set(EXAMPLE_TARGET ${TARGET_PREFIX}.tutorial.${EXAMPLE})
add_executable(${EXAMPLE_TARGET})
target_sources(${EXAMPLE_TARGET} PRIVATE ${EXAMPLE}.cpp)
target_link_libraries(
${EXAMPLE_TARGET}
PRIVATE ${TARGET_NAMESPACE}::${TARGET_NAME}
)
endforeach()
38 changes: 38 additions & 0 deletions docs/code/tst-basic-timer.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
// examples/tst-basic-timer.cpp -*-C++-*-
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
// ----------------------------------------------------------------------------

#include <beman/execution/execution.hpp>
#include <iostream>
#include "tst.hpp"
namespace ex = beman::execution;

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

struct receiver {
using receiver_concept = ex::receiver_t;

auto set_value() && noexcept { std::cout << "timer done\n"; }
auto set_error(const std::exception_ptr&) && noexcept { std::cout << "timer error\n"; }
auto set_stopped() && noexcept { std::cout << "timer stopped\n"; }
};

std::ostream& fmt_now(std::ostream& os) { return os << std::chrono::system_clock::now(); }

auto main() -> int {
std::size_t count{3};
ex::sync_wait(tst::repeat_effect_until(ex::just() | ex::then([]() noexcept { std::cout << "effect\n"; }),
[&count]() noexcept { return --count == 0u; }));

tst::timer timer{};
std::cout << fmt_now << ": start\n";
ex::sync_wait(
ex::when_all(tst::resume_after(timer.get_token(), std::chrono::milliseconds(1000)) |
ex::then([]() noexcept { std::cout << fmt_now << ": 1000ms timer fired\n"; }),
tst::resume_after(timer.get_token(), std::chrono::milliseconds(500)) |
ex::then([]() noexcept { std::cout << fmt_now << ": 500ms timer fired\n"; }),
tst::resume_after(timer.get_token(), std::chrono::milliseconds(1500)) |
ex::then([]() noexcept { std::cout << fmt_now << ": 1500ms timer fired\n"; }),
timer.when_done() | ex::then([]() noexcept { std::cout << fmt_now << ": timer done\n"; }),
ex::just()));
}
16 changes: 16 additions & 0 deletions docs/code/tst-config.hpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
// docs/code/tst-config.hpp -*-C++-*-
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception

#ifndef INCLUDED_DOCS_CODE_TST_CONFIG
#define INCLUDED_DOCS_CODE_TST_CONFIG

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

#include <beman/execution/execution.hpp>
namespace tst {
namespace ex = beman::execution;
}

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

#endif
87 changes: 87 additions & 0 deletions docs/code/tst-repeat_effect_until.hpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,87 @@
// examples/tst-repeat_effect_until.hpp -*-C++-*-
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
// ----------------------------------------------------------------------------

#ifndef INCLUDED_EXAMPLES_TST_REPEAT_EFFECT_UNTIL
#define INCLUDED_EXAMPLES_TST_REPEAT_EFFECT_UNTIL

#include <optional>
#include <type_traits>
#include <utility>
#include "tst-config.hpp"

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

namespace tst {
template <ex::sender Sndr, ex::receiver Rcvr>
struct connector {
decltype(ex::connect(::std::declval<Sndr>(), ::std::declval<Rcvr>())) op;
connector(auto sndr, auto rcvr) : op(ex::connect(::std::move(sndr), ::std::move(rcvr))) {}

auto start() & noexcept -> void { ex::start(this->op); }
};

inline constexpr struct repeat_effect_unilt_t {
template <ex::sender Child, typename Fun>
struct sender {
using sender_concept = ex::sender_t;
using completion_signatures =
ex::completion_signatures<ex::set_value_t(), ex::set_error_t(std::exception_ptr), ex::set_stopped_t()>;

template <ex::receiver Receiver>
struct state {
using operation_state_concept = ex::operation_state_t;
struct own_receiver {
using receiver_concept = ex::receiver_t;
state* s;
auto set_value() && noexcept -> void {
static_assert(ex::receiver<own_receiver>);
this->s->next();
}
auto set_error(std::exception_ptr error) && noexcept -> void {
ex::set_error(::std::move(this->s->receiver), std::move(error));
}
auto set_stopped() && noexcept -> void { ex::set_stopped(::std::move(this->s->receiver)); }
};

std::remove_cvref_t<Child> child;
std::remove_cvref_t<Fun> fun;
std::remove_cvref_t<Receiver> receiver;
std::optional<tst::connector<std::remove_cvref_t<Child>, own_receiver>> child_op;

auto start() & noexcept -> void {
static_assert(ex::operation_state<state>);
this->run_one();
}
auto run_one() & noexcept -> void {
this->child_op.emplace(this->child, own_receiver{this});
this->child_op->start();
}
auto next() & noexcept -> void {
if (this->fun()) {
ex::set_value(::std::move(this->receiver));
} else {
this->run_one();
}
}
};

std::remove_cvref_t<Child> child;
std::remove_cvref_t<Fun> fun;

template <ex::receiver Receiver>
auto connect(Receiver&& receiver) const& noexcept -> state<Receiver> {
static_assert(ex::sender<sender>);
return state<Receiver>(this->child, this->fun, ::std::forward<Receiver>(receiver));
}
};
template <ex::sender Child, typename Pred>
auto operator()(Child&& child, Pred&& pred) const -> sender<Child, Pred> {
return {::std::forward<Child>(child), ::std::forward<Pred>(pred)};
}
} repeat_effect_until{};
} // namespace tst

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

#endif
71 changes: 71 additions & 0 deletions docs/code/tst-sync_wait.hpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,71 @@
// docs/code/tst-sync_wait.hpp -*-C++-*-
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
// ----------------------------------------------------------------------------

#ifndef INCLUDED_DOCS_CODE_TST_SYNC_WAIT
#define INCLUDED_DOCS_CODE_TST_SYNC_WAIT

#include <utility>
#include <type_traits>
#include "tst-config.hpp"

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

namespace tst {
template <tst::ex::sender Sender>
struct add_set_value {
template <typename S>
struct is_set_value : std::false_type {};
template <typename... A>
struct is_set_value<tst::ex::set_value_t(A...)> : std::true_type {};
template <typename>
struct contains_set_value;
template <typename... S>
struct contains_set_value<tst::ex::completion_signatures<S...>>
: std::bool_constant<(... || is_set_value<S>::value)> {};

template <typename T, bool = contains_set_value<T>::value>
struct add_signature {
using type = T;
};
template <typename... S>
struct add_signature<tst::ex::completion_signatures<S...>, false> {
using type = tst::ex::completion_signatures<tst::ex::set_value_t(), S...>;
};
using sender_concept = tst::ex::sender_t;
template <typename Env>
constexpr auto get_completion_signatures(const Env& e) noexcept {
using orig = decltype(tst::ex::get_completion_signatures(std::declval<Sender>(), e));
return typename add_signature<orig>::type{};
}
std::remove_cvref_t<Sender> inner;
template <tst::ex::receiver Rcvr>
auto connect(Rcvr&& rcvr) && {
return tst::ex::connect(std::move(this->inner), std::forward<Rcvr>(rcvr));
}
};
template <tst::ex::sender Sender>
add_set_value(Sender&&) -> add_set_value<std::remove_cvref_t<Sender>>;

inline constexpr struct just_error_t {
template <typename E>
auto operator()(E&& e) const {
return add_set_value(ex::just_error(std::forward<E>(e)));
}
} just_error{};
inline constexpr struct when_all_t {
template <tst::ex::sender... Senders>
auto operator()(Senders&&... sndrs) const {
return ex::when_all(tst::add_set_value(std::forward<Senders>(sndrs))...);
}
} when_all{};

template <tst::ex::sender Sender>
auto sync_wait(Sender&& sndr) {
return tst::ex::sync_wait(add_set_value<Sender>{std::forward<Sender>(sndr)});
}
} // namespace tst

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

#endif
Loading