/* +------------------------------------------------------+ ____/ \____ /| - Open source game framework licensed to freely use, | \ / / | copy, modify and sell without restriction | +--\ ^__^ /--+ | | | ~/ \~ | | - created for | | ~~~~~~~~~~~~ | +------------------------------------------------------+ | SPACE ~~~~~ | / | ~~~~~~~ BOX |/ +--------------+ A switch object contains a state of either on or off which can be read by reading the switch in a bool context. A switch can be flipped to change the state. If a reaction function has been set, it will run every time the switch is flipped. The function must accept a bool as its first argument which will provide the state of the switch. It can also accept an arbitrary number of arguments after that and can return a value. This example will flip the switch three times and print the state each time. sb::Switch<> sw1; sw1.on_state_change([](bool state) { std::cout << "Door is " << (state ? "OPEN" : "CLOSED") << std::endl; }); sw1.flip(); sw1.flip(); sw1.flip(); The expected output is Door is OPEN Door is CLOSED Door is OPEN This example ignores the switch state and adds an arbitrary amount to a running tally each flip. sb::Switch sw2; sw2.on_state_change([](bool state, int tally, int add) -> int { tally += add; std::cout << "Adding " << add << " to tally: " << tally << std::endl; return tally; }); int tally = 0; tally = sw2.flip(tally, 1); tally = sw2.flip(tally, 5); tally = sw2.flip(tally, 99); The expected output is Adding 1 to tally: 1 Adding 5 to tally: 6 Adding 99 to tally: 105 Although the state can just be ignored, it can also be forced to remain in one state. This example uses the `&` operator to allow access to the switch itself so it can be turned off automatically after every flip. sb::Switch sw3; sw3.on_state_change([&](bool state, const std::string& visitor) { if (state) { std::cout << "Come in " << visitor << ", my door is always " << (state ? "OPEN" : "CLOSED") << std::endl; sw3.flip(""); } }); sw3.flip("Mario"); sw3.flip("Luigi"); sw3.flip("Toad"); The expected output is Come in Mario, my door is always OPEN Come in Luigi, my door is always OPEN Come in Toad, my door is always OPEN */ #pragma once #include #include namespace sb { template class Switch { private: using Reaction = std::function; bool state = false; Reaction reaction; public: /*! * The switch object will initialized to the off state. A reaction function can be specified, otherwise it will have an * empty reaction function. * * @param callback function to run on state change */ Switch(Reaction reaction = Reaction()) : reaction(reaction) {} Switch(bool state, Reaction reaction = Reaction()) : state(state), reaction(reaction) {} /*! * Toggle state, triggering the reaction function. * * @param args arbitrary list of arguments that are expected by the reaction function * @return return the return value of the reaction function */ return_type flip(arguments... args) { state = !state; return reaction(state, args...); } /*! * Assign the reaction function. * * @param reaction function to be called when the state is changed */ void on_state_change(Reaction reaction) { this->reaction = reaction; } /*! * @return when called as a boolean, return state */ operator bool() const { return state; } /*! * Assign the switch object's state using the assignment operator with a boolean value, without triggering the reaction function. * To trigger the reaction function, use `Switch::flip` instead. * * @param state state the switch will be set to */ Switch& operator=(bool state) { this->state = state; return *this; } operator std::string() const { return state ? "true" : "false"; } }; } namespace std { template std::ostream& operator<<(std::ostream& stream, const sb::Switch& sw) { stream << std::string(sw); return stream; } }