Leandro Lucarella, el 14 de enero a las 16:32 me escribiste:
> > 1) Storing a ev_loop* in the watchers, instead of a loop_ref. But I'm
> > affraid that not doing so could break some existing code. This leads to
> > the ugly "ev::loop_ref (w.loop).stop (ev::ALL);" in the example (if a
> > loop_ref could be stored, it looked like "w.loop.stop (ev::ALL);")
>
> To implement this point, 2) is necessary if we don't want to be needed to
> explicitly set every watcher's loop, because the lack of a "standard" way
> to get the default loop.
>
> > 2) The default loop handling. Now a default loop has to be instantiated,
> > and a simple check is done to ensure just one default loop exists, but
> > with an ugly hack (via de default_loop_created() function) to avoid the
> > need of object code (a class static variable would need it).
> > This model is a little different to the C API, because you can't get
> > the default loop from anywhere (at least not in the same way, but you
> > can do it with something like loop_ref(ev_default_loop(0)), but it's
> > not very nice). The easiest way to do that is making the default_loop a
> > global variable, but it's a "userspace" hack.
> > I thought of making a more singleton-approach (using a function to
> > get/create the loop object if it doesn't exist, for example
> > "get_default_loop(int flags = 0) { static default_loop* l = 0; if (!l) l
> > =
> > new default_loop(flags); return l; }" but this way you have to
> > explicitly free the default loop). Now that I see this, maybe it could
> > be "elegantly" solved using a reference:
> > default_loop&
> > get_default_loop(int flags = 0)
> > {
> > static default_loop l(flags);
> > return l;
> > }
> > (class names could be rearranged to name this funtion default_loop()
> > and the class loop_default, for example, to be more C API-consistent)
>
> Here's patch that implement this scheme (over the original patch). Even if
> it's nice to stay close to the C API and to avoid the posibility of
> instantiating 2 default loops from the root, I think the usage is a little
> less intuitive in a C++ environment.
Third iteration =)
Main changes:
* 1) and 2) has been implemented.
* Now the loops methods to start and stop the loop match the C API (loop()
and unloop()).
* Some bugfixes.
* loop_ref is renamed to loop_base and it's an abstract class (not a
"pointer wrapper"). It's non-copiable since now loop_base is not
instantiable.
* Watchers store a loop_base pointer instead of an ev_loop pointer.
* Some readonly attributes are now private/protected, providing accessor
methods (e.g. w.loop()).
* is_default was removed from loop_base, since now the class matches the
type of loop (is_default == dynamic_cast< loop_default* >(loop_ptr)).
* Add a lot of ev_once () flavors to loop_base.
* Add feed_event () to base watcher, and feed_fd_event () and
feed_signal_event () loop_base.
A new patch (with all the changes) and a new example (now with a timer
watcher) are attached. Again, this is a rough patch, if you like it,
cleaning and documentation will follow =)
Thank you!
--
Leandro Lucarella (luca) | Blog colectivo: http://www.mazziblog.com.ar/blog/
----------------------------------------------------------------------------
GPG Key: 5F5A8D05 (F8CD F9A7 BF00 5431 4145 104C 949E BFB6 5F5A 8D05)
----------------------------------------------------------------------------
La esperanza es una amiga que nos presta la ilusión.
diff --git a/ev++.h b/ev++.h
index e447998..93644f2 100644
--- a/ev++.h
+++ b/ev++.h
@@ -45,18 +45,318 @@
#else
# include <ev.h>
#endif
+#include <stdexcept>
namespace ev {
+ typedef ev_tstamp tstamp;
+
+ enum
+ {
+ AUTO = EVFLAG_AUTO,
+ NOENV = EVFLAG_NOENV,
+ FORKCHECK = EVFLAG_FORKCHECK,
+ SELECT = EVBACKEND_SELECT,
+ POLL = EVBACKEND_POLL,
+ EPOLL = EVBACKEND_EPOLL,
+ KQUEUE = EVBACKEND_KQUEUE,
+ DEVPOLL = EVBACKEND_DEVPOLL,
+ PORT = EVBACKEND_PORT
+ };
+
+ enum
+ {
+ NONBLOCK = EVLOOP_NONBLOCK,
+ ONESHOT = EVLOOP_ONESHOT
+ };
+
+ enum how_t
+ {
+ ONE = EVUNLOOP_ONE,
+ ALL = EVUNLOOP_ALL
+ };
+
+ struct bad_loop: std::runtime_error
+ {
+ bad_loop()
+ : std::runtime_error("loop can't be initialized") {}
+ };
+
+#if EV_MULTIPLICITY
+# define EV_AX _loop
+# define EV_AX_ _loop,
+#else
+# define EV_AX
+# define EV_AX_ ,
+#endif
+
+ struct loop_base
+ {
+
+#if EV_MULTIPLICITY
+ operator struct ev_loop * () const throw ()
+ {
+ return _loop;
+ }
+
+ operator const struct ev_loop * () const throw ()
+ {
+ return _loop;
+ }
+
+ virtual
+#endif
+ ~loop_base () throw ()
+ {
+ }
+
+ void loop (int flags = 0) throw ()
+ {
+ ev_loop (EV_AX_ flags);
+ }
+
+ void unloop (how_t how = ALL) throw ()
+ {
+ ev_unloop (EV_AX_ how);
+ }
+
+ virtual void fork () throw () = 0;
+
+ unsigned int count () const throw ()
+ {
+ return ev_loop_count (EV_AX);
+ }
+
+ unsigned int backend () const throw ()
+ {
+ return ev_backend (EV_AX);
+ }
+
+ tstamp now () const throw ()
+ {
+ return ev_now (EV_AX);
+ }
+
+ void ref () throw ()
+ {
+ ev_ref (EV_AX);
+ }
+
+ void unref () throw ()
+ {
+ ev_unref (EV_AX);
+ }
+
+ void set_io_collect_interval (tstamp interval) throw ()
+ {
+ ev_set_io_collect_interval (EV_AX_ interval);
+ }
+
+ void set_timeout_collect_interval (tstamp interval) throw ()
+ {
+ ev_set_timeout_collect_interval (EV_AX_ interval);
+ }
+
+ // function callback
+ void once (int fd, int events, tstamp timeout, void (*cb)(int, void *), void* arg = 0)
+ {
+ ev_once (EV_AX_ fd, events, timeout, cb, arg);
+ }
+
+ // method callback
+ template<class K, void (K::*method)(int)>
+ void once (int fd, int events, tstamp timeout, K *object)
+ {
+ once (fd, events, timeout, method_thunk<K, method>, object);
+ }
+
+ template<class K, void (K::*method)(int)>
+ static void method_thunk (int revents, void* arg)
+ {
+ K *obj = static_cast<K *>(arg);
+ (obj->*method) (revents);
+ }
+
+ // const method callback
+ template<class K, void (K::*method)(int) const>
+ void once (int fd, int events, tstamp timeout, const K *object)
+ {
+ once (fd, events, timeout, const_method_thunk<K, method>, object);
+ }
+
+ template<class K, void (K::*method)(int) const>
+ static void const_method_thunk (int revents, void* arg)
+ {
+ K *obj = static_cast<K *>(arg);
+ (obj->*method) (revents);
+ }
+
+ // simple method callback
+ template<class K, void (K::*method)()>
+ void once (int fd, int events, tstamp timeout, K *object)
+ {
+ once (fd, events, timeout, method_noargs_thunk<K, method>, object);
+ }
+
+ template<class K, void (K::*method)()>
+ static void method_noargs_thunk (int revents, void* arg)
+ {
+ K *obj = static_cast<K *>(arg);
+ (obj->*method) ();
+ }
+
+ // simpler function callback
+ template<void (*cb)(int)>
+ void once (int fd, int events, tstamp timeout)
+ {
+ once (fd, events, timeout, simpler_func_thunk<cb>);
+ }
+
+ template<void (*cb)(int)>
+ static void simpler_func_thunk (int revents, void* arg)
+ {
+ (*cb) (revents);
+ }
+
+ // simplest function callback
+ template<void (*cb)()>
+ void once (int fd, int events, tstamp timeout)
+ {
+ once (fd, events, timeout, simplest_func_thunk<cb>);
+ }
+
+ template<void (*cb)()>
+ static void simplest_func_thunk (int revents, void* arg)
+ {
+ (*cb) ();
+ }
+
+ void feed_fd_event (int fd, int revents)
+ {
+ ev_feed_fd_event (EV_AX_ fd, revents);
+ }
+
+ void feed_signal_event (int signum)
+ {
+ ev_feed_signal_event (EV_AX_ signum);
+ }
+
+ protected:
+
+#if EV_MULTIPLICITY
+ struct ev_loop* _loop;
+#endif
+
+ loop_base (EV_P) throw (bad_loop)
+#if EV_MULTIPLICITY
+ : _loop (EV_A)
+#endif
+ {
+ if (!EV_A)
+ throw bad_loop ();
+ }
+
+ private:
+
+ loop_base (const loop_base &);
+
+ loop_base & operator= (const loop_base &);
+
+ };
+
+#if EV_MULTIPLICITY
+ struct loop_simple: loop_base
+ {
+
+ loop_simple (unsigned int flags = AUTO) throw (bad_loop)
+ : loop_base (ev_loop_new (flags))
+ {
+ }
+
+ ~loop_simple () throw ()
+ {
+ ev_loop_destroy (_loop);
+ }
+
+ void fork () throw ()
+ {
+ ev_loop_fork (_loop);
+ }
+
+ private:
+
+ loop_simple (const loop_simple &);
+
+ loop_simple & operator= (const loop_simple &);
+
+ };
+#endif
+
+ struct loop_default;
+
+ loop_default&
+ default_loop (int flags = 0);
+
+ struct loop_default: loop_base
+ {
+
+ ~loop_default () throw ()
+ {
+ ev_default_destroy ();
+ }
+
+ void fork () throw ()
+ {
+ ev_default_fork ();
+ }
+
+ protected:
+
+ loop_default (unsigned int flags = AUTO) throw (bad_loop)
+ : loop_base (ev_default_loop (flags))
+ {
+ }
+
+ private:
+
+ loop_default (const loop_default &);
+
+ loop_default & operator= (const loop_default &);
+
+ friend loop_default& default_loop (int flags);
+
+ };
+
+ inline
+ loop_default&
+ default_loop (int flags)
+ {
+ static loop_default l (flags);
+ return l;
+ }
+
+ #undef EV_AX
+ #undef EV_AX_
+
template<class ev_watcher, class watcher>
struct base : ev_watcher
{
#if EV_MULTIPLICITY
- EV_P;
+ loop_base *_loop;
+
+ void set (loop_base &loop) throw ()
+ {
+ _loop = &loop;
+ }
+
+ loop_base& loop () throw ()
+ {
+ return *_loop;
+ }
- void set (EV_P)
+ const loop_base& loop () const throw ()
{
- this->EV_A = EV_A;
+ return *_loop;
}
#endif
@@ -141,6 +441,11 @@ namespace ev {
{
return ev_is_pending (static_cast<const ev_watcher *>(this));
}
+
+ void feed_event (int revents)
+ {
+ ev_feed_event (loop (), static_cast<const ev_watcher *>(this), revents);
+ }
};
enum {
@@ -161,24 +466,66 @@ namespace ev {
ERROR = EV_ERROR,
};
- typedef ev_tstamp tstamp;
+ inline tstamp time ()
+ {
+ return ev_time ();
+ }
+
+ inline void sleep (tstamp interval)
+ {
+ ev_sleep (interval);
+ }
+
+ inline int version_major ()
+ {
+ return ev_version_major ();
+ }
+
+ inline int version_minor ()
+ {
+ return ev_version_minor ();
+ }
- inline ev_tstamp now (EV_P)
+ inline unsigned int supported_backends ()
{
- return ev_now (EV_A);
+ return ev_supported_backends ();
+ }
+
+ inline unsigned int recommended_backends ()
+ {
+ return ev_recommended_backends ();
+ }
+
+ inline unsigned int embeddable_backends ()
+ {
+ return ev_embeddable_backends ();
+ }
+
+ inline void set_allocator (void *(*cb)(void *ptr, long size))
+ {
+ ev_set_allocator (cb);
+ }
+
+ inline void set_syserr_cb (void (*cb)(const char *msg))
+ {
+ ev_set_syserr_cb (cb);
}
#if EV_MULTIPLICITY
#define EV_CONSTRUCT \
- (EV_P = EV_DEFAULT) \
+ (loop_base& loop = default_loop ()) \
{ \
- set (EV_A); \
+ set (loop); \
}
+ #define EV_AX EV_A ()
+ #define EV_AX_ EV_A (),
#else
#define EV_CONSTRUCT \
() \
{ \
}
+ #define EV_AX
+ #define EV_AX_ ,
#endif
/* using a template here would require quite a bit more lines,
@@ -189,12 +536,12 @@ namespace ev {
{ \
void start () \
{ \
- ev_ ## cstem ## _start (EV_A_ static_cast<ev_ ## cstem *>(this)); \
+ ev_ ## cstem ## _start (EV_AX_ static_cast<ev_ ## cstem *>(this)); \
} \
\
void stop () \
{ \
- ev_ ## cstem ## _stop (EV_A_ static_cast<ev_ ## cstem *>(this)); \
+ ev_ ## cstem ## _stop (EV_AX_ static_cast<ev_ ## cstem *>(this)); \
} \
\
cppstem EV_CONSTRUCT \
@@ -235,7 +582,7 @@ namespace ev {
if (active) start ();
}
- void start (int fd, int events)
+ void start (int fd, int events = READ)
{
set (fd, events);
start ();
@@ -259,7 +606,7 @@ namespace ev {
void again ()
{
- ev_timer_again (EV_A_ static_cast<ev_timer *>(this));
+ ev_timer_again (EV_AX_ static_cast<ev_timer *>(this));
}
EV_END_WATCHER (timer, timer)
@@ -281,7 +628,7 @@ namespace ev {
void again ()
{
- ev_periodic_again (EV_A_ static_cast<ev_periodic *>(this));
+ ev_periodic_again (EV_AX_ static_cast<ev_periodic *>(this));
}
EV_END_WATCHER (periodic, periodic)
#endif
@@ -337,7 +684,7 @@ namespace ev {
void update ()
{
- ev_stat_stat (EV_A_ static_cast<ev_stat *>(this));
+ ev_stat_stat (EV_AX_ static_cast<ev_stat *>(this));
}
EV_END_WATCHER (stat, stat)
#endif
@@ -365,7 +712,7 @@ namespace ev {
void sweep ()
{
- ev_embed_sweep (EV_A_ static_cast<ev_embed *>(this));
+ ev_embed_sweep (EV_AX_ static_cast<ev_embed *>(this));
}
EV_END_WATCHER (embed, embed)
#endif
@@ -377,8 +724,11 @@ namespace ev {
#endif
#undef EV_CONSTRUCT
+ #undef EV_AX
+ #undef EV_AX_
#undef EV_BEGIN_WATCHER
#undef EV_END_WATCHER
+
}
#endif
#include <ev++.h>
#include <string.h>
#include <stdio.h>
#include <unistd.h>
#include <fcntl.h>
/* called when data readable on stdin */
struct myclass
{
void io_cb (ev::io &w, int revents)
{
printf ("stdin ready\n");
char buff[BUFSIZ];
buff[0] = '\0';
int r = read (w.fd, buff, BUFSIZ);
printf ("%d bytes read: %s", r, buff);
w.stop (); /* just a syntax example */
// same as w.loop ().unloop (ev::ONE); in this example
ev::default_loop ().unloop (ev::ONE); /* leave one loop call */
}
void
timeout_cb (ev::timer &w, int revents)
{
printf ("timeout!\n");
// simulate activity on STDIN_FILENO
ev::default_loop ().feed_fd_event (0, ev::READ);
}
};
int
main (void)
{
// If we don't initialize the default loop, it still works.
// Is this expected behaviour?
//ev::default_loop ();
int flags;
if ((flags = fcntl (0, F_GETFL, 0)) < 0)
perror ("fcntl F_GETFL");
if (fcntl (0, F_SETFL, flags | O_NONBLOCK) < 0)
perror ("fcntl F_SETFL");
myclass obj;
// io event
ev::io iow;
iow.set <myclass, &myclass::io_cb> (&obj);
/* start watching fd 0 for reading (default) */
iow.start (/*STDIN_FILENO*/ 0);
// timeout event
ev::timer tw (ev::default_loop ()); // just to show how to pass a loop
tw.set <myclass, &myclass::timeout_cb> (&obj);
/* simple non-repeating 5.5 second timeout */
tw.start (5.5);
/* loop till timeout or data ready */
ev::default_loop ().loop ();
return 0;
}
_______________________________________________
libev mailing list
[email protected]
http://lists.schmorp.de/cgi-bin/mailman/listinfo/libev