[RFC] extending ev++.h to add C++ loop wrappers.

Marc Lehmann schmorp at schmorp.de
Wed Jan 16 05:43:07 CET 2008


On Tue, Jan 15, 2008 at 11:34:41AM -0200, Leandro Lucarella <llucax at gmail.com> wrote:
> > Hmm, the loops are not polymorphic, so the dynamic_cast will not be helpful.
> > In any case, is_default == (loop_ == ev_default_loop (0)).
> 
> They are in the patch I sent =)
> Just for the fork() and destructors.

Oh, horrible. Would it be possible to get rid of that with an explicit
(non-dynamic-cast) is_default check?

In fact, I could document and commit to ev_loop_fork and ev_loop_destroy
working on the default loop as well (they don't at the moment, but its
easy to do that), so no polymorphism is needed and all the ideas about
loops being as efficient as ev_loop pointers would then become true :)

> > If everything *is* inside ev:: that would be fine, but its quite a bit of
> > work. Otherwise, I have no hard feelings either way.
> 
> Is not that hard, is a bit of work done once (and it's already done in the
> patch ;). So, it's ok for you to expose the entire API in the ev namespace?

Absolutely.

If you could fine a nice way to hide it from the default namespace this would
be even better, but ev.h defines so many macros...

> > for various reasons, I would prefer the "_" at the end, but again, I don't
> > care much :)
> 
> I'm use to the "_" as a prefix, but I don't care much either and is you
> lib, so, I'll use the suffix form ;)

Well, the reason is that that the _-namespace is not completely usable by
apps and it is easy to stumble over reserved namespace that way, which cannot
happen with a trailing _. It doesn't matter in that case, it just explains my
general prefernce to do it that way always.

> > I wonder why it cannot be simply "loop"? Does it collide with anything?
> 
> Yes, with loop_base& loop().

How about get_loop? I am not being funny, because I would expect loop to
be either a mutator (which optionally sets it and this becoems equivalent
with a variable), or separate get/set methods.

In the end, I'd simply prefer it to be exposed, however, I am not a friend
of making getters and setters that are trivial (in my experience, either
you have a language that makes variabkles syntactically equivalent to
methods (self, javascript...) or you will not want to take the speed
hit of a nontrivial setter anyways and thus change caller code on any
changes).

I cna be convinced otherwise, though, these are just my thoughts.

> But if you think is too much trouble, I cant expose the loop (without "_")
> pointer directly.

I am mostly fine with it, if I cannot convince you with my arguments
above, we will do it as you said.

> That would only be true if fork can be a macro symbol. I don't know if
> this is actually done by any OS but the POSIX standard don't say anything
> about fork() being a macro (or even if it's implementation defined) [1] as
> it does with other symbols (FD_XXX for instance [2]).

Ha! Quoting posix on me! :)

   http://www.opengroup.org/onlinepubs/000095399/functions/xsh_chap02_01.html

   Any function declared in a header may also be implemented as a macro
   defined in the header...

> > Maybe loop_simpel should rarlly be "loop" if we can get away with it
> > without collisions.
> 
> Yes, I thought about it too, but with all that "loop" everywhere I didn't
> thought it was a great idea (exactly because the high probability of
> colission ;). But once the patch is stabilized I can try to rename it to
> loop and see what happens...

Well, I think its a wonderful idea, its a loop, it should be called loop :)

I dtetes loop_base, if we would need a seperate ype, it should probably be
loop_t (which is immensely ugly), so why not settle for "class loop" in
conflicts? :)

(See above for going with you if this isn't convincing)

> > > +  inline
> > > +  loop_default&
> > > +  default_loop (int flags)
> > > +  {
> > > +    static loop_default l (flags);
> > > +    return l;
> > > +  }
> > 
> > I my, I am scared at how horrible code that will generate in each
> > translation unit (I know, the same thing is done in ev.h, but its still
> > ugly :). Maybe leave it up to the user to declare her own loop_default
> > object? Or maybe not.
> 
> The problem with that is the lack of a "standard" mechanism to access to
> that default loop (for instance, in the watchers contructors to use it as
> a default argument).

Oh right, the default loop would detroy itself in the destructor. Maybe
we shouldn't do that (destroying the default loop, a singleton object, is
deeply anti-OO, maybe the right thing is to not do it?)

> > >      #if EV_MULTIPLICITY
> > > -      EV_P;
> > > +      loop_base *_loop;
> > 
> > Thats the only negative thing, as it creates a need to use the ev++.h loop
> > wrappers.
> 
> And that is wrong because....? I think if you are using ev++.h you'll be
> glad to use the loop wrapper =)

Sure, but its less flexible, and breaks all my C++ code :)

If we could do both, that would certainly be better, no? :)

> The problem is not uglier code in watchers. The problem is when you store
> an ev_loop*, you can't get back the loop_xxx object (unless you use some
> hash to access the object using the loop address =P). I can go back to the
> loop_ref approach, but it has its own issues, which I forgot now :S

Why would one want to go back to the loop_xxx object? I don't see any
reason to do so, as it has no state whatsoever (as all state is stored
in struct ev_loop, and either the interface is lean and simple and has
no state, or it adds something over the ev loops, which wouldn't make it
simple anymore).

> And I really think it's much more simple and elegant the new approach,

I wouldn't say its inherently more or less elegant, its different... I
am not sure wether it would be simpler: the non-virtual approahc would
create simpler objects, probably less management code and would overall be
faster.

> even when it forces you to use the loop wrapper (which you can pass to C
> API functions too, so that shouldn't be a huge problem either).

If I cna pas it to C API functions, why not also allow it to be passed
to the watcher? Whats the inherent advantage of having the same address
for the same loop pointer (consider the loop objects meerely pointers
once)? What counts is the identify of the underlying loop. i.e. your
approach could be beiwed like this:

   struct ev_loop *myloop = ...

and then pass around &myloop as *the* loop because you think it is
important that the address of all loops point to myloop, but in fact, the
only important thing is that the underlying loop is the same one (i.e. the
contents, not the address).

I agree that your approach is nice and c++'ish, but wasn't one of the
goals of ev++.h to be lean and simple and to be very close to the ev.h
API? Right now, I can mix and match the C++ with C API in my programs
whenever its convinient, eith ev++.h I have a insular kingdom where only
ev++'s loop work (for example, the problem comes up when C++ code needs
to use the loop created by some C code, consider a program written in C++
that needs to access a library that creates its own event loop. ev++.h
currently has no "nice simple and elegant" way to make that possible).

> Even more, if the default_loop_ptr is exposed, all virtuality could be
> avoided, so no overhead at all will be introduced in the loop wrapper

Yes, either that, or we allow ev_loop_destroy and ev_loop_fork for the
default loop as well: Since ev++.h is part of libev, we could even call this
a hidden implementation detail.

In fact, one could even export a ev_loop_is_default() predicate in ev.h
for that and other purposes, without exposing the internal pointer to
ev++.h (or, heaven forbid, even other parts :)

> > One would lose the ability to compare loop_base's by address, but a nice
> > operator == would do that or somesuch, if needed.
> 
> There are several problems with this. First, ev_loop struct is not
> available in ev.h =)

How is that a problem? You can still pass around pointers to it, and that is
all that is required.

> Second, I wrapped the loop not only for cosmetical purposes, I like the
> automatic destruction =). If you lost the difference between an object and
> a raw pointer, then you can't know when to call the ev_xxx_destroy()
> (unless we use reference counting, which I think is overkill for this
> case).

You can have automatic destruction by providing three types:

   ev::loop         // for storing loops
   ev::loop_dynamic // for dynamically-created loops
   ev::loop_default // for the default loop

Naming could probbaly be improved, but you need these three types anyways,
to be able to handle externally-provided loop pointers (which certainly
will come up).

> > > -    void start (int fd, int events)
> > > +    void start (int fd, int events = READ)
> > 
> > Do you really think this is such a good idea? Is READ really predominantly
> > what people want?
> 
> Is what I predominantly want =)

Well, if you think so, ok then.

> I can remove the default...

Not if you are convinced its what people predominantly want. I have as
much data on that as you (i.e. mostly my own: I have more READ than write
watchers, but I don't see why it should be the default. Think readability:

   start (fd); // waits for read events

this doesn't look intuitive when I remove the comment).

But feel fre to overrule me here again.

-- 
                The choice of a       Deliantra, the free code+content MORPG
      -----==-     _GNU_              http://www.deliantra.net
      ----==-- _       generation
      ---==---(_)__  __ ____  __      Marc Lehmann
      --==---/ / _ \/ // /\ \/ /      pcg at goof.com
      -=====/_/_//_/\_,_/ /_/\_\



More information about the libev mailing list