|
Boost : |
From: Tomasz Kowalczyk (tomek_at_[hidden])
Date: 2000-04-14 23:41:48
Thank you everybody for feedback.
I guess that I have posted the library a little too early. As I can see
there are still lots of issues that would have to be addressed before a
library like this can be shown to the public (including PR as some have
mentioned :)
Karl Nelson wrote:
>
> This letter isn't meant as a put down, but rather to inform
> that there are far more issues than brought up here. If boost
> is really serious about getting a signal slot system, there
> is a huge number of trade offs.
Currently, it is not boost that is serious, but me :) I am sure that it
is possible to design the interface in such a way that the trade offs
you are talking about in this post can be avoided.
> I have discussed dozens of systems that people have written
> either before seeing libsigc++ or inspired by it. There are
> a number of tradeoffs that are not apparent without
> a lot of discussion. This is by no means a complete list or
> the issues involved. I am always happy to discuss alternative
> design choices on my mailing list.
I guess that it may be a better place to discuss these matters. Does
"your" mailing list mean libsigc++ mailing list ?
> I read the proposed system because it ICEd my 2.95.2 compiler.
> Thus I can't be sure of all my info as accurate (I may misread
> code.) Also this means I couldn't run my torture suite against
> it to see if it handles all the cases.
Believe me, I am really ashamed :( It appears that egcs 1.1.2 is not the
best choice when you want to write portable code ... Meanwhile I have
updated the library, and compiled on egcs 1.1.2, gcc 2.95 and SGI's
MIPSpro 7.2.1 (I am not very happy about it because to compile with
MIPSpro I had to write some ugly code). Care to run your torture suite
... :)
That's a pity but neither Visual C++ 6.0 nor Borland free compiler
handles the code. Still, I don't think this is really my fault. When
supporting crappy compilers you should draw a line at some place. I
think that it is quite reasonable to expect from the compiler to handle
class template specializations correctly, and this is where my line
goes. Still, without proper template partial ordering (I hope it's the
right name ... not sure now) it is very difficult to write interesting
code.
Libsigc++ aims at an important goal of portability among compilers
available in year 1999. Because of this however, it is forced to live
with many tradeoffs. If these tradeoffs were present only in the
implementation it wouldn't matter. However, I can see a few interface
design issues, which make this library not as general as it could be.
When you place more demanding expectations on your compiler, it is very
likely that your are going to end up with more elegant and more general
interface.
I would also like to point out, that because of different aims libsigc++
and the stuff I have written address, it is not very meaningful to
compare them. Libsigc++ is a heavily used production system, a proof
that signals can be implemented efficiently. On the other hand, I am
attempting to create better (more general, easier to use) interface for
things related to the signal idiom.
> [...]
>
> > You might consider some reasons, to name a few:
> > - doing approximately the same, is about 5 times smaller,
> > also unlike libsigc:
>
> 5 times the source != 5 times larger.
5 times the source means 5 times the source. Approximately 5 x 5 times
easier to understand and to maintain. A very important feature when
trying to land with a consistent interface.
> Libsigc++ was written
> with a lot of portablity, 2.5 times the callbacks, several entire
> adaptor sets you don't even have.
This is one of design issues with libsigc++ I can see. You have to write
adaptors.
> Further, libsigc++ was
> optimizing for binary size and memory size and the expense
> of code size.
This is good. I agree.
> > - is exception safe,
>
> Libsigc++ isn't intentionally exception unsafe. If it
> has a problem it is fixable.
Exception safety is a very important issue in an interface of any
library. When you write a GUI program, you may not care what happens
when you get out of memory, but this is not necessary true in general
case (libsigc++ aims to be a general-use library).
Because of its complexity I wasn't able to follow libsigc++ code and
actually find any examples of exception unsafety in more important case
than lack of memory i.e. when callbacks or user-defined constructors or
assignments throw. Fortunately for library critics, unless a library is
guaranteed exception safe we can call it "not exception safe" ;)
If you start trying to check the exception safety, you might appreciate
the small size and simplicity of your code. I can give you
exception-related semantics for each function in signal, and guarantee
that any thrown exception will leave the signal in a valid state. After
these things have been fixed, you can proceed to optimize your code in
such a way not to break the interface contracts.
>
> > - contains headers only, there is no need for linking,
>
> This is by far more of a pitfall than a feature. Libsigc++
> uses template factorization which pushes much of the code
> into a shared library. Excessive inlining such as your proposal
> uses is quite bad.
Could you elaborate please why you find it excessive ? Almost all
functions are 1,2 liners, which completely depend on the arguments of
the templates they were instantiated in. Even if you could extract some
common code and push it into the library, I consider it an
implementation detail, which has a drawback of forcing you to maintain
additional library to link with. And moreover, you really have no
guarantee that the code indeed will be smaller.
Compilation and installation, and even more maintanance of a library and
programs linked with it is a non-negligble effort. Usually separate
instalation is necessary for each compiler, or even each compiler
version, and keeping track of all that is very likely to cause you
maintanance headaches.
>
> Further, you mad massive use of virtuals within templates.
> Since the dtor for a virtual indicates the location of
> typeinfo and other structures then it will place all of
> that info in every object file. On many systems this will
> result in vastly large binaries. (Yes, they should strip
> out redundant typeinfo and virtual tables, but some don't.)
>
> Libsigc++ uses no virtuals to implement callbacks to avoid
> the problem. It is a feature.
>
I agree that a production implementation should address this issue.
> > - allows to easily connect to a signal anything that can be called
> as a
> > function,
>
> Libsigc++ also has this. Also libsigc++ is entirely open
> so adding new functor types is a breeze.
Do you mean that I can use STL binders or lambda library to generate
some functor and easily use it anonymously as a slot ?
> > - allows to copy signal objects - you can store them in a container
> for
> > example,
>
> Copying signals is a huge dispute on the libsigc++ mailing list.
> There is no clear rule to which a signal should be copied. For
> example, signal systems set up parent/child relationships, such
> as a signal in a class. If a slot connects a signal to its
> parents methods and is copied with a new parent should the slots
> point to the old or the new? How would a copy screen this
> out?
If you are saying that you have been discussing it in a long thread,
then I can only believe you about complexity of this problem. I am not
convinced though, since copying of a signal is roughly equivalent to
copying of a pointer. If you have pointers to itself in some object, you
have to adjust them by hand after the object was copied ... There is a
simple rule to follow: "don't pay for what you don't use".
> > - avoids circular references by not using reference counting for
> signal
> > receivers,
>
> Signals in libsigc++ don't reference anything other than the slot
> internals. You are confusing the handle/manage() system which was
> an experiment/requirement for Gtk--.
Sorry. My mistake. I should have looked more closely in the code.
> > - has no casts in the implementation,
>
> No casts force virtuals into the library. Where is it written
> that all good code shouldn't have any casting? Casting is
> commonly used in templates to share internals and implementation
> and can be done safely and portably.
Excessive casting is usually a sign of bad design. Casts can be forgiven
in libraries interfacing to old code or highly optimized ones. Libsigc++
belongs to the second category thus some might be tempted to accept the
way it is implemented, but some may still look at it suspiciously. (I
was hoping the post asking about the reasons to use my code over libsigc
belonged like me to the second category :)
> > - uses standard library where appropriate,
>
> By design libsigc++ depends on nothing, not even STL. Since
> STL isn't used in many systems this is a feature. If an when
> the quality of STL implementations rises to universal acceptance
> and use libsigc++ could be made to use it. Also libsigc++ has
> polymorphic list elements which really can't be represented by
> STL, so much of it was necessary.
>
> > - makes writing generic code easier by using consistent naming
> > (signal<>, signal<A> vs Signal0<>, Signal1<A> etc.)
>
> Libsigc++ is consistant with Gtk-- which is consistant with
> Gtk+. That is the cost of starting with one project.
>
> Not having the number means really poor error messages. Good
> errors were considered worth the effort. Also your system
> completely rules out marshallers and expansion by using
> that system.
I suspect that the numbers are more the result of including a marshaller
in the signal template argument list than the consistency with Gtk :) I
like your reasoning but still names with numbers attached to them are
ugly ( remember not1, bind2nd ? ... brrrr, why not bind3rd for the
goodness sake !!!? )
And the main reason above all is that C++ does not allow you to treat
the argument list as a whole.
Btw. have you seen lambda library ? THAT's a BEAST :)
>
> Gtk-- requires up to 6 arguments with marshallers and return
> codes. Six arguments would cause yours to give totally
> unreadable error messages with signal<foo,void,void,void,void,void>
> Marshallers require optional arguments which would force the
> user to type out all those voids. And if you need one more
> signal/slot argument it is impossible to do with your system.
>
> > To be fair, I have to write what you cannot find in my library:
> > - signals returning values
> > - user-defined marshaling of arguments
>
> > First was a design decision. Second was a tradeoff, and can be added
> if
> > needed.
>
> You missed portablity. Libsigc++ has a portablity in the template
> realm which is unparalleled. It can be ported to gcc 2.7.2 and
> Visual C++ 5.0 and still make full use of the library.
You are right. I can only hope that portability problem dissapears in
1,2 years :)
> Your proposed system doesn't come close to touching what is
> required for Gtk--, a vast and complicated library built on
> signal/slot concepts. Thus I think your system is more of a
> toy than what could be used in a standard.
You are joking about the standard, right :) ? Of course it is a toy, but
I hope to make something useful out of it.
I really appreciated your comments. Thanks.
Tomasz
> Now I am not saying that yours should be rejected from boost, (I
> am not at all associated with the project) merely there is far
> more to consider. I have talked to tons of people who read through
> libsigc++ code, felt it was too complicated and sent me a reduced
> system of one type or another. However, all thus far have become
> convinced that the problem is not nearly as easy and clear cut
> as I started exposing the issues.
>
> Also mentioned in a later this thread that libsigc++ contains a nearly
> complete thread wrapper. This is with really good reason.
> Signal/Slots are anonymous. Thus it is not possible for a user
> to thread safe their own code. They don't know all the
> receivers nor should they be able to find them out. Thus
> any signal/slot system must deal with at least some thread
> concepts internally. Also speed was a consideration for libsigc++
> at least the first implementation. In order to "void" a return
> code you would have to use an exception or a thread safe flag.
> A thread safe flag is 40 times faster even when the try/catch is
> trivial. You don't have marshallers or returns so you haven't
> faced the problem.
> (Libsigc++ is still working on a good rwlock which is needed to
> make it thread safe.)
>
> Anyway, thanks for another signal/slot system to scrutanize. I
> will look it over some more later when I reconsider some of libsigc++
> internals. Later.
>
> --Karl
>
Boost list run by bdawes at acm.org, gregod at cs.rpi.edu, cpdaniel at pacbell.net, john at johnmaddock.co.uk