In GNU C++, you can use the keyword signature
to define a
completely abstract class interface as a datatype. You can connect this
abstraction with actual classes using signature pointers. If you want
to use signatures, run the GNU compiler with the
`-fhandle-signatures
' command-line option. (With this option, the
compiler reserves a second keyword sigof
as well, for a future
extension.)
Roughly, signatures are type abstractions or interfaces of classes.
Some other languages have similar facilities. C++ signatures are
related to ML's signatures, Haskell's type classes, definition modules
in Modula-2, interface modules in Modula-3, abstract types in Emerald,
type modules in Trellis/Owl, categories in Scratchpad II, and types in
POOL-I. For a more detailed discussion of signatures, see
Signatures: A C++ Extension for Type Abstraction and Subtype Polymorphism by Gerald Baumgartner and Vincent F. Russo (Tech report
CSD--TR--93--059, Dept. of Computer Sciences, Purdue University,
September 1993, to appear in Software Practice & Experience).
You can get the tech report by anonymous FTP from
ftp.cs.purdue.edu
in `pub/reports/TR93-059.PS.Z
'.
Syntactically, a signature declaration is a collection of
member function declarations and nested type declarations.
For example, this signature declaration defines a new abstract type
S
with member functions `int foo ()
' and `int bar (int)
':
signature S { int foo (); int bar (int); };
Since signature types do not include implementation definitions, you cannot write an instance of a signature directly. Instead, you can define a pointer to any class that contains the required interfaces as a signature pointer. Such a class implements the signature type.
To use a class as an implementation of S
, you must ensure that
the class has public member functions `int foo ()
' and `int bar (int)
'. The class can have other member functions as well, public
or not; as long as it offers what's declared in the signature, it is
suitable as an implementation of that signature type.
For example, suppose that C
is a class that meets the
requirements of signature S
(C
conforms to
S
). Then
C obj; S * p = &obj;
defines a signature pointer p
and initializes it to point to an
object of type C
.
The member function call `int i = p->foo ();
'
executes `obj.foo ()
'.
Abstract virtual classes provide somewhat similar facilities in standard C++. There are two main advantages to using signatures instead:
T
is a subtype of a signature type S
independent of
any inheritance hierarchy as long as all the member functions declared
in S
are also found in T
. So you can define a subtype
hierarchy that is completely independent from any inheritance
(implementation) hierarchy, instead of being forced to use types that
mirror the class inheritance hierarchy.
There is one more detail about signatures. A signature declaration can
contain member function definitions as well as member function
declarations. A signature member function with a full definition is
called a default implementation; classes need not contain that
particular interface in order to conform. For example, a
class C
can conform to the signature
signature T { int f (int); int f0 () { return f (0); }; };
whether or not C
implements the member function `int f0 ()
'.
If you define C::f0
, that definition takes precedence;
otherwise, the default implementation S::f0
applies.