diff --git a/CMakeLists.txt b/CMakeLists.txt index ac1b764..051fc22 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -20,6 +20,11 @@ list(APPEND "$<$,$,$>:-Wall;-Wextra;-pedantic;-Werror;-Wno-parentheses;-Wno-shadow;-Wconversion;-Wsign-conversion>" "$<$:/W4>") # /WX for -Werror +# Fix atomic lib linking for gcc. +list(APPEND + BASE_ADDITIONAL_LIBS + "$<$,$>:atomic>") + set(CMAKE_RUNTIME_OUTPUT_DIRECTORY ${PROJECT_BINARY_DIR}/bin) set(CMAKE_ARCHIVE_OUTPUT_DIRECTORY ${PROJECT_BINARY_DIR}/lib) set(CMAKE_LIBRARY_OUTPUT_DIRECTORY ${PROJECT_BINARY_DIR}/lib) diff --git a/include/hspp.h b/include/hspp.h index a2c8789..07b9fb6 100644 --- a/include/hspp.h +++ b/include/hspp.h @@ -1262,18 +1262,32 @@ template constexpr static auto isIOV = IsIO>::value; template -auto io(Func func) +constexpr auto io(Func func) { using Data = std::invoke_result_t; - return IO{func}; + return IO{std::move(func)}; } template -auto ioData(Data data) +constexpr auto ioData(Data data) { return io([data=std::move(data)] { return data; }); } +template +constexpr auto toTEIOImpl(IO const& p) +{ + return IO{[p]{ + return p.run(); + }}; +} + +constexpr auto toTEIO = toGFunc<1> | [](auto p) +{ + return toTEIOImpl(p); +}; + + constexpr auto putChar = toFunc<> | [](char c) { return io( @@ -2663,7 +2677,7 @@ class Functor template constexpr static auto fmap(Func&& func, data::Reader const& in) { - return data::toReader | func (data::runReader | in); + return data::toReader || func (data::runReader | in); } }; @@ -2832,12 +2846,12 @@ class Applicative : public Functor | [](auto ret) { - return data::toReader | data::toFunc<>([ret=std::move(ret)](FirstArg){ return ret; }); + return data::toReader || data::toFunc<>([ret=std::move(ret)](FirstArg){ return ret; }); }; template constexpr static auto ap(Reader1 func, Reader1 in) { - return data::toReader | data::toFunc<>( + return data::toReader || data::toFunc<>( [func=std::move(func), in=std::move(in)](FirstArg arg) { return data::runReader | func | arg || data::runReader | in | arg; @@ -3564,6 +3578,17 @@ constexpr auto nullary(T const &t) return Nullary{t}; } +template +constexpr auto toTENullaryImpl(Nullary const &t) +{ + return nullary(std::function>{t}); +} + +constexpr auto toTENullary = toGFunc<1> | [](auto const& t) +{ + return toTENullaryImpl(t); +}; + template class IsNullary : public std::false_type { @@ -3577,6 +3602,13 @@ class IsNullary> : public std::true_type template constexpr auto isNullary = IsNullary>::value; +template , void>> +constexpr auto evalDeferredImpl(T&& t) +{ + static_assert(std::is_same_v, MonadType>>); + return t(); +} + template class IsNullaryOrId : public IsNullary { @@ -3825,7 +3857,15 @@ template constexpr auto do_(Head const& head, Rest const&... rest) { using MClass = MonadClassType; - return doImpl(head, rest...); + auto result = doImpl(head, rest...); + static_assert(!isNullaryOrIdV); + return result; +} + +template +constexpr auto doInner(Args&&... args) +{ + return nullary([=] { return do_(evaluate_(args)...); }); } template diff --git a/test/hspp/CMakeLists.txt b/test/hspp/CMakeLists.txt index 13c37ff..145486e 100644 --- a/test/hspp/CMakeLists.txt +++ b/test/hspp/CMakeLists.txt @@ -1,6 +1,5 @@ add_executable(unittests test.cpp stm.cpp) target_include_directories(unittests PRIVATE) -target_compile_options(unittests PRIVATE ${BASE_COMPILE_FLAGS}) -target_link_libraries(unittests PRIVATE hspp gtest_main) +target_link_libraries(unittests PRIVATE hspp gtest_main ${BASE_ADDITIONAL_LIBS}) set_target_properties(unittests PROPERTIES CXX_EXTENSIONS OFF) gtest_discover_tests(unittests) \ No newline at end of file diff --git a/test/hspp/stm.cpp b/test/hspp/stm.cpp index 7e62823..f4d134b 100644 --- a/test/hspp/stm.cpp +++ b/test/hspp/stm.cpp @@ -6,6 +6,7 @@ #include #include #include +#include using namespace hspp; using namespace hspp::data; @@ -51,22 +52,22 @@ TEST(forkIO, 1) io_.run(); } -TEST(forkIO, 2) +constexpr auto setReminder = toFunc<> | [](std::string const& s) { - auto setReminder = toFunc<> | [](std::string const& s) - { - Id n; - return do_( - n = (hspp::read | s), - putStr | "Ok, I'll remind you in ", - print | n, - putStrLn | " seconds", - threadDelay | (1000000U * n), - print | n, - putStrLn | " seconds is up! BING!BEL" - ); - }; + Id n; + return do_( + n = (hspp::read | s), // let expression. + putStr | "Ok, I'll remind you in ", + print | n, + putStrLn | " seconds", + threadDelay | (1000000U * n), + print | n, + putStrLn | " seconds is up! BING!BEL" + ); +}; +TEST(forkIO, 2) +{ Id s; auto io_ = forever || do_( s <= getLine, @@ -74,8 +75,193 @@ TEST(forkIO, 2) ); // io.run(); (void)io_; + +} + +TEST(forkIO, 3) +{ + IO<_O_> loop0 = []{ return _o_; }; + auto loop = loop0; + + Id s; + loop = toTEIO | do_( + s <= getLine, + ifThenElse(s == "exit") + || loop0 + || toTEIO | (doInner(forkIO || setReminder | s, + nullary([&]{ return loop;}))) // capturing by ref is important, so that loop is not fixed to loop0. + ); + + // loop.run(); + (void)loop; +} + +template +struct MVar +{ + using T = std::pair, std::shared_mutex>; + std::shared_ptr data = std::make_shared(); + MVar() = default; + MVar(A a) + : data{std::make_shared(a, {})} + {} +}; + +template +constexpr auto newEmptyMVar = io([]{ return MVar{}; }); + +template +constexpr auto newMVarImpl(A a) +{ + return io([&]{ return MVar{std::move(a)}; }); +} + +constexpr auto newMVar = toGFunc<1> | [](auto a) +{ + return newMVarImpl(a); +}; + +template +constexpr auto takeMVarImpl(MVar const& a) +{ + return io([a] + { + while (true) + { + { + std::unique_lock lock{a.data->second}; + if (a.data->first.has_value()) + { + auto result = std::move(a.data->first.value()); + a.data->first.reset(); + return result; + } + } + std::this_thread::yield(); + } + }); +} + +constexpr auto takeMVar = toGFunc<1> | [](auto a) +{ + return takeMVarImpl(a); +}; + +template +constexpr auto putMVarImpl(MVar& a, A new_) +{ + return io([a, new_] + { + while (true) + { + { + std::unique_lock lock{a.data->second}; + if (!a.data->first.has_value()) + { + a.data->first = new_; + return _o_; + } + } + std::this_thread::yield(); + } + }); +} + +constexpr auto putMVar = toGFunc<2> | [](auto a, auto new_) +{ + return putMVarImpl(a, new_); +}; + +TEST(MVar, 1) +{ + (void)newMVar; + + Id> m; + Id r; + auto const io_ = do_( + m <= newEmptyMVar, + forkIO || putMVar | m | 'x', + r <= (takeMVar | m), + print | r + ); + io_.run(); +} + +TEST(MVar, 2) +{ + Id> m; + Id r; + auto io_ = do_( + m <= newEmptyMVar, + forkIO || doInner( + putMVar | m | 'x', + putMVar | m | 'y' + ), + r <= (takeMVar | m), + print | r, + r <= (takeMVar | m), + print | r + ); + io_.run(); +} + +TEST(MVar, 3) +{ + Id> m; + auto io_ = do_( + m <= newEmptyMVar, + takeMVar | m + ); + // stuck + (void)io_; + // io_.run(); } +#if 0 +class Message : public std::string{}; +class Stop : public MVar<_O_>{}; + +using LogCommand = std::variant; +class Logger : public MVar{}; + + +auto logger(Logger& m) +{ + IO<_O_> loop0 = []{ return _o_; }; + auto loop = loop0; + + auto const dispatchCmd = toFunc<> | [&loop](LogCommand const& lc) + { + return std::visit(overload( + [&](Message const& msg){ + return toTEIO | do_(print | msg, loop); + }, + [](Stop s){ + return toTEIO | do_(putStrLn | "logger: stop", putMVar | s | _o_); + } + ), lc); + }; + + Id cmd; + loop = toTEIO | do_( + cmd <= (takeMVar | m), + dispatchCmd | cmd + ); +} + +constexpr auto initLoggerImpl() +{ + Id m; + Id l; + return do_( + m <= newEmptyMVar, + l = (Logger | m), + forkIO | (logger | l), + return_ | l + ); +} +#endif // 0 + template struct IORef {