/** * Copyright 2015 Denis Blank * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #include #include #include "Callback.h" #include "WeakCallbackContainer.h" #include "Continuable.h" #include #include #include #include #include #include #include #include #include #include #include // #include "concurrentqueue.h" #include enum SpellCastResult { SPELL_FAILED_SUCCESS = 0, SPELL_FAILED_AFFECTING_COMBAT = 1, SPELL_FAILED_ALREADY_AT_FULL_HEALTH = 2, SPELL_FAILED_ALREADY_AT_FULL_MANA = 3, SPELL_FAILED_ALREADY_AT_FULL_POWER = 4, SPELL_FAILED_ALREADY_BEING_TAMED = 5 }; template using Optional = boost::optional; Continuable<> Log(std::string const& message) { return make_continuable([=](Callback<>&& callback) { std::cout << message << std::endl; callback(); }); } struct ResultSet { ResultSet(std::size_t affected_) : affected(affected_) { }; std::size_t affected; }; Continuable AsyncQuery(std::string const& query) { return make_continuable([=](Callback&& callback) { std::cout << query << std::endl; callback(ResultSet(2)); }); } // Original method taking an optional callback. void CastSpell(int id, Optional> const& callback = boost::none) { std::cout << "Casting " << id << std::endl; // on success call the callback with SPELL_FAILED_SUCCESS if (callback) (*callback)(SPELL_FAILED_SUCCESS); } // Promise wrapped callback decorator. Continuable CastSpellPromise(int id) { return make_continuable([=](Callback&& callback) { CastSpell(id, callback); }); } // Void instant returning continuable promise for testing purposes Continuable<> TrivialPromise(std::string const& msg = "") { return Log(msg); } Continuable Validate() { return make_continuable([=](Callback&& callback) { std::cout << "Validate " << std::endl; callback(true); }); } Continuable&&> MoveTest() { return make_continuable([=](Callback&&>&& callback) { // Move the unique ptr out to test moveability std::unique_ptr ptr(new int(5)); callback(std::move(ptr)); }); } typedef std::unique_ptr Moveable; void testMoveAbleNormal(std::function&&)> callback) { std::unique_ptr ptr(new int(5)); callback(std::move(ptr)); } template void test_unwrap(std::string const& msg) { std::cout << msg << " is unwrappable: " << (fu::is_unwrappable::value ? "true" : "false") << std::endl; } #include #include #include // static std::atomic_size_t move_tracer_index = 0; /// Class to trace construct, destruct, copy and move operations. /* class CopyMoveTracer { std::size_t id; std::size_t flags; std::size_t copied; std::size_t moved; bool IsEnabled(std::size_t mask) const { // msvc warning silencer return (flags & mask) != 0; } void Log(std::string const& msg) const { } public: enum Flags : std::size_t { CAPTURE_NONE = 0x1, CAPTURE_CONSTRUCT = 0x1, CAPTURE_DESTRUCT = 0x2, CAPTURE_COPY = 0x4, CAPTURE_MOVE = 0x8, CAPTURE_ALL = CAPTURE_CONSTRUCT | CAPTURE_DESTRUCT | CAPTURE_COPY | CAPTURE_MOVE }; // Empty construct CopyMoveTracer() : id(++move_tracer_index), flags(CAPTURE_ALL), copied(0), moved(0) { if (IsEnabled(CAPTURE_CONSTRUCT)) Log("Tracer constructed"); } // Construct with flags CopyMoveTracer(std::size_t flags_) : id(++move_tracer_index), flags(flags_), copied(0), moved(0) { if (IsEnabled(CAPTURE_CONSTRUCT)) Log("Tracer constructed"); } // Copy construct CopyMoveTracer(CopyMoveTracer const& right) : id(move_tracer_index++), flags(right.flags), copied(0), moved(0) { if (IsEnabled(CAPTURE_COPY)) Log("Tracer copy constructed"); } // Copy construct CopyMoveTracer(CopyMoveTracer&& right) : id(right.id), flags(right.flags), copied(0), moved(0) { if (IsEnabled(CAPTURE_COPY)) Log("Tracer copy constructed"); } }; */ /* namespace detail { template struct function_matches_to_args; template struct function_matches_to_args< std::function, std::function> { }; } */ /* class DispatcherPool { enum TerminationMode { NONE, TERMINATE, AWAIT }; typedef std::function Callable; std::vector _pool; std::atomic _shutdown; std::mutex _mutex; std::condition_variable _condition; // moodycamel::ConcurrentQueue _queue; public: DispatcherPool() : DispatcherPool(std::thread::hardware_concurrency()) { } DispatcherPool(unsigned int const threads) : _shutdown(NONE) { for (unsigned int i = 0; i < threads; ++i) { _pool.emplace_back([&, i] { // Reserve the consumer token // moodycamel::ConsumerToken token(_queue); Callable callable; while (_shutdown != TERMINATE) { if (_queue.try_dequeue(token, callable)) { std::string msg = "Thread " + std::to_string(i) + " is dispatching...\n"; // std::cout << msg; callable(); } else { if (_shutdown == AWAIT) break; { std::string msg = "Thread " + std::to_string(i) + " out of work...\n"; // std::cout << msg; } std::unique_lock lock(_mutex); // Lock until new tasks are added _condition.wait(lock); { std::string msg = "Thread " + std::to_string(i) + " wakes up...\n"; // std::cout << msg; } } } }); } } ~DispatcherPool() { Shutdown(); } template void Dispatch(Functional&& functional) { _queue.enqueue(std::forward(functional)); std::unique_lock lock(_mutex); _condition.notify_one(); } void Shutdown() { _Shutdown(TERMINATE); } void Await() { _Shutdown(AWAIT); } void _Shutdown(TerminationMode const mode) { _shutdown = mode; _condition.notify_all(); for (auto&& thread : _pool) if (thread.joinable()) thread.join(); } }; */ void some_examples() { // CopyMoveTracer tracer; /* CastSpellPromise(1) .then([](SpellCastResult) { return CastSpellPromise(2); }) .then([](SpellCastResult) { std::cout << "Pause a callback (void test) " << std::endl; }) .then(Validate()) .then(AsyncQuery("SELECT * FROM `users`") .then([](ResultSet result) { // Evaluate result std::size_t const affected = result.affected; return Log(std::to_string(affected) + " rows affected\n"); }) ) .then(TrivialPromise("huhu")) .then(CastSpellPromise(3)) .then(CastSpellPromise(4) .then(CastSpellPromise(5)) ) .then(CastSpellPromise(6)) .then([](SpellCastResult) { return Validate(); }); MoveTest() .then([](std::unique_ptr&& ptr) { static_assert(std::is_rvalue_reference::value, "no rvalue"); // Error here std::unique_ptr other = std::move(ptr); }); // Mockup of aggregate methods make_continuable() .all( [] { return TrivialPromise(); }, [] { return TrivialPromise(); }, [] { return TrivialPromise(); } ) .some(2, // Only 2 of 3 must complete [] { return TrivialPromise(); }, [] { return TrivialPromise(); }, [] { return TrivialPromise(); } ) .any( // Any of 2. [] { return TrivialPromise(); }, [] { return TrivialPromise(); } ) .then([] { std::cout << "Finished" << std::endl; }); */ //Continuable cb = make_continuable([](Callback&& callback) //{ // callback(true); //}); //test_unwrap("void()"); //test_unwrap>("std::function"); //test_unwrap>("std::vector"); //make_continuable([=](Callback<>&&) //{ //}); //int i = 0; //++i; //auto lam = [=](Callback&&) //{ // // on success call the callback with SPELL_FAILED_SUCCESS // // callback(SPELL_FAILED_SUCCESS); //}; //fu::function_type_of_t fun1; //fun1 = lam; //fun1(Callback()); //fu::function_type_of_t> fun2; // //shared_callback_of_t> sc1; //weak_callback_of_t> sc2; // //make_weak_wrapped_callback(sc1); //make_weak_wrapped_callback(sc2); //WeakCallbackContainer callback; // //auto weakCallback = callback([] //{ //}); //typedef Continuable cont123; //typedef Continuable myty1; //typedef Continuable myty2; //// Convertible test // //// Continuable> spell //{ // auto stack = // int iii = 0; // iii = 1; //} //std::vector myvec; //typedef fu::requires_functional_constructible>::type test_assert1; //// typedef fu::requires_functional_constructible>::type test_assert2; //// Brainstorming: this shows an example callback chain //// Given by continuable //std::function&&)> continuable_1 = [](Callback&& callback) //{ // callback(SPELL_FAILED_AFFECTING_COMBAT); //}; //// Implemented by user //std::function&&)>(SpellCastResult)> callback_by_user_1 = [](SpellCastResult) //{ // // Given by continuable // // Fn2 // return [](Callback&& callback) // { // callback(true); // }; //}; //// Implemented by user //std::function&&)>(bool)> cn2 = [](bool val) //{ // // Finished // std::cout << "Callback chain finished! -> " << val << std::endl; // // Given by continuable (auto end) // return [](Callback<>&&) // { // // Empty callback // }; //}; //// Entry point //std::function&&>)> entry = [continuable_1 /*= move*/, callback_by_user_1 /*given by the user (::then(...))*/] // (std::function&&)>) //{ // // Call with auto created wrapper by the continuable // continuable_1([&](SpellCastResult result /*forward args*/) // { // // Wrapper functional to process unary or multiple promised callbacks // // Returned from the user // std::function&&)> fn2 = callback_by_user_1(/*forward args*/ result); // return std::move(fn2); // }); //}; //// Here we go //entry(); /* detail::unary_chainer_t< std::function()> >::callback_arguments_t args213987; typedef detail::functional_traits<>::result_maker_of_t< std::function()>, decltype(CastSpellPromise(2)), decltype(TrivialPromise()), std::function()>, std::function()>, std::function()> > maker; maker::arguments_t test282_args; maker::partial_results_t test282_pack; auto test282_size = maker::size; // static_assert(std::is_same<>::value, detail::concat_identities, fu::identity>::type myt; // fu::identity::position<1>> i; std::tuple> tup; Moveable moveable(new int(7)); auto myargs = std::make_tuple(7, std::vector({ 1, 2, 3 }), std::move(moveable)); std::function, Moveable&&)> lam = [](int given_i, std::vector given_vec, Moveable&& moveable) { Moveable other = std::move(moveable); ++given_i; return 1; }; fu::invoke_from_tuple(lam, std::move(myargs)); fu::sequence_generator<2>::type seqtype; fu::sequence_generator<1>::type zero_seqtype; detail::multiple_when_all_chainer_t< fu::identity<>, fu::identity< std::function()>, std::function()> > >::result_maker::partial_results_t myres123345; */ /* auto firstType = detail::multiple_when_all_chainer_t< fu::identity<>, fu::identity< std::function()>, std::function()>, std::function()> > >::make_when_all( [] { // void return CastSpellPromise(10); }, [] { return make_continuable(); }, [] { return CastSpellPromise(20); }) .then([](SpellCastResult, SpellCastResult) { }) .then([] { }); */ make_continuable() /*.all( CastSpellPromise(10) .then(CastSpellPromise(15)), CastSpellPromise(20), make_continuable([](Callback>&& callback) { callback(true, false, 0.3f, std::unique_ptr(new std::string("oh, all work is done!"))); }), [] { return TrivialPromise(); }) */ /*.then([](SpellCastResult r0, SpellCastResult r1, bool r2, bool r3, double r4, std::unique_ptr message) { return TrivialPromise("Lets see... ").then(Log(*message)); })*/ .then([] { return Log("ok, now its really finished!").then(CastSpellPromise(2)); }) .then([](SpellCastResult) { }) .then([] { }); // DispatcherPool countPool(1); // DispatcherPool pool; /* auto const seed = std::chrono::steady_clock::now().time_since_epoch().count(); std::mt19937 rng(static_cast(seed)); std::uniform_int_distribution gen(10, 150); std::vector container; unsigned int counter = 0; for (unsigned int run = 0; run < 15; ++run) { for (unsigned int i = 0; i < 20; ++i) { unsigned int wait = gen(rng); ++counter; pool.Dispatch([&countPool, &container, i, run, wait] { // std::this_thread::sleep_for(std::chrono::milliseconds(wait)); std::string str = "Pass " + std::to_string(run) + " dispatching " + std::to_string(i) + " (" + std::to_string(wait) + "ms delay)" + "\n"; // std::cout << str; countPool.Dispatch([&container, wait] { container.emplace_back(wait); }); }); } std::this_thread::sleep_for(std::chrono::milliseconds(50)); } // std::cout << "Awaiting termination...\n"; // std::this_thread::sleep_for(std::chrono::seconds(5)); // std::this_thread::sleep_for(std::chrono::seconds(5)); pool.Await(); countPool.Await(); std::cout << container.size() << " == " << counter; */ } template using cross_forward_t = typename std::conditional< std::is_rvalue_reference::value, typename std::decay::type&&, typename std::conditional< std::is_lvalue_reference::value, typename std::decay::type&, typename std::decay::type >::type >::type; template cross_forward_t cross_forward(V&& var) { return static_cast&&>(var); } struct TestContainer { std::shared_ptr ptr; }; template TestContainer extract(T&& c) { return TestContainer{cross_forward(c.ptr)}; } void test_cross_forward() { TestContainer con; con.ptr = std::make_shared(5); static_assert( std::is_same&>, std::unique_ptr&&>::value, "oO"); static_assert( std::is_same&>, std::unique_ptr&>::value, "oO"); extract(con); extract(std::move(con)); int i = 0; ++i; } void test_incubator() { test_cross_forward(); std::function(int, float)> fn1 = detail::correctors::partial_signature_corrector::correct([](int, float) { return make_continuable(); }); std::function(int, float)> fn2 = detail::correctors::partial_signature_corrector::correct([] { return make_continuable(); }); }