 |
C++Talk.NET C++ language newsgroups
|
| View previous topic :: View next topic |
| Author |
Message |
Nicola Musatti Guest
|
Posted: Thu Apr 01, 2004 1:50 pm Post subject: Re: Common baseclass for all |
|
|
[email]hattons (AT) globalsymmetry (DOT) com[/email] ("Steven T. Hatton") wrote in message news:<SNqdnbFsCt2yMffdRVn-jA (AT) speakeasy (DOT) net>...
| Quote: | Nicola Musatti wrote:
[email]hattons (AT) globalsymmetry (DOT) com[/email] ("Steven T. Hatton") wrote in message
[...]
While some of the functions addressed by java.lang.Object would be
nice additions to C++, I believe none of those are useful to the
majority of the classes I wrote. Let's examine some of these in
detail:
- clone() : Note that there's no way to enforce a correct
implementation of cloning in derived classes.
Is this different from the situation with the C++ default copy constructor?
Clone is often overridden in Java, in much the same way as a programmer
provides a copy constructor in C++.
|
They are different operations from a conceptual point of view. Cloning
is polymorphic, in that the dynamic type of an object is involved. The
result may be a shallow copy by default, but it is never sliced.
Copy construction operates on the static type of the object and
slicing takes place if you assign an object of a derived class to an
object of a base class; in a way in clonong the type of the source of
the copy wins, while in copy construction the type of the destination
wins.
Cloning makes sense for classes with pointer semantics, while copy
construction mostly makes sense for objects with value semantics. As
Java doesn't directly support classes with value semantics, cloning is
much more important than in C++.
| Quote: | - equals() : it is actually very error prone to provide equality
comparison as a virtual member function (for details see [1]).
I looked at [1]. I have to say the assertion that the default behavior of
equals(), (which is the same as Java's '==') does not represent equivalence
is incorrect. To take an abstract example, lets say we have a set S
containing objects as follows {0,1,2,3,...,9,10...,etc}. Now suppose we
have variables x and y which can each hold one of these objects. We could
define == such that x == y is true if and only if x holds the same object
as y. I.e. 1==1,2==2,3==3, etc., are all true, but 1==2,2==3,3==1, are
false. In other words identity forms an equivalence class. It's the
equivalence class used in all of mathematics unless otherwise specified in
the context.
|
You are confusing values and objects. Where class instances are
involved in Java, equality comparisons implements identity, not
equivalence. Given the following (pseudo) Java:
Integer i = 1;
Integer j = 1;
i == j is false.
This is more or less the same as C++'s:
int i = 1;
int j = 1;
&i == &j which is also false.
| Quote: | But, now I ask how does C++'s overloaded '==' operator differ from Java's
equals() in such a way as to prevent the same kinds of problems as
described in [1]?
|
By applying value oriented semantics, i.e. operate on the arguments'
static type rather than on their dynamic type.
| Quote: | - finalize() : C++ has deterministic destructors, which are a far
superior tool.
I assume that comment is in reference to Java's garbage collection? How does
finalize() otherwise differ from the default destructors of C++?
|
The difference is that you know exactly when a destructor is called in
C++, while you are not even guaranteed that a finalizer will ever run
in Java.
[...]
| Quote: | The others are hashCode(), toString() and a bunch of
threading related methods.
This is another feature I was thinking could be introduced into the core
language through a UBC. I'm not sure of the value of having threading as a
fundamental part of the language. I don't know the value of having a
threaded version of the STL, or even what that would mean.
|
Actually I'm convinced that many commercial STL implementations
currently available *are* thread safe, as the platform they run on
support some form of multithreading. Again, I believe that a standard
approach to multithreading would be very valuable, but this doesn'tr
have anything to do with UBC's.
| Quote: | MI is a powerful tool and as it is with all powerful tools, it must be
handled with care. Introducing a UBC would force all kind of
undesirable problems, by forcing the well known "diamond of death" on
all multiple inheritance hyerarchies.
Something tells me that could be handled through some kind of special
provision, but I'm fairly well persuaded a UBC really isn't advisable for
C++. I kind of hate to give up the cause so easily because I think it
would be productive to get a lot of people looking at it. It's part of a
bigger objective I believe is worth persuing. Determining what can be
learned and adopted from C# and Java to add value to C++ without
subtracting value?
|
I'm glad we convinced you
As far as the core language is concerned, my opinion is very little:
after all those languages have mostly been devised by removing
features from C++. A more profitable endeavour would be to address
some of the features provided by those languages' standard libraries.
The immediate accessibility of C libraries has certainly been one of
C++ major asset in the past, but it probably inhibited the development
of proper C++ (standard) libraries; I'm thinking of domains such as
base OS services, networking, multithreading, graphics, etc.
| Quote: | Here I don't follow. Can you elaborate on this? Java actually employs
introspection at design time through the JavaBeans specification.
Design time is still runtime, even though your components may do
different things than when your application is running. I suspect
you're not familiar with the template metaprogramming technique.
Check out [2] for a (rather tough) introduction.
Sure, Ozone http://www.ozone-db.org/frames/home/what.html uses a kind of
metaprogramming approach. Qt's moc is used to implement slots and signals
as a form of metaprogramming. And of course, C++ has templates. I'm not
sure how reflection plays into this. I'll admit I have not read the entire
document you referenced. Can you tell me what parts might shed some light
on how reflection could be useful in this area?
|
I'm not the most qualified person to get into details about template
metaprogramming. Maybe someone else would care to step in?
[...]
| Quote: | The distinction is that members of the classspace would not have private
or protected access to class members.
You can achieve the same by nesting classes.
I don't think that would accomplish my objective. A better solution might
be an access specifier in addition to public, private and protected.
Perhaps 'associated'. A function declared as associated would be resolved
like a member function, but would not be given access to private or
protected members. The problem would be getting people to use it
correctly.
|
Are you aware of the existence of Argument Dependent Lookup, that is
that functions participate in overload resolution not only according
to the namespace they are defined in, but also according to all the
namespaces in which the function's arguments' types are defined.
Cheers,
Nicola Musatti
---
[ comp.std.c++ is moderated. To submit articles, try just posting with ]
[ your news-reader. If that fails, use mailto:std-c++@ncar.ucar.edu ]
[ --- Please see the FAQ before posting. --- ]
[ FAQ: http://www.jamesd.demon.co.uk/csc/faq.html ]
|
|
| Back to top |
|
 |
Nicola Musatti Guest
|
Posted: Thu Apr 01, 2004 6:15 pm Post subject: Re: Common baseclass for all |
|
|
[email]ahp6 (AT) email (DOT) byu.edu[/email] ("Adam H. Peterson") wrote in message news:<c4feud$ea7u$1 (AT) acs2 (DOT) byu.edu>...
| Quote: | Steven T. Hatton wrote:
[...]
But, now I ask how does C++'s overloaded '==' operator differ from Java's
equals() in such a way as to prevent the same kinds of problems as
described in [1]?
Someone will probably immediately contradict me, but I think they both
tend to have much the same problems. It is easier to overload C++'s
equivalent mechanism for derived types, though, but that's not a
guarantee that the base version won't still accidentally be called
through a base interface.
|
Wha you can do in C++ is realize that equivalence is an operation that
is meaningful for objects with value semantics and not for objects
with pointer semantics, i.e. that the comparison should only take into
account the base subobject whose static type coincides with the
operator argument. It is then easy to implement comparisons for
subclasses in terms of the base class comparison.
Cheers,
Nicola Musatti
---
[ comp.std.c++ is moderated. To submit articles, try just posting with ]
[ your news-reader. If that fails, use mailto:std-c++@ncar.ucar.edu ]
[ --- Please see the FAQ before posting. --- ]
[ FAQ: http://www.jamesd.demon.co.uk/csc/faq.html ]
|
|
| Back to top |
|
 |
Steven T. Hatton Guest
|
Posted: Thu Apr 01, 2004 6:15 pm Post subject: Re: Common baseclass for all |
|
|
Nicola Musatti wrote:
| Quote: | hattons (AT) globalsymmetry (DOT) com ("Steven T. Hatton") wrote in message
news:<SNqdnbFsCt2yMffdRVn-jA (AT) speakeasy (DOT) net>...
Nicola Musatti wrote:
[email]hattons (AT) globalsymmetry (DOT) com[/email] ("Steven T. Hatton") wrote in message
[...]
They are different operations from a conceptual point of view. Cloning
is polymorphic, in that the dynamic type of an object is involved. The
result may be a shallow copy by default, but it is never sliced.
|
I'd have to consider what happens if I call clone through a superclass
pointer in Java. I don't know if that would effectively slice or not.
| Quote: | Cloning makes sense for classes with pointer semantics, while copy
construction mostly makes sense for objects with value semantics. As
Java doesn't directly support classes with value semantics, cloning is
much more important than in C++.
|
I'm not really sure I understand what you mean by value semantics verse
pointer semantics. From the purely conceptual reference, I will argue Java
more readily and frequently supports value semantics than does C++. This
seems somewhat off topic, so I don't want to pursue it unless it
contributes to the objective of this newsgroup. If it helps to clarify
definitions used to specify the language then it probably is worth further
discussion.
| Quote: | You are confusing values and objects. Where class instances are
involved in Java, equality comparisons implements identity, not
equivalence. Given the following (pseudo) Java:
Integer i = 1;
Integer j = 1;
i == j is false.
This is more or less the same as C++'s:
int i = 1;
int j = 1;
&i == &j which is also false.
|
Mathematically, identity forms an equivalence class over a set of enumerable
objects. That equivalence class is isomorphic to the subset of non-negative
integers with an upperbound of n where n is the number of objects. It's not
a question of confusion, it's a question of semantics. But here we are
trespassing on the ground of philosophy and fast approaching that of
religion. Let's agree to disagree on this one.
| Quote: | But, now I ask how does C++'s overloaded '==' operator differ from Java's
equals() in such a way as to prevent the same kinds of problems as
described in [1]?
By applying value oriented semantics, i.e. operate on the arguments'
static type rather than on their dynamic type.
|
I'm not sure such a clear distinction can be drawn. An experiment is in
order. I hypothesize that clone() can be called on a Java object's
superclass producing basically the same result as slicing in C++. C++
assignment operators can optionally be declared virtual (13.5.3.2). Doing
so will produce a result comperable to Java's clone() when called on the
most derived class of an object.
Perhaps I should run the experiment before I post this, but at some point I
have to break the infinite regression, so I'll simply post the hypothesis.
| Quote: | The difference is that you know exactly when a destructor is called in
C++, while you are not even guaranteed that a finalizer will ever run
in Java.
|
The consequence of this is unclear to me. Certainly in terms of garbage
collection there is meaning, but the impact of further execution of the
program after the point at which finalize could be called is less clear.
I'm not disagreeing. I'm simply expressing ignorance of the details.
| Quote: | Actually I'm convinced that many commercial STL implementations
currently available *are* thread safe, as the platform they run on
support some form of multithreading.
|
Being thread safe, and running on threaded platforms are not synonymous.
It's is possible to 'wrap' objects in synchronized objects. Thus the STL
implementation need never have threadding support incorporated. I'm not
sure it's even meaningful to talk about a thread-safe implementation of the
STL. But this again brings us to semantics.
| Quote: | Again, I believe that a standard
approach to multithreading would be very valuable, but this doesn'tr
have anything to do with UBC's.
|
That is not entirely clear. There may be no necessary connection, but, as
you point out. Java does seem to leverage its UBC to support threading.
| Quote: | I'm glad we convinced you
As far as the core language is concerned, my opinion is very little:
after all those languages have mostly been devised by removing
features from C++. A more profitable endeavour would be to address
some of the features provided by those languages' standard libraries.
|
I have the sense the UBC is standing over C++ invisibly manifesting itself
in every class we create. It my be worth persuing as a metaphor if not a
design feature.
| Quote: | I'm not the most qualified person to get into details about template
metaprogramming. Maybe someone else would care to step in?
[...]
|
I'm interested to know what ideas exist regarding 'compiling' templates. I
guess RT_M applies here to some extent. I have not read the section in the
Standard which discusses templates, so it may address much of what I might
have questions about. Templates are becoming more interesting to me. I'm
starting to consider how they might be used at runtime to define new
classes.
| Quote: | The distinction is that members of the classspace would not have
private or protected access to class members.
You can achieve the same by nesting classes.
I don't think that would accomplish my objective. A better solution
might be an access specifier in addition to public, private and
protected.
Perhaps 'associated'. A function declared as associated would be
resolved like a member function, but would not be given access to private
or
protected members. The problem would be getting people to use it
correctly.
Are you aware of the existence of Argument Dependent Lookup, that is
that functions participate in overload resolution not only according
to the namespace they are defined in, but also according to all the
namespaces in which the function's arguments' types are defined.
|
Is that the same as Koening Lookup? How might that apply here?
--
STH
http://www.kdevelop.org
http://www.suse.com
http://www.mozilla.org
---
[ comp.std.c++ is moderated. To submit articles, try just posting with ]
[ your news-reader. If that fails, use mailto:std-c++@ncar.ucar.edu ]
[ --- Please see the FAQ before posting. --- ]
[ FAQ: http://www.jamesd.demon.co.uk/csc/faq.html ]
|
|
| Back to top |
|
 |
Matt Austern Guest
|
Posted: Sun Apr 04, 2004 5:23 pm Post subject: Re: Common baseclass for all |
|
|
[email]susudata (AT) setidava (DOT) kushan.aa[/email] ("Steven T. Hatton") writes:
| Quote: | I also find the notion of common baseclass inheritance intuitively
attractive. My inclination is to believe it would facilitate the
introduction of such functionality as threading into the core language.
|
What do you see as the connection between threading and a common base
class? I can think of lots of languages that contain one but not the
other. (For example, pthreads work perfectly well in C.)
---
[ comp.std.c++ is moderated. To submit articles, try just posting with ]
[ your news-reader. If that fails, use mailto:std-c++@ncar.ucar.edu ]
[ --- Please see the FAQ before posting. --- ]
[ FAQ: http://www.jamesd.demon.co.uk/csc/faq.html ]
|
|
| Back to top |
|
 |
Nicola Musatti Guest
|
Posted: Mon Apr 05, 2004 6:05 pm Post subject: Re: Common baseclass for all |
|
|
[email]hattons (AT) globalsymmetry (DOT) com[/email] ("Steven T. Hatton") wrote in message news:<zIGdnclWP8up2_Hd4p2dnA (AT) speakeasy (DOT) net>...
| Quote: | Nicola Musatti wrote:
[email]hattons (AT) globalsymmetry (DOT) com[/email] ("Steven T. Hatton") wrote in message
news:<SNqdnbFsCt2yMffdRVn-jA (AT) speakeasy (DOT) net>...
Nicola Musatti wrote:
[email]hattons (AT) globalsymmetry (DOT) com[/email] ("Steven T. Hatton") wrote in message
[...]
They are different operations from a conceptual point of view. Cloning
is polymorphic, in that the dynamic type of an object is involved. The
result may be a shallow copy by default, but it is never sliced.
I'd have to consider what happens if I call clone through a superclass
pointer in Java. I don't know if that would effectively slice or not.
|
According to the Sun documentation, it does not.
| Quote: | Cloning makes sense for classes with pointer semantics, while copy
construction mostly makes sense for objects with value semantics. As
Java doesn't directly support classes with value semantics, cloning is
much more important than in C++.
I'm not really sure I understand what you mean by value semantics verse
pointer semantics. From the purely conceptual reference, I will argue Java
more readily and frequently supports value semantics than does C++. This
seems somewhat off topic, so I don't want to pursue it unless it
contributes to the objective of this newsgroup. If it helps to clarify
definitions used to specify the language then it probably is worth further
discussion.
|
No, Java only supports value semantics for primitive types. User
defined types have pointer semantics.
| Quote: | You are confusing values and objects. Where class instances are
involved in Java, equality comparisons implements identity, not
equivalence. Given the following (pseudo) Java:
Integer i = 1;
Integer j = 1;
i == j is false.
This is more or less the same as C++'s:
int i = 1;
int j = 1;
&i == &j which is also false.
Mathematically, identity forms an equivalence class over a set of enumerable
objects. That equivalence class is isomorphic to the subset of non-negative
integers with an upperbound of n where n is the number of objects. It's not
a question of confusion, it's a question of semantics. But here we are
trespassing on the ground of philosophy and fast approaching that of
religion. Let's agree to disagree on this one.
|
No, sorry. It's a question of language semantics. In Java the '=='
operator always compares addresses. Two objects of user defined types
are different unless they are the same object. This doesn't really
make sense for classes that represent quantities, whereas it might be
reasonable for classes whose instances represent different entities in
the real world. You and me might have the same first and last names
and thus be "equal" in a sense, but we still are different persons.
| Quote: | But, now I ask how does C++'s overloaded '==' operator differ from Java's
equals() in such a way as to prevent the same kinds of problems as
described in [1]?
By applying value oriented semantics, i.e. operate on the arguments'
static type rather than on their dynamic type.
I'm not sure such a clear distinction can be drawn. An experiment is in
order. I hypothesize that clone() can be called on a Java object's
superclass producing basically the same result as slicing in C++. C++
assignment operators can optionally be declared virtual (13.5.3.2). Doing
so will produce a result comperable to Java's clone() when called on the
most derived class of an object.
|
The assumption is wrong. Unless it is overridden the clone method
always returns an object of the same type as the most derived type of
the object on which it is called.
| Quote: | Perhaps I should run the experiment before I post this, but at some point I
have to break the infinite regression, so I'll simply post the hypothesis.
|
If only to avoid embarassment :-)
| Quote: | The difference is that you know exactly when a destructor is called in
C++, while you are not even guaranteed that a finalizer will ever run
in Java.
The consequence of this is unclear to me. Certainly in terms of garbage
collection there is meaning, but the impact of further execution of the
program after the point at which finalize could be called is less clear.
I'm not disagreeing. I'm simply expressing ignorance of the details.
|
You are missing the important point, which is what would happen in C++
if a destructor weren't called when it is supposed to be. One very
useful application of C++ destructor is the release of resources upon
scope exit, both in the normal case *and* in the presence of
exceptions. Stack unwinding ensures that a local object's destructor
is called in both cases.
| Quote: | Actually I'm convinced that many commercial STL implementations
currently available *are* thread safe, as the platform they run on
support some form of multithreading.
Being thread safe, and running on threaded platforms are not synonymous.
It's is possible to 'wrap' objects in synchronized objects. Thus the STL
implementation need never have threadding support incorporated. I'm not
sure it's even meaningful to talk about a thread-safe implementation of the
STL. But this again brings us to semantics.
|
Such an implementation would most likely be useless. Instead most STL
implementations I know guarantee that the handling of internal
structures is thread safe and the user only has to worry about the
public interface to STL objects.
| Quote: | Again, I believe that a standard
approach to multithreading would be very valuable, but this doesn'tr
have anything to do with UBC's.
That is not entirely clear. There may be no necessary connection, but, as
you point out. Java does seem to leverage its UBC to support threading.
|
That's because inheritance is at the same time the only generalization
mechanism it supports and the basic unit of function grouping.
[...]
| Quote: | As far as the core language is concerned, my opinion is very little:
after all those languages have mostly been devised by removing
features from C++. A more profitable endeavour would be to address
some of the features provided by those languages' standard libraries.
I have the sense the UBC is standing over C++ invisibly manifesting itself
in every class we create. It my be worth persuing as a metaphor if not a
design feature.
|
The existence of commonality is not necessarily best expressed by a
common base class, and sometimes it actually may not be expressed in
that way. The STL is probably the best example of this. You can read
the "Concepts and Modeling" and the "Refinement" paragraphs from the
following article for details:
http://www.sgi.com/tech/stl/stl_introduction.html
[...]
| Quote: | I don't think that would accomplish my objective. A better solution
might be an access specifier in addition to public, private and
protected.
Perhaps 'associated'. A function declared as associated would be
resolved like a member function, but would not be given access to private
or
protected members. The problem would be getting people to use it
correctly.
Are you aware of the existence of Argument Dependent Lookup, that is
that functions participate in overload resolution not only according
to the namespace they are defined in, but also according to all the
namespaces in which the function's arguments' types are defined.
Is that the same as Koening Lookup? How might that apply here?
|
Yes. ADL already performs the kind of resolution you describe above.
Cheers,
Nicola Musatti
---
[ comp.std.c++ is moderated. To submit articles, try just posting with ]
[ your news-reader. If that fails, use mailto:std-c++@ncar.ucar.edu ]
[ --- Please see the FAQ before posting. --- ]
[ FAQ: http://www.jamesd.demon.co.uk/csc/faq.html ]
|
|
| Back to top |
|
 |
Joshua Lehrer Guest
|
Posted: Mon Apr 05, 2004 6:32 pm Post subject: Re: Common baseclass for all |
|
|
[email]susudata (AT) setidava (DOT) kushan.aa[/email] ("Steven T. Hatton") wrote in message news:<f4-dnYG_cpTgdPzdRVn-hA (AT) speakeasy (DOT) net>...
| Quote: | In some programming languages there is a common baseclass for all (user
defined) objects. This allows for certain functionality to be shared by all
classes. Obviously, C++ accomplishes some of this without providing a
common baseclass.
|
I've always felt that "void" should be a common baseclass for all.
Thus, the folling would all be legal c++ code:
const void& temp(std::string("hello"));
void func(const void &);
func(std::string("hello"));
void func2(const void *);
std::string s("hello");
func2(&s);
One might ask why you would want to bind a temporary to const void &.
There are many times where you want to rely on the rule that a
temporary will live longer than a reference bound to it, but you don't
necessarily have an easy way to get the type of the temporary:
const void & locker = get_lock(args);
get_lock may be overloaded based on the types of the args, and
deducing the return type may be complex. This is part of the
motivation of the "auto" proposal.
This technique would make programming techniques like Scopeguard and
LockRegion significantly easier.
joshua lehrer
factset research systems
NYSE:FDS
---
[ comp.std.c++ is moderated. To submit articles, try just posting with ]
[ your news-reader. If that fails, use mailto:std-c++@ncar.ucar.edu ]
[ --- Please see the FAQ before posting. --- ]
[ FAQ: http://www.jamesd.demon.co.uk/csc/faq.html ]
|
|
| Back to top |
|
 |
Adam H. Peterson Guest
|
Posted: Mon Apr 05, 2004 7:35 pm Post subject: Re: Common baseclass for all |
|
|
| Quote: | I'm not sure such a clear distinction can be drawn. An experiment is in
order. I hypothesize that clone() can be called on a Java object's
superclass producing basically the same result as slicing in C++. C++
assignment operators can optionally be declared virtual (13.5.3.2). Doing
so will produce a result comperable to Java's clone() when called on the
most derived class of an object.
The assumption is wrong. Unless it is overridden the clone method
always returns an object of the same type as the most derived type of
the object on which it is called.
|
Is this true? I was under the impression that Java's clone() method
returns an object of the most derived type that actually overrides
clone() (assuming clone() is defined to return an object of the class's
type), which means you get slicing _unless_ you override it. This is
certainly what happens in comparable C++ virtual clone factories.
But (assuming I'm correct) from that point of view, slicing in Java
works completely differently than the slicing in C++ that results from a
value copy. In C++, it is the expression performing the copy that
governs the most derived type of the result, and in Java it is the type
of the most derived object that implements clone() on which the
operation is called.
---
[ comp.std.c++ is moderated. To submit articles, try just posting with ]
[ your news-reader. If that fails, use mailto:std-c++@ncar.ucar.edu ]
[ --- Please see the FAQ before posting. --- ]
[ FAQ: http://www.jamesd.demon.co.uk/csc/faq.html ]
|
|
| Back to top |
|
 |
Nicola Musatti Guest
|
Posted: Wed Apr 07, 2004 1:43 am Post subject: Re: Common baseclass for all |
|
|
[email]ahp6 (AT) email (DOT) byu.edu[/email] ("Adam H. Peterson") wrote in message news:<c4sab3$b2g4$1 (AT) acs2 (DOT) byu.edu>...
[...]
| Quote: | The assumption is wrong. Unless it is overridden the clone method
always returns an object of the same type as the most derived type of
the object on which it is called.
Is this true? I was under the impression that Java's clone() method
returns an object of the most derived type that actually overrides
clone() (assuming clone() is defined to return an object of the class's
type), which means you get slicing _unless_ you override it. This is
certainly what happens in comparable C++ virtual clone factories.
|
On the contrary, by convention clone() overrides are expected to
obtain the cloned object by calling the base class clone() method and
then perform class local customizations on that object. If you do
nothing you obtain a shallow copy of the most derived object.
In C++ this corresponds to implementing clone() in every class as
MyClass * clone() { return new MyClass(*this); }
but never redefine the copy constructor.
Cheers,
Nicola Musatti
---
[ comp.std.c++ is moderated. To submit articles, try just posting with ]
[ your news-reader. If that fails, use mailto:std-c++@ncar.ucar.edu ]
[ --- Please see the FAQ before posting. --- ]
[ FAQ: http://www.jamesd.demon.co.uk/csc/faq.html ]
|
|
| Back to top |
|
 |
|
|
You cannot post new topics in this forum You cannot reply to topics in this forum You cannot edit your posts in this forum You cannot delete your posts in this forum You cannot vote in polls in this forum
|
|