how to use Timer ?

Started by
8 comments, last by donjonson 19 years ago
I try to write Text-Base RGP (console application), and i want every 5 seconds my character HP(hit point) increase +1. it would be nice if you include source code. Thanks you in advance.
DinGY
Yesterday is history.Tomorrow is a mystery. Today is a gift"
Advertisement
Off the top of my head. Should print "foo." on a line every 5 seconds. Visual C++ doesn't like the templated recursion, so I can't test it myself :/

Still, there's probably more elegant ways than this, even on these forums. Take a look around. Timers are a common topic.

#include <vector>#include <iostream>using namespace std;struct   print_foo{        void    operator()(){                cout << "foo.\n";        }};template <typename F>struct time_triggered_repeating_event{       long    counter;       long    reset;       F       func;       void    trigger(){              while (counter < 0){                      func();                      counter=counter+reset;                      trigger();              }       }       time_triggered_repeating_event(DWORD  per_time):counter(0),reset(per_time){}};struct  modify_and_trigger{       DWORD      time_ptr;       DWORD      time_difference;       void      operator()(time_triggered_repeating_event &event){               event.counter=event.counter-time_difference;               event.trigger();       }       modify_and_trigger(DWORD intime):time_difference(0),time_ptr(intime){}       void       update(DWORD newtime){                time_difference=newtime-time_ptr;               time_ptr=newtime;       }};       int    main(){modify_and_trigger                                 time_tracker(GetTickCount());vector<time_triggered_repeating_event<print_foo> > timers;time_triggered_repeating_event<print_foo>          five_second_foo(5000);timers.push_back(five_second_foo);while (1){       time_tracker.update(GetTickCount());       for_each(timers.begin(),timers.end(),time_tracker);}}


[edit: actually, the time_triggered_repeating_event should probably take an optional object F to copy in rather than the default object... ]
Good solution!
Maybe add a Sleep(1) so your CPU usage doesn't go berserk if that's the only thing the loop does.

Of course, care has to be taken not halt the loop in a cin >> userinput; (or scanf() for the C guys) since it would pause the timer until the user has made some input. Don't know it from the top of my head, but there's probably some kbhit() equivalent which allows you to check whethere there are waiting keystrokes in the input buffer that you can use to only read input from the console when the user actually has typed something.

-Markus-
Professional C++ and .NET developer trying to break into indie game development.
Follow my progress: http://blog.nuclex-games.com/ or Twitter - Topics: Ogre3D, Blender, game architecture tips & code snippets.
Thanks Telastyn for fast reply, but there was error in your code.
DinGY
Yesterday is history.Tomorrow is a mystery. Today is a gift"
Here's a cooperative single-threaded scheduler for you.


Scheduler.cc
#include "Scheduler.h"#include <sys/time.h>#include <deque>#include <algorithm>#include <functional>#include <iostream>//////////////////////////////////////////////////////////////////////namespace{   struct EventPtrCompare :      std::binary_function<Event*, Event*, bool>   {      bool operator()(Event* lhs, Event* rhs) const      {         return rhs->time < lhs->time;      }   };   struct EventPtrDelete :      std::unary_function<Event*, void>   {      void operator()(Event* e) const { delete e; }   };}//////////////////////////////////////////////////////////////////////namespace{   const EventTime max_time = (EventTime)std::numeric_limits<time_t>::max() +                              (EventTime)std::numeric_limits<suseconds_t>::max() * 1e-6;   const EventTime min_time = (EventTime)std::numeric_limits<time_t>::min() +                              (EventTime)std::numeric_limits<suseconds_t>::min() * 1e-6;   void advance_time(EventTime& time, EventTime adj)   {      if(time + adj > max_time)      {         time -= (max_time - min_time);         time += adj _time;      }      else         time += adj;   }   EventTime get_time()   {      timeval time;      gettimeofday(&time, 0);      return (EventTime)time.tv_sec + 1e-6 * time.tv_usec;   }}Scheduler::Scheduler() :   last_run(get_time()){}Scheduler::~Scheduler(){   for_each(queue.begin(), queue.end(), EventPtrDelete());   for_each(overflow.begin(), overflow.end(), EventPtrDelete());}void Scheduler::schedule(void (*action)() ){   Event e = { action, get_time(), 0, 0 };   schedule( new Event(e) );}void Scheduler::schedule(Event e){   if (e.time == 0) e.time = get_time();   schedule( new Event(e) );}void Scheduler::schedule(Event* e){   queue_t& q = (e->time < last_run) ? overflow : queue;   // insert in the appropriate queue   q.push_back(e);   std::push_heap(q.begin(), q.end(), EventPtrCompare());}void Scheduler::process(EventTime now){   Event* e = queue.front();   std::cerr << '(' << now << ") \t"             << (void*)e->action << ' '             << e->time << ' '             << e->count << ' '             << e->period << "\t";   (e->action)();   // Remove from queue   std::pop_heap(queue.begin(), queue.end(), EventPtrCompare());   queue.pop_back();   if(e->count > 0)   {      --e->count;      // Dead, don't reschedule      if(e->count == 0)      {         delete e;         return;      }   }   if(e->period == 0)      e->time = now;   else      advance_time(e->time, e->period);   std::cerr << (void*)e->action << ' '             << e->time << ' '             << e->count << ' '             << e->period << std::endl;   // Reinsert in queue   schedule(e);}void Scheduler::run(){    EventTime now = get_time();    if(now<last_run)  // Timer wrap-around    {       while( !queue.empty() )          process(now);       queue.swap(overflow);    }    while( !queue.empty() &&            queue.front()->time <= now )       process(now);    last_run = now;}



Scheduler.h
#include <deque>typedef double EventTime;struct Event{   void (*action)();   EventTime time;   unsigned long count;   EventTime period;};class Scheduler{   // not using q priority queue because I want to be able to swap them   typedef std::deque<Event*> queue_t;   queue_t queue;   queue_t overflow;   EventTime last_run;public:   Scheduler();  ~Scheduler();public:   void schedule(void (*action)());   void schedule(Event event);   void run();private:   void schedule(Event* event);   void process(EventTime now);private:   // Scheduler object is non-copyable   Scheduler(const Scheduler&);   Scheduler& operator=(const Scheduler);};



foo.cc
#include "Scheduler.h"#include <iostream>#include <iomanip>#include <string>Scheduler scheduler;int hp = 0;const int max_hp = 50;void hpregen(){   hp = std::min( hp + 5, max_hp );}Event hpregen_event = { hpregen, 0, 0, 5 };void read_command(std::string line){   std::cout << "HP: " << hp << "/" << max_hp << " >";   std::getline( std::cin, line );}void execute_command(const std::string& line){   std::cout << "You have typed \"" << line << "\"" << std::endl;}int main(){   scheduler.schedule( hpregen_event );   std::string line;   while( true )   {      read_command(line);      scheduler.run();      execute_command(line);      scheduler.run();   }}


The timer ticks even when the program is waiting on input though, since it is single threaded, the events are only processed when you call run(), at which point all the past due events are rushed. So, for example, if you wait 20 seconds before pressing return, the hpregen() function will be called 3 or 4 times in a row (depending on how the events fall in the period).

The parameters of the Event structure are: function to call, delay until first call (seconds), number of times the even is scheduled (0 = infinite), delay until rescheduling (0 = "now").

Also, note that I use the Linux gettimeofday() timer. If you're a Windows user, switch to timeGetTime() or QueryPerformanceCounter().

I also deal with timer overflow. Correctly, I hope. [smile]
"Debugging is twice as hard as writing the code in the first place. Therefore, if you write the code as cleverly as possible, you are, by definition, not smart enough to debug it." — Brian W. Kernighan
Quote:Original post by invisal
Thanks Telastyn for fast reply, but there was error in your code.



Eh, yeah, I think for_each requires a header I didn't specify. Still, Fruny's solution looks more complete.

[edit: cygon: Or use select() as the triggering mechanism]
thanks for your answer :)
DinGY
Yesterday is history.Tomorrow is a mystery. Today is a gift"
could someone explain this code for me?
struct   print_foo{        void    operator()(){                cout << "foo.\n";        }};/*I understand struct, and I think I understand overloading operators but what this code does, I dont understand*/


if you could explain it that would be great
These tears..leave scars...as they run down my face.
Quote:Original post by donjonson
could someone explain this code for me?
*** Source Snippet Removed ***

if you could explain it that would be great



Certainly!

So this is a "functor". That's the keyword for searches, and the term most folks will use.

What this code does is declare a structure [or a type if you prefer] which behaves like a function. Just like classes, it's just a type, so you can't call print_foo() and expect it to print "foo". You need to actually make an object:

print_foo      a;print_foo      b;


a() will then print foo. As will b().

This in and of itself isn't too astounding. Consider this code though:

struct  print_something{        string s;        void    operator()(){                cout << s << endl;        }        print_something(const char *c):s(c){}};


Now the functor has a constructor. It also contains a state [the string]. This way you can store parameters to a function in an instance of the function. For example:

print_something       a("foo");print_something       b("bar");


Now, running a() will print foo, but running b() will print bar. These objects a and b can be passed around like any other object. Something like this is commonly used in UI code to store "actions" for when buttons are clicked. Also, since functors are just normal objects, they work well with templates.
wow that is pretty awsome, I like that!
These tears..leave scars...as they run down my face.

This topic is closed to new replies.

Advertisement