 |
C++Talk.NET C++ language newsgroups
|
| View previous topic :: View next topic |
| Author |
Message |
Lukasz Dobrek Guest
|
Posted: Thu Jan 13, 2005 9:42 pm Post subject: Design by Contract tool for C++ |
|
|
Dear C++ users,
We are proud to present C^2 - a Design by Contract [DBC] extension for C++. DBC
is an idea which helps you to reduce the number of bugs. This works as follows:
Classes and their clients make contracts between each other. These contracts
are automatically checked at runtime. Contracts are written inside comments,
hence they don't interfere with regular C++ compiler. C^2 is a code generator.
It parses comments of your class definitions and generates the corresponding
code. You can use C^2 with any C++ project, eaven with those you've already
started.
We have prepared the following list of frequently asked questions about C^2.
1. What does C^2 do?
C^2 parses your C++ sources, including the comments. Only a few simple keywords
are needed for the formulation of contracts.
2. What are those contracts?
C^2 supports three basic types of contracts: preconditions, postconditions and
class invariants. Preconditions must be fulfilled before you call the method,
postconditions before returning from a method and invariants should always be
met. Contracts are specified by giving a condition which has to evaluate to
true. Conditions are formulated in regular C++.
3. What happens when the contract is violated?
This is entirely up to you. In case of a contract violation the function
"_dbc::failure" is called. We provide a default implementation of this function
reporting a message, but you are free to modify the content of this function to
suit your own needs.
4. How to use preconditions?
class Math
{
public:
// @pre( a >= 0 )
float sqrt( float a ) const;
};
If you compile this code using C^2, every time you call this function the
condition "a >= 0" is going to be checked.
5. How to use postconditions?
class ElectricalDevice
{
public:
bool isOperational() const;
// @post( ! isOperational() )
void unplug();
};
From now on, every time you call unplug() you are guaranteed that your
ElectricalDevice is not operational.
6. How to use invariants?
class RationalNumber
{
public:
....
private:
int mNominator;
int mDenominator; // @invariant( mDenominator != 0 )
};
From now on whatever you do with the object of class RationalNumber, you can be
sure that it is well defined.
7. What about inheritance?
This is where C^2 really starts to show its strengths. All contracts are
inherited, according to the rules of behavioral subtyping [Bhvr Sbt]. For more
information please look into the C^2 handbook.
8. Aren't contracts a great support for testing?
Yes, they are. For example check out the "Testing Strategies" chapter 10 of the
"C++ FAQ Book" [FAQ Book].
9. Aren't contracts a wonderful new way of enhancing class documentation?
Indeed. The best possible way to document something is source code itself. C^2
transforms your contracts into sourcecode. Thus, you avoid synchronization
problems between documentation and code.
10. How does C^2 help during debugging?
Contracts help a lot during the debugging session.
- they help to pinpoint an error. The runtime checking will report an error
already when it occurs, not when it has erroneous and visible consequences. DBC
makes the implications clear: Violated preconditions imply errors at the call
of the method, violated postconditions or invariants imply errors inside the
methods body.
- most debuggers will respect conditions in C^2 - generated code while stepping
through the code.
11. In which way are contracts better than asserts?
There are a number of reasons: First, contracts are written inside of class
definitions, hence they enhance class documentation. Second, contracts support
inheritance. Third, there is no easy way to express class invariants using
asserts. You have to insert them by hand at each place you want them to be
verified. This can be tedious and prone to errors.
12. What about performance?
Usually, this is not a problem, though validation of conditions might take
time. For this exact reason you might decide to use them only during
development and testing phase of your production cycle. You are given an
opportunity to switch the contracts off in your release version.
13. Where can I get C^2?
Please download a Windows and Linux trial version of C^2 at
http://www.aechmea.de/html/german/Downloads01_e.htm
14. Where can I read more?
Download C^2. The installation also contains a handbook fully documenting the
usage of C^2.
15. Which platforms are supported?
At the mment, we support Microsoft Visual Studio 6.0 environment. We also have
a Linux version of dbc. We are going to support Microsoft Visual Studio .NET
environment very soon.
16. I have an unanswered question about C^2, what am I suppose to do?
Write us at [email]feedback (AT) aechmea (DOT) de[/email], we will be happy to answer your questions. If
you have any comments, questions or ideas about Design by Contract for C++ we
are happy to hear from you.
1. [DBC] http://archive.eiffel.com/doc/manuals/technology/contract/
2. [Bhvr Sbt] http://www-2.cs.cmu.edu/afs/cs/project/calder/www/fmdp.html
3. [FAQ Book] C++ FAQ / Marshall Cline, Greg Lomow, Mike Girou. - 2nd ed.
[ See http://www.gotw.ca/resources/clcm.htm for info about ]
[ comp.lang.c++.moderated. First time posters: Do this! ]
|
|
| Back to top |
|
 |
Thorsten Ottosen Guest
|
Posted: Thu Jan 13, 2005 11:46 pm Post subject: Re: Design by Contract tool for C++ |
|
|
"Lukasz Dobrek" <Lukasz.Dobrek (AT) aechmea (DOT) de> wrote
| Quote: | Dear C++ users,
We have prepared the following list of frequently asked questions about C^2.
|
you might find additional inspiration in
http://www.open-std.org/JTC1/SC22/WG21/docs/papers/2004/n1669.html
for example, why don't you provide loop invariants?
| Quote: |
7. What about inheritance?
This is where C^2 really starts to show its strengths. All contracts are
inherited, according to the rules of behavioral subtyping [Bhvr Sbt]. For
more
information please look into the C^2 handbook.
|
this behavior is really cute on the surface, but the question is
if it doesn't create more problems than it solves.
| Quote: | 9. Aren't contracts a wonderful new way of enhancing class documentation?
|
yes.
br
Thorsten
[ See http://www.gotw.ca/resources/clcm.htm for info about ]
[ comp.lang.c++.moderated. First time posters: Do this! ]
|
|
| Back to top |
|
 |
Jim Cochrane Guest
|
Posted: Fri Jan 14, 2005 11:03 am Post subject: Re: Design by Contract tool for C++ |
|
|
In article <54836886.0501130724.7cc01a9 (AT) posting (DOT) google.com>, Lukasz Dobrek wrote:
| Quote: | Dear C++ users,
We are proud to present C^2 - a Design by Contract [DBC] extension for C++. DBC
is an idea which helps you to reduce the number of bugs. This works as follows:
Classes and their clients make contracts between each other. These contracts
are automatically checked at runtime. Contracts are written inside comments,
hence they don't interfere with regular C++ compiler. C^2 is a code generator.
It parses comments of your class definitions and generates the corresponding
code. You can use C^2 with any C++ project, eaven with those you've already
started.
|
It's good to make DBC available for C++ development, but why did you
not give any credit or mention in your announcement of its originator,
Bertrand Meyer, or of the language in which it was originally implemented,
Eiffel? (And no, I don't think posting to an Eiffel newsgroup counts
a mention of the language.)
[ See http://www.gotw.ca/resources/clcm.htm for info about ]
[ comp.lang.c++.moderated. First time posters: Do this! ]
|
|
| Back to top |
|
 |
Walter Guest
|
Posted: Fri Jan 14, 2005 11:20 am Post subject: Re: Design by Contract tool for C++ |
|
|
"Lukasz Dobrek" <Lukasz.Dobrek (AT) aechmea (DOT) de> wrote
| Quote: | Dear C++ users,
We are proud to present C^2 - a Design by Contract [DBC] extension for
C++. DBC
is an idea which helps you to reduce the number of bugs.
|
DBC is also implemented as an extension to C++ in Digital Mars C++. See
www.digitalmars.com/ctg/contract.html.
-Walter
www.digitalmars.com free C, C++, D compilers
"code of the nerds"
[ See http://www.gotw.ca/resources/clcm.htm for info about ]
[ comp.lang.c++.moderated. First time posters: Do this! ]
|
|
| Back to top |
|
 |
kanze@gabi-soft.fr Guest
|
Posted: Sat Jan 15, 2005 3:13 am Post subject: Re: Design by Contract tool for C++ |
|
|
Lukasz Dobrek wrote:
| Quote: | We are proud to present C^2 - a Design by Contract [DBC]
extension for C++. DBC is an idea which helps you to reduce
the number of bugs. This works as follows: Classes and their
clients make contracts between each other. These contracts are
automatically checked at runtime. Contracts are written inside
comments, hence they don't interfere with regular C++
compiler. C^2 is a code generator. It parses comments of your
class definitions and generates the corresponding code. You
can use C^2 with any C++ project, eaven with those you've
already started.
We have prepared the following list of frequently asked
questions about C^2.
1. What does C^2 do?
C^2 parses your C++ sources, including the comments. Only a
few simple keywords are needed for the formulation of
contracts.
2. What are those contracts?
C^2 supports three basic types of contracts: preconditions,
postconditions and class invariants. Preconditions must be
fulfilled before you call the method, postconditions before
returning from a method and invariants should always be
met. Contracts are specified by giving a condition which has
to evaluate to true. Conditions are formulated in regular C++.
|
What happens when the code exits the function because of an
exception? Can I specify a separate set of post-conditions?
Can I use "old" (i.e. the previous value of the object) in my
post-conditions? Does this also work with an object which
doesn't support copy?
| Quote: | 6. How to use invariants?
class RationalNumber
{
public:
...
private:
int mNominator;
int mDenominator; // @invariant( mDenominator != 0 )
};
From now on whatever you do with the object of class
RationalNumber, you can be sure that it is well defined.
|
Are their some provisions for weakened invariants after an
exception is thrown?
For the rest, what are the advantages compared to the current
solution of using an inline public function with the contract
checks, which forwards to a virtual private function?
And does your system support the (IMHO mistaken) Eiffel
position, that the checks are only invoked when the function is
called from outside the class.
--
James Kanze GABI Software http://www.gabi-soft.fr
Conseils en informatique orientée objet/
Beratung in objektorientierter Datenverarbeitung
9 place Sémard, 78210 St.-Cyr-l'École, France, +33 (0)1 30 23 00 34
[ See http://www.gotw.ca/resources/clcm.htm for info about ]
[ comp.lang.c++.moderated. First time posters: Do this! ]
|
|
| Back to top |
|
 |
Lukasz Dobrek Guest
|
Posted: Sat Jan 15, 2005 3:14 am Post subject: Re: Design by Contract tool for C++ |
|
|
In article <41e6f0a3$0$48316$14726298 (AT) news (DOT) sunsite.dk>, Thorsten Ottosen wrote:
| Quote: |
"Lukasz Dobrek" <Lukasz.Dobrek (AT) aechmea (DOT) de> wrote in message
news:54836886.0501130724.7cc01a9 (AT) posting (DOT) google.com...
| Dear C++ users,
| We have prepared the following list of frequently asked questions about C^2.
|
you might find additional inspiration in
http://www.open-std.org/JTC1/SC22/WG21/docs/papers/2004/n1669.html
Thanks a lot for a hint. I won't comment on the differences between |
your and our proposal yet. I need more time to think about your
solutions.
| Quote: | for example, why don't you provide loop invariants?
This I can tell you. |
- We concentrated on the contracts as a way of enhancing OO programing.
- We would have to parse the functions bodies much more carefully
than we do.
- They are not much better than asserts from our point of view.
| Quote: |
|
| 7. What about inheritance?
|
| This is where C^2 really starts to show its strengths. All contracts are
| inherited, according to the rules of behavioral subtyping [Bhvr Sbt]. For
more
| information please look into the C^2 handbook.
this behavior is really cute on the surface, but the question is
if it doesn't create more problems than it solves.
My personal experience is that it creates problems, in cases which are |
anyway bad design. But perhaps you have a counterexamples of
well design OO - hierarchy were this approach to DBC results in
problems. I will be happy to look at it.
Thanks for hints
Lukasz Dobrek
[ See http://www.gotw.ca/resources/clcm.htm for info about ]
[ comp.lang.c++.moderated. First time posters: Do this! ]
|
|
| Back to top |
|
 |
stevenwurster@lycos.com Guest
|
Posted: Sat Jan 15, 2005 3:35 am Post subject: Re: Design by Contract tool for C++ |
|
|
Thorsten Ottosen wrote:
| Quote: |
|
| 7. What about inheritance?
|
| This is where C^2 really starts to show its strengths. All
contracts are
| inherited, according to the rules of behavioral subtyping [Bhvr
Sbt]. For
more
| information please look into the C^2 handbook.
this behavior is really cute on the surface, but the question is
if it doesn't create more problems than it solves.
|
Could you expand on your concern, please. It works fairly well in
Eiffel, in my opinion.
Steve
[ See http://www.gotw.ca/resources/clcm.htm for info about ]
[ comp.lang.c++.moderated. First time posters: Do this! ]
|
|
| Back to top |
|
 |
heilig@iname.com Guest
|
Posted: Sat Jan 15, 2005 3:55 am Post subject: Re: Design by Contract tool for C++ |
|
|
I'm glad to see you've included @result, the ability to make assertions
about a function's result.
Why didn't you include the `old' semantics, the ability to use a
variable's value before entering a routine in a postcondition? I would
guess that it's possible in C++ because there is a copy constructor.
Why doesn't any language implement this very important feature but
Eiffel (not even D)?
A well-established example is in a stack count, which is suspiciously
missing from the stack example in your manual. From your manual:
// @pre( ! isEmpty() )
int pop();
And I would add this:
// @post( count() = @old( count() ) - 1 )
Other than this ommision, and the one already pointed out about loop
invariants, it seems good. I was hoping you would be the first C++
pre-processor to implement the old semantics. I wouldn't consider
purchasing this product without the old semantics as I believe it is an
incomplete implementation. I'm seriously trying to be constructive
here, and I hope you read it that way.
Brian Heilig
[ See http://www.gotw.ca/resources/clcm.htm for info about ]
[ comp.lang.c++.moderated. First time posters: Do this! ]
|
|
| Back to top |
|
 |
John G Harris Guest
|
Posted: Sat Jan 15, 2005 4:02 am Post subject: Re: Design by Contract tool for C++ |
|
|
In message <54836886.0501130724.7cc01a9 (AT) posting (DOT) google.com>, Lukasz
Dobrek <Lukasz.Dobrek (AT) aechmea (DOT) de> writes
| Quote: | Dear C++ users,
We are proud to present C^2 - a Design by Contract [DBC] extension for C++. DBC
is an idea which helps you to reduce the number of bugs. This works as follows:
Classes and their clients make contracts between each other. These contracts
are automatically checked at runtime.
|
But note that some contracts can't be tested by C++ code. E.g. That a
pointer is pointing to allocated storage.
Also, some contracts are impractical to test. E.g. That a sort hasn't
lost any of its values.
| Quote: | Contracts are written inside comments,
hence they don't interfere with regular C++ compiler.
|
Significant comments are a menace. Isn't it time that the Standard
defined ways to make certain kinds of comment reserved to
implementations ?
<snip>
| Quote: | C^2 supports three basic types of contracts: preconditions, postconditions and
class invariants. Preconditions must be fulfilled before you call the method,
postconditions before returning from a method and invariants should always be
met.
snip |
But note that invariants can be temporarily false inside functions whose
job is to change the state of an object.
John
--
John Harris
[ See http://www.gotw.ca/resources/clcm.htm for info about ]
[ comp.lang.c++.moderated. First time posters: Do this! ]
|
|
| Back to top |
|
 |
Lukasz Dobrek Guest
|
Posted: Sun Jan 16, 2005 4:19 am Post subject: Re: Design by Contract tool for C++ |
|
|
On 2005-01-15, [email]kanze (AT) gabi-soft (DOT) fr[/email] <kanze (AT) gabi-soft (DOT) fr> wrote:
| Quote: | What happens when the code exits the function because of an
exception? Can I specify a separate set of post-conditions?
|
Nothing happens, the postconditions nor invariants are not checked.
It is not trivial how to solve in general this sort of code
generation.
| Quote: |
Can I use "old" (i.e. the previous value of the object) in my
post-conditions? Does this also work with an object which
doesn't support copy?
|
No we don't have old keyword yet. We are planing to add it
in one of the forthcoming releases, but it is not yet there,
| Quote: | 6. How to use invariants?
class RationalNumber
{
public:
...
private:
int mNominator;
int mDenominator; // @invariant( mDenominator != 0 )
};
From now on whatever you do with the object of class
RationalNumber, you can be sure that it is well defined.
Are their some provisions for weakened invariants after an
exception is thrown?
No. |
| Quote: |
For the rest, what are the advantages compared to the current
solution of using an inline public function with the contract
checks, which forwards to a virtual private function?
- You can easily switch contract checking on and off. |
- You have contracts text close to the function declaration. This
help reading class interface.
- You have contract inheritance for granted.
- You have a possibility to have invariants written close
to members. This helps reading the class interface.
- I guess some more.
| Quote: | And does your system support the (IMHO mistaken) Eiffel
position, that the checks are only invoked when the function is
called from outside the class.
No. The checks are always invoked. This must be handled with care |
in order not to get into infinite recursion.
Sincerely Yours
Lukasz Dobrek
[ See http://www.gotw.ca/resources/clcm.htm for info about ]
[ comp.lang.c++.moderated. First time posters: Do this! ]
|
|
| Back to top |
|
 |
Lukasz Dobrek Guest
|
Posted: Sun Jan 16, 2005 4:20 am Post subject: Re: Design by Contract tool for C++ |
|
|
On 2005-01-15, [email]heilig (AT) iname (DOT) com[/email] <heilig (AT) iname (DOT) com> wrote:
| Quote: | I'm glad to see you've included @result, the ability to make assertions
about a function's result.
Why didn't you include the `old' semantics, the ability to use a
variable's value before entering a routine in a postcondition? I would
guess that it's possible in C++ because there is a copy constructor.
Why doesn't any language implement this very important feature but
Eiffel (not even D)?
1. We will do it. |
2. We just didn't find it in our own examples as necessary
as you claim it to be, hence we decided to postpone the "old"
keyword into the one of the forthcoming releases.
3. This is not trivial how to do it. In the example of yours
| Quote: | // @post( count() = @old( count() ) - 1 )
One have to somehow infer the type of count(). This is a |
hard problem, it most likely can be done, with some kind of
Hindley Milner algorithm but it is not simple.
Perhaps we should use:
// @old( int, count() )
It will be easier, but a bit more clumsy.
What do you think about it?
| Quote: | A well-established example is in a stack count, which is suspiciously
missing from the stack example in your manual. From your manual:
// @pre( ! isEmpty() )
int pop();
And I would add this:
// @post( count() = @old( count() ) - 1 )
Other than this ommision, and the one already pointed out about loop
invariants, it seems good. I was hoping you would be the first C++
pre-processor to implement the old semantics. I wouldn't consider
purchasing this product without the old semantics as I believe it is an
incomplete implementation. I'm seriously trying to be constructive
here, and I hope you read it that way.
|
This omission in striking if you are used to use "old".
But the real reason for it, is that we have no @old keyword.
But I agree this is an additional contract which
should be checked. We will add an "@old" keyword to C^2.
Thank you for your comments. I am happy you liked C^2.
Lukasz Dobrek
[ See http://www.gotw.ca/resources/clcm.htm for info about ]
[ comp.lang.c++.moderated. First time posters: Do this! ]
|
|
| Back to top |
|
 |
Lukasz Dobrek Guest
|
Posted: Sun Jan 16, 2005 4:29 am Post subject: Re: Design by Contract tool for C++ |
|
|
On 2005-01-15, John G Harris <news0 (AT) nospam (DOT) demon.co.uk> wrote:
| Quote: | In message <54836886.0501130724.7cc01a9 (AT) posting (DOT) google.com>, Lukasz
Dobrek <Lukasz.Dobrek (AT) aechmea (DOT) de> writes
Dear C++ users,
We are proud to present C^2 - a Design by Contract [DBC] extension for C++. DBC
is an idea which helps you to reduce the number of bugs. This works as follows:
Classes and their clients make contracts between each other. These contracts
are automatically checked at runtime.
But note that some contracts can't be tested by C++ code. E.g. That a
pointer is pointing to allocated storage.
In principle I guess this could be solve. It will not be fast certainly |
but In principle could be done.
| Quote: | Also, some contracts are impractical to test. E.g. That a sort hasn't
lost any of its values.
True. But already testing whether qsort really sorted the array for |
arrays bigger than 1000 elements adds ~3% to the time of sorting.
| Quote: | Contracts are written inside comments,
hence they don't interfere with regular C++ compiler.
Significant comments are a menace. Isn't it time that the Standard
defined ways to make certain kinds of comment reserved to
implementations ?
I am not sure I understand the question. Could you elaborate on it?
snip
C^2 supports three basic types of contracts: preconditions, postconditions and
class invariants. Preconditions must be fulfilled before you call the method,
postconditions before returning from a method and invariants should always be
met.
snip
But note that invariants can be temporarily false inside functions whose
job is to change the state of an object.
|
Thats true. We propose to do it in the following way:
The functions which return leaving your class in the state
with violated invariant make private. There should anyway not
be public. And using
// @off( inv, private )
Switch off checking the invariant for private function.
using:
// @off( inv )
void noInvCheck()
// @on ( inv )
You can switch invariants checks for one particular function.
Hope this helps, and thank you very much for comments.
Sincerely Yours
Lukasz Dobrek
[ See http://www.gotw.ca/resources/clcm.htm for info about ]
[ comp.lang.c++.moderated. First time posters: Do this! ]
|
|
| Back to top |
|
 |
Thorsten Ottosen Guest
|
Posted: Sun Jan 16, 2005 4:58 am Post subject: Re: Design by Contract tool for C++ |
|
|
"Lukasz Dobrek" <Lukasz.Dobrek (AT) aechmea (DOT) de> wrote
| Quote: | In article <41e6f0a3$0$48316$14726298 (AT) news (DOT) sunsite.dk>, Thorsten Ottosen
wrote: |
| Quote: | | 7. What about inheritance?
|
| This is where C^2 really starts to show its strengths. All contracts are
| inherited, according to the rules of behavioral subtyping [Bhvr Sbt]. For
more
| information please look into the C^2 handbook.
this behavior is really cute on the surface, but the question is
if it doesn't create more problems than it solves.
My personal experience is that it creates problems, in cases which are
anyway bad design. But perhaps you have a counterexamples of
well design OO - hierarchy were this approach to DBC results in
problems. I will be happy to look at it.
|
I have studied this subject quite some time and I haven't seen one
good, realistic example of a weakened precondition. If got an example, then
please post it.
-Thorsten
[ See http://www.gotw.ca/resources/clcm.htm for info about ]
[ comp.lang.c++.moderated. First time posters: Do this! ]
|
|
| Back to top |
|
 |
Thorsten Ottosen Guest
|
Posted: Sun Jan 16, 2005 5:02 am Post subject: Re: Design by Contract tool for C++ |
|
|
<stevenwurster (AT) lycos (DOT) com> wrote
| Quote: | Thorsten Ottosen wrote:
|
| 7. What about inheritance?
|
| This is where C^2 really starts to show its strengths. All
contracts are
| inherited, according to the rules of behavioral subtyping [Bhvr
Sbt]. For
more
| information please look into the C^2 handbook.
this behavior is really cute on the surface, but the question is
if it doesn't create more problems than it solves.
Could you expand on your concern, please. It works fairly well in
Eiffel, in my opinion.
|
You will find some comments about this my proposal under virtual functions.
Anyway, assume the precondition is weakened on a virtual function in a derived
class;
then how do the caller take advantage of that?
He has to do a downcast. Further, even though he has done the downcast,
the precondition of the original function must still be checked to guarantee
that the derived precondition is weaker *by design*.
So there is no way to ensure at compile time nor runtime that the precondition
is in fact weaker. The correctness rests on the shoulders of the programmer,
yet we still get
a runtime penalty for it.
This means that if we do allow weakened precondition, we should not make
it enforced at run-time, but simply treat as a new precondition on a new
function.
Alternatively, as I suggest, don't allow weaker precondtions.
br
Thorsten
[ See http://www.gotw.ca/resources/clcm.htm for info about ]
[ comp.lang.c++.moderated. First time posters: Do this! ]
|
|
| Back to top |
|
 |
Thorsten Ottosen Guest
|
Posted: Sun Jan 16, 2005 11:17 am Post subject: Re: Design by Contract tool for C++ |
|
|
"Lukasz Dobrek" <Lukasz.Dobrek (AT) aechmea (DOT) de> wrote
| Quote: |
On 2005-01-15, [email]kanze (AT) gabi-soft (DOT) fr[/email] <kanze (AT) gabi-soft (DOT) fr> wrote:
What happens when the code exits the function because of an
exception? Can I specify a separate set of post-conditions?
Nothing happens, the postconditions nor invariants are not checked.
It is not trivial how to solve in general this sort of code
generation.
|
just add a stack object with a call to the invariant in the destructor.
-Thorsten
[ See http://www.gotw.ca/resources/clcm.htm for info about ]
[ comp.lang.c++.moderated. First time posters: Do this! ]
|
|
| 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
|
|