I contacted Karl Nelson who is the main author of libsigc++ with a few
questions about documentation etc. I enclose below his informative
response.
Allan. (ARRae)
---------- Forwarded message ----------
Date: Thu, 04 Mar 1999 03:20:12 -0800
From: Karl Nelson <[EMAIL PROTECTED]>
To: Allan Rae <[EMAIL PROTECTED]>
Subject: Re: [gtkmm] Libsigc++
Allan Rae writes
>On Sun, 28 Feb 1999, Karl Nelson wrote:
>
>Built okay for me (egcs-1.1.1)
Yeah, that is my current compiler so it should work no problem.
>> If you don't like the way something looks this is the best time
>> to get it changed.
>
>I asked a couple of other LyX developers to take a look at it and the
>general comments are that a couple of *simple* example files would be very
>handy.
Okay, there is actually one very simple example mixed in amoungst the
piles of evil templates and wrappers.
tests/signals/basic.cc
This displays the basic functionality in the closest to the equivelent
of gtk--.
> Also:
>Are there any design discussion documents? (detailing how it all
>fits together -- this would certainly help make it understandable)
Not currently but here is a start...
The best document on that is the sigc++/API file. That gives a
short description of those parts that are intended for user
consumption. Any function not listed there is basically
an implemention detail.
There are a few terms and concepts in the document that
you are likely unfamilar with. (basically I made a lot of them
up!)
The primary concept is that of a template signiture.
A template signiture is best defined as a simple template
with nothing but inline functions that calls methods from
an unspecified class. Okay, so if it does nothing more then
call methods from another class, why have it at all?
The answer is two fold. The primary modivation is to prevent
the mixing of template parameters with virtuals. Each
class with a virtual table will generate a new table for
each template parmeter used in the template. However, as
the signiture does not derive from the class it wraps, no
virtual tables are generated. This would be the equivent in
Java of declaring all functions to be final, so they do not
take up table entries in later defined classes. This
is clearly the case of the Handle class.
The secondary concern is to bind a set of unrelated classes
together through common interface. The usual C++ approach
would be to take and make an abstract base class and then
derive all classes from it. However, here again we run
into the natural bloat of C++ templates. So instead we
will avoid using virtuals and build a set of seperate classes.
Once they are passed through the signiture they can be bound
together and refered to using inline templates. This creates
a compile time signiture for those classes.
To use this system, the class has to be broken into 3 pieces
Impl Defines the implementation of the class
:
: (is contained in)
:
V
Signiture Binds the class to the common functional group
|
| (is derived)
|
V
Concrete Provides access functionality not contained in
Signiture
If you have read through the design specs for the STL, you will
see that I am attempting to attack much the same problem. Only
I will avoid the wide interface problem with lots of inline functions
to a common base class rather that trying to subdivide functionality
with incompatable peices.
Additional benifits to this is that the user can write their own
implementation classes that just need to have the required functions
and place them in the signitures and you have an expanded API.
The new implementations are now transparent to the system.
So now a real life example of this. I have implemented a
very low level and simple signal implementation (which is missing
a lot of functionality I have planned, but demonstrates all
the basic concepts.) The implementation details (Basic_Signal_Impl)
are that it is like the gtk-- signal, with a list of incoming and outgoing
connections. On destruction all of those connections are
trashed. This is placed in a signiture (Signal) for a signal and
the a cap class (Basic_Signal) provides the finished interface.
So the user just declares a
Basic_Signal1<void,int> sig;
(== Signal1<void,int,Basic_Signal_Impl<void,int> > sig; )
which grabs that whole tree, and one never needs to worry about the
exact implemention.
Now a few of the major design decisions.
- Object is the base class of all classes that can have connections.
(In addition an object can be managed, in which case it acts like a
reference counted object.)
- Slots are a bound callback to a static function, Object method, or
a signal. (All slots are managed and dynamic)
- Signals are a container for slots.
- Signals provide their own slots for incoming calls.
- All signals have a return type which may be void.
- Slots are generated from a factory called slot().
- Holding a slot is inherently unsafe so a Handle class contains them.
- Handles will become NULL when the object they point to destructs.
- Connections are specialize handles with no information about the slot
type.
- methods are to be in objects unless they are general (working on
large group of classes)
>Are there any commented versions of the source?
There is no commented source as of yet. This has to do with
the fact that is it is being rewritten from code which has no
source documentation and is 99% generated. If there is some
section that needs perticular attention just mail me and I
will add some descriptions.
Most of my effort will be on filling out the API document,
and documention in the source would just be redundantly documenting
different implementations of identical functions. (from the
users prospective)
It is unfortunate in C++ templates that the entire implementation
has to be in the headers. This is what gives the appearance of
completity, as I can not hide any details in a C file.
>Any documentation?
>Does the gtk-- wrapper you mentioned to me the other day exist yet?
Not as of yet. I have to get the data slot stuff done first
before I will try an compatiblity stuff.
However the entire thing can be summed up in 10 lines.
Signal1<int> sig; => Basic_Signal1<void,int> sig;
Signal1_r<int,int> sig; => Basic_Signal1<int,int> sig;
connect_to_function(sig,foo); => sig.connect(slot(foo));
connect_to_method(sig,&a,&A::foo); => sig.connect(slot(a,&A::foo));
connect_to_signal(sig,sig2); => sig.connect(slot(sig2));
connect_to_function(sig,foo,dat); => sig.connect(data(slot(foo),dat));
connect_to_method(sig,&a,&A::foo,dat);=> sig.connect(data(slot(a,&A::foo),dat));
connect_to_signal(sig,sig2,dat); => sig.connect(data(slot(sig2),dat));
sig(1); => sig(1); (or sig.emit(1);)
Connections are unchanged.
The changes are all to add orthagonality and simpify the user interface.
Return type signals are merged. Type of connect specifications
are gone. And ambiguity in the number of arguments has been removed.
As an added bonus it will not generate nosensical errors.
int foo(int i);
Basic_Signal1<void,int> sig; // int sig(int);
sig.connect(slot(foo)); <= wrong slot type!
Gives this error...
no matching function for call to `::SigC::::SigC::Basic_Signal1<void,int>::connect
(::SigC::::SigC::Slot1<int,int> *)'
../../sigc++/signal.h:94: candidates are:
::SigC::::SigC::Signal1<void,int,::SigC::::SigC::Basic_Signal_Impl1<void,int>
>::connect<int,
::SigC::::SigC::Basic_Signal_Impl1<void,int>>(::SigC::::SigC::Slot1<void,int> *)
Clearly a Slot1<int,int> != Slot1<void,int>
>As I've mentioned to you before we use signals widely in LyX and this new
>scheme is offering a few more interesting possibilities but its difficult
>to judge what those possibilities are if we can't understand what's going
>on or how to use it. The code in the tests/ makes it look a lot more
>complicated to use than it should be. A simple example would help, and if
>I get time I'll try to convert some of my own stuff.
The tests by in large are not meant as examples, but testing the
possible paths of the internal functions and the expandablity of
the interface. Since templates are not compiled until the instatiated
I have to write long programs that hit all possible paths just to
get the syntax errors out.
Actual demos of the usage are very short and to the point.
See tests/signals/basic.cc and tests/handles/handles.cc
basic shows signals in action.
handles shows the internal scoping mechanism.
>Maybe we've been spoit with the simplicity of the gtk-- signal API but
>then again KISS.
This is still early in the design stage, so if you see areas that
are just way to complicated we can trim them if a simple interface
can be found.
Thanks for the comments. I hope this gives you a better idea
of the goals I have in mind.
--Karl