Connecting Tech Pros Worldwide Help | Site Map

Including related files

  #1  
Old July 23rd, 2005, 05:59 AM
Connell Gauld
Guest
 
Posts: n/a
Hi,
I have what feels like a really stupid question and I'm sorry if it is
asked a lot.
Imagine I have two classes cShip and cPassenger. They each have a
definition in their own header cShip.h and cPassenger.h and their
implementation in cShip.cpp and cPassenger.cpp. Now here is the header
file for each:

#ifndef CSHIP_H
#define CSHIP_H

class cShip
{
...
cPassenger * owner;
...
};

#endif

------Next file

#ifndef CPASSENGER_H
#define CPASSENGER_H

class cPassenger
{
...
cShip * current_vehicle;
...
};

#endif

Now the problem I have is how to join these files together with includes
such that they compile. (The idea here is that a ship always has an
owner but that a passenger can be in a ship that they don't own).

Sorry if this is a really mundane question,
Connell
  #2  
Old July 23rd, 2005, 05:59 AM
Victor Bazarov
Guest
 
Posts: n/a

re: Including related files


Connell Gauld wrote:[color=blue]
> I have what feels like a really stupid question and I'm sorry if it is
> asked a lot.
> [...circular dependency question snipped...][/color]

Don't be sorry, just read the FAQ before posting.
You can find it here: http://www.parashift.com/c++-faq-lite/

V
  #3  
Old July 23rd, 2005, 05:59 AM
James Daughtry
Guest
 
Posts: n/a

re: Including related files


An owner may not be a passenger, so it may make more sense to have a
separate cOwner and cPassenger class. But to get circular references to
compile, you can simply use a forward declaration:

#ifndef CSHIP_H
#define CSHIP_H

class cPassenger;

class cShip
{
...
cPassenger * owner;
...
};

#endif

#ifndef CPASSENGER_H
#define CPASSENGER_H

class cShip;

class cPassenger
{
...
cShip * current_vehicle;
...
};

#endif

This could cause you some problems though if any part of the class
declaration relies on deeper knowledge about the forward declared
class. A forward declaration only gives you the name of the class, not
any details about data members or member functions.

  #4  
Old July 23rd, 2005, 05:59 AM
Alf P. Steinbach
Guest
 
Posts: n/a

re: Including related files


* Connell Gauld:[color=blue]
>
> I have what feels like a really stupid question and I'm sorry if it is
> asked a lot.[/color]

Not at all, it's a good question.

There are a host of very _similar_ questions that all involve
circular dependencies.

In your case the usual solution, forward declarations, is _not_
appropriate.

[color=blue]
> Imagine I have two classes cShip and cPassenger. They each have a
> definition in their own header cShip.h and cPassenger.h and their
> implementation in cShip.cpp and cPassenger.cpp. Now here is the header
> file for each:
>
> #ifndef CSHIP_H
> #define CSHIP_H
>
> class cShip
> {
> ...
> cPassenger * owner;
> ...
> };
>
> #endif
>
> ------Next file
>
> #ifndef CPASSENGER_H
> #define CPASSENGER_H
>
> class cPassenger
> {
> ...
> cShip * current_vehicle;
> ...
> };
>
> #endif
>
> Now the problem I have is how to join these files together with includes
> such that they compile. (The idea here is that a ship always has an
> owner but that a passenger can be in a ship that they don't own).[/color]

What you have is a design problem, not (just) a technical circular
dependency problem.

Let the owner of a ship be a cPerson.

Let cPassenger be a class derived from cPerson, and voilą, problem solved;
you then have

cShip depends on CPerson
cPassenger depends on cShip and cPerson

Later on you might consider the case where the owner might be a person _or_
a corporation.

With that twist it gets more interesting... ;-)

--
A: Because it messes up the order in which people normally read text.
Q: Why is it such a bad thing?
A: Top-posting.
Q: What is the most annoying thing on usenet and in e-mail?
  #5  
Old July 23rd, 2005, 05:59 AM
Gianni Mariani
Guest
 
Posts: n/a

re: Including related files


Connell Gauld wrote:
....[color=blue]
>
> Now the problem I have is how to join these files together with includes
> such that they compile. (The idea here is that a ship always has an
> owner but that a passenger can be in a ship that they don't own).[/color]

Ownership (no pun intended) is somthing you need to manage with the
code, not the declaration (if you were using smart pointers you could do
this otherwise).

As for the declaration issues - you need to forward declare the class
you're referring to - see below.

#ifndef CSHIP_H
#define CSHIP_H

class cPassenger; // declare a class cPassenger

class cShip
{
...
cPassenger * owner;
...
};

#endif

------Next file

#ifndef CPASSENGER_H
#define CPASSENGER_H

class cShip; // declare a class cShip

class cPassenger
{
...
cShip * current_vehicle;
...
};


  #6  
Old July 23rd, 2005, 05:59 AM
Victor Bazarov
Guest
 
Posts: n/a

re: Including related files


Alf P. Steinbach wrote:[color=blue]
> * Connell Gauld:
>[color=green]
>>I have what feels like a really stupid question and I'm sorry if it is
>>asked a lot.[/color]
>
>
> Not at all, it's a good question.
>
> There are a host of very _similar_ questions that all involve
> circular dependencies.
>
> In your case the usual solution, forward declarations, is _not_
> appropriate.[/color]

Huh?
[color=blue]
> [...][/color]
  #7  
Old July 23rd, 2005, 05:59 AM
Alf P. Steinbach
Guest
 
Posts: n/a

re: Including related files


* Victor Bazarov:[color=blue]
> Alf P. Steinbach wrote:[color=green]
> > * Connell Gauld:
> >[color=darkred]
> >>I have what feels like a really stupid question and I'm sorry if it is
> >>asked a lot.[/color]
> >
> >
> > Not at all, it's a good question.
> >
> > There are a host of very _similar_ questions that all involve
> > circular dependencies.
> >
> > In your case the usual solution, forward declarations, is _not_
> > appropriate.[/color]
>
> Huh?[/color]

See the rest of that message, but, since you ask, some background:

It's generally not a good idea to solve design problems by applying
technical kludges to the _symptoms_, whether the kludges be forward
declarations, 'friend', 'void*', C-style casts, or whatever.

Such "solutions" come back to haunt you, and also they typically
yield more complex, non-maintainable code in the first place, because you
missed out on sensible abstraction opportunities (which simplify things).

Cheers,

- Alf

--
A: Because it messes up the order in which people normally read text.
Q: Why is it such a bad thing?
A: Top-posting.
Q: What is the most annoying thing on usenet and in e-mail?
  #8  
Old July 23rd, 2005, 05:59 AM
Victor Bazarov
Guest
 
Posts: n/a

re: Including related files


Alf P. Steinbach wrote:[color=blue]
> * Victor Bazarov:
>[color=green]
>>Alf P. Steinbach wrote:
>>[color=darkred]
>>>* Connell Gauld:
>>>
>>>
>>>>I have what feels like a really stupid question and I'm sorry if it is
>>>>asked a lot.
>>>
>>>
>>>Not at all, it's a good question.
>>>
>>>There are a host of very _similar_ questions that all involve
>>>circular dependencies.
>>>
>>>In your case the usual solution, forward declarations, is _not_
>>>appropriate.[/color]
>>
>>Huh?[/color]
>
>
> See the rest of that message, but, since you ask, some background:
>
> It's generally not a good idea to solve design problems by applying
> technical kludges to the _symptoms_, whether the kludges be forward
> declarations, 'friend', 'void*', C-style casts, or whatever.[/color]

What makes you think there *is* a design problem there? The need to use
a "technical kludge" is *not* necessarily an indication of a design flaw.
Just like 'friend' and 'void*' and 'dynamic_cast', forward declarations
are there to be used to _accommodate_ certain design decisions, not to be
avoided at all costs.
[color=blue]
> Such "solutions" come back to haunt you, and also they typically
> yield more complex, non-maintainable code in the first place, because you
> missed out on sensible abstraction opportunities (which simplify things).[/color]

No! What you did suggest was, in fact, a kludge. If two classes do
depend on each other, the model in question is _fine_, it is perfectly
OK to have a pointer to A in B and a pointer to B in A. And, yes, the
only clean way to resolve the reference "problems" is in the FAQ.

Introducing a non-existent design elements just to avoid using some
language features you might be finding "inappropriate" for whatever
reason, is definitely a mistake.

Design comes first, language-specific implementation comes second.

V
  #9  
Old July 23rd, 2005, 05:59 AM
Alf P. Steinbach
Guest
 
Posts: n/a

re: Including related files


* Victor Bazarov:[color=blue]
>
> Design comes first, language-specific implementation comes second.[/color]

Right.

I gather all that about likes and dislikes that I snipped abvoe, a
completely unwarranted speculation about my thinking (I don't think that
way), was really about your earlier posting, posted almost at the same time
as mine, where you failed to notice it was a design problem... ;-)

Check the design again, then tell me it's reasonable that a boat's owner
must be a passenger.

--
A: Because it messes up the order in which people normally read text.
Q: Why is it such a bad thing?
A: Top-posting.
Q: What is the most annoying thing on usenet and in e-mail?
  #10  
Old July 23rd, 2005, 05:59 AM
Karl Heinz Buchegger
Guest
 
Posts: n/a

re: Including related files


"Alf P. Steinbach" wrote:[color=blue]
>[/color]
[snip][color=blue]
>
> Let the owner of a ship be a cPerson.[/color]

OK.
(Side note: Every cPerson owns a ship? I don't think so.
So I take it for granted that you wanted to have a cPerson class
and derive a COwner class from it)
[color=blue]
>
> Let cPassenger be a class derived from cPerson, and voilą, problem solved;
> you then have
>
> cShip depends on CPerson
> cPassenger depends on cShip and cPerson
>
> Later on you might consider the case where the owner might be a person _or_
> a corporation.[/color]

Well. Eventually he will come to the point where his ships actually transport
cPassenger-s.

So this will end up in

cShip depends on COwner and cPassenger
cOwner depends on cShip
cPassenger depends on cShip

Same problem. The design is better, but the circular dependency stays
the same.

--
Karl Heinz Buchegger
kbuchegg@gascad.at
  #11  
Old July 23rd, 2005, 05:59 AM
Victor Bazarov
Guest
 
Posts: n/a

re: Including related files


Alf P. Steinbach wrote:[color=blue]
> * Victor Bazarov:
>[color=green]
>>Design comes first, language-specific implementation comes second.[/color]
>
>
> Right.
>
> I gather all that about likes and dislikes that I snipped abvoe, a
> completely unwarranted speculation about my thinking (I don't think that
> way), was really about your earlier posting, posted almost at the same time
> as mine, where you failed to notice it was a design problem... ;-)[/color]

*What* design problem?
[color=blue]
> Check the design again, then tell me it's reasonable that a boat's owner
> must be a passenger.[/color]

You're trying to read more than the OP has ever thought of presenting.
Whether the design is reasonable is _not_ the topic of this discussion.
For all it's worth, the question could be about A having a B* in it and
B having an A*.

*You* read the original post again. It doesn't say "My boat ownership
model demands to have two classes 'cPassenger' and 'cShip' and here is
the relationship between them". It says "IMAGINE I have two classes..."
(emphasis added).

I don't dispute that there can be design flaws. What I am disputing that
the need to use a forward declaration is an automatic indication (symptom)
of a bad design.

Besides, if you want to talk design, perhaps you should invite the OP to
comp.object? He asked for a language-specific solution and he got it.

V
  #12  
Old July 23rd, 2005, 05:59 AM
Alf P. Steinbach
Guest
 
Posts: n/a

re: Including related files


* Victor Bazarov:[color=blue]
> It says "IMAGINE I have two classes..." (emphasis added).[/color]

So, the example is what one should imagine, then. ;-)

[color=blue]
> I don't dispute that there can be design flaws.[/color]

Good, we're converging toward agreement here.

[color=blue]
> What I am disputing that the need to use a forward
> declaration is an automatic indication (symptom) of a bad design.[/color]

There we agree completely! :-)

Now who the ... said that?

Give him to me (I've got my hammer ready); I'll bash him!

[color=blue]
> Besides, if you want to talk design, perhaps you should invite the OP to
> comp.object? He asked for a language-specific solution and he got it.[/color]

As you wrote earlier,

[color=blue]
> Design comes first, language-specific implementation comes second.[/color]


Using a language correctly or reasonably has very much to do with the design
level. Otherwise we wouldn't frown on 'void*' and the like. Remove the
design level and you could as well use only the 'asm{ ... }' feature.

So, IMO the design level is the first one should focus on except when a
problem is stated without any connection to an application, e.g. like your
A/B example, and I think you agree with that.

Cheers,

- Alf

--
A: Because it messes up the order in which people normally read text.
Q: Why is it such a bad thing?
A: Top-posting.
Q: What is the most annoying thing on usenet and in e-mail?
  #13  
Old July 23rd, 2005, 05:59 AM
Alf P. Steinbach
Guest
 
Posts: n/a

re: Including related files


* Karl Heinz Buchegger:[color=blue]
> "Alf P. Steinbach" wrote:[color=green]
> >[/color]
> [snip][color=green]
> >
> > Let the owner of a ship be a cPerson.[/color]
>
> OK.
> (Side note: Every cPerson owns a ship? I don't think so.
> So I take it for granted that you wanted to have a cPerson class
> and derive a COwner class from it)[/color]

Nope.

That "nope" follows from the minimalist guideline.

If there isn't a problem, don't solve it (yet), and there isn't a problem:
a ship can have a person as owner without every person being an owner.

A cOwner class is only necessary if an owner has some extra attributes
or different behavior than a person.

So far nothing has indicated that owners do, but in the OP's model
passengers have, compared to persons.

[color=blue][color=green]
> > Let cPassenger be a class derived from cPerson, and voilą, problem solved;
> > you then have
> >
> > cShip depends on CPerson
> > cPassenger depends on cShip and cPerson
> >
> > Later on you might consider the case where the owner might be a person _or_
> > a corporation.[/color]
>
> Well. Eventually he will come to the point where his ships actually transport
> cPassenger-s.
>
> So this will end up in
>
> cShip depends on COwner and cPassenger
> cOwner depends on cShip
> cPassenger depends on cShip
>
> Same problem. The design is better, but the circular dependency stays
> the same.[/color]

No, and yes.

Different but in some respects similar problem: different solution.

Details matter, IMHO.

I think it's interesting that with cPerson (I don't like prefixes!)
one can also more easily see a solution to this new slightly different
problem, where one detail is exemplified by: "what class should contain the
embark() member function?". Should we write ship.embark(passenger) or
should we write passenger.embarkOn(ship), or what?

I think I'd land on the "or what", at least as fundamental implementation;
when thinking about the design level a person isn't forever a passenger,
it's just a transient property, and it can even be an implied property that
shouldn't then be represented explicitly in the cPerson objects except as an
optimization if it turns out that optimization is necessary.

General summary: circular dependencies sometimes arise from bad design,
and generalizing the model to cover more is one possible way to remove such
a dependency (not always applicable, but often enough).

The first generalization, to add the Person concept; the second, to extend
the model to cover cases where persons aren't passengers all the time.

--
A: Because it messes up the order in which people normally read text.
Q: Why is it such a bad thing?
A: Top-posting.
Q: What is the most annoying thing on usenet and in e-mail?
  #14  
Old July 23rd, 2005, 05:59 AM
Victor Bazarov
Guest
 
Posts: n/a

re: Including related files


Alf P. Steinbach wrote:[color=blue]
> [...]
> So, IMO the design level is the first one should focus on except when a
> problem is stated without any connection to an application, e.g. like your
> A/B example, and I think you agree with that.[/color]

Well, no. You need to remember that C++ is a multi-paradigm language.
OO is not the end of its application. You also need to remember that
we are in a _language_ newsgroup, and while design is important, and it
does come first, when asked a *simple* language question, one shouldn't
instead go off on a tangent and begin discussing design when no design
discussion is requested.

It's all about assumptions. I (and a couple of other folks) assumed
that the OP knows what he's doing, and while there can always be some
not-so-obvious misconceptions somebody might have about the model he is
using, it's up to him to recognize that and ask for comments on it. You
OTOH assumed that the OP does *not* know what he's doing. Now, while it
is up to the OP to decide whose assumption is the correct one (and I am
not asking about it 'coz I really don't care), with all other things
being equal, a plain answer to the question posed is _better_ than any
other off-on-a-tangent speculation.

If you don't think so, fine. If you reluctantly agree, fine. If you
now see the light and admit your off-on-a-tangent-ness, fine. In any
case, I am not interested in discussing this any more, at least here.
I've stated my point of view, explaining it further makes no sense to me.

Thanks for reading.

V
  #15  
Old July 23rd, 2005, 05:59 AM
Alf P. Steinbach
Guest
 
Posts: n/a

re: Including related files


* Victor Bazarov:[color=blue]
> Alf P. Steinbach wrote:[color=green]
> > [...]
> > So, IMO the design level is the first one should focus on except when a
> > problem is stated without any connection to an application, e.g. like your
> > A/B example, and I think you agree with that.[/color]
>
> Well, no. You need to remember that C++ is a multi-paradigm language.
> OO is not the end of its application. You also need to remember that
> we are in a _language_ newsgroup, and while design is important, and it
> does come first, when asked a *simple* language question, one shouldn't
> instead go off on a tangent and begin discussing design when no design
> discussion is requested.[/color]

One should give the best available answer, and here that was design.

That's not going off on a tanget: it's ordaining the best medicine.

This medicine also happens to be a general technique not AFAIK mentioned in
the FAQ, so it's interesting in its own right.

[color=blue]
> It's all about assumptions. I (and a couple of other folks) assumed
> that the OP knows what he's doing, and while there can always be some
> not-so-obvious misconceptions somebody might have about the model he is
> using, it's up to him to recognize that and ask for comments on it. You
> OTOH assumed that the OP does *not* know what he's doing.[/color]

Naturally; he or she wouldn't ask if he or she knew.

Do you see that now?

[color=blue]
> Now, while it
> is up to the OP to decide whose assumption is the correct one (and I am
> not asking about it 'coz I really don't care), with all other things
> being equal, a plain answer to the question posed is _better_ than any
> other off-on-a-tangent speculation.[/color]

What I gave was the plainest, best answer.

Advicing to use forward declarations, void* pointers or whatever
tecnical kludges that can hide symptoms, but not cure, and that
can and probably will exacerbate the problems, is ungood.

Especially when the cure is so exceedingly simple.

Cheers,

- Alf

--
A: Because it messes up the order in which people normally read text.
Q: Why is it such a bad thing?
A: Top-posting.
Q: What is the most annoying thing on usenet and in e-mail?
  #16  
Old July 23rd, 2005, 05:59 AM
Victor Bazarov
Guest
 
Posts: n/a

re: Including related files


Alf P. Steinbach wrote:[color=blue]
> [...]
> Especially when the cure is so exceedingly simple.[/color]

ROFLMAO Yeah, right...
  #17  
Old July 23rd, 2005, 05:59 AM
Alf P. Steinbach
Guest
 
Posts: n/a

re: Including related files


* Victor Bazarov:[color=blue]
> Alf P. Steinbach wrote:[color=green]
> > [...]
> > Especially when the cure is so exceedingly simple.[/color]
>
> ROFLMAO Yeah, right...[/color]

Sharpen up, Victor.

It _is_ exceedingly simple.

--
A: Because it messes up the order in which people normally read text.
Q: Why is it such a bad thing?
A: Top-posting.
Q: What is the most annoying thing on usenet and in e-mail?
  #18  
Old July 23rd, 2005, 05:59 AM
Victor Bazarov
Guest
 
Posts: n/a

re: Including related files


Alf P. Steinbach wrote:[color=blue]
> * Victor Bazarov:
>[color=green]
>>Alf P. Steinbach wrote:
>>[color=darkred]
>>>[...]
>>>Especially when the cure is so exceedingly simple.[/color]
>>
>>ROFLMAO Yeah, right...[/color]
>
>
> Sharpen up, Victor.
>
> It _is_ exceedingly simple.[/color]

ROFLMAO To whom?

While I have to admit this *is* amusing... I got no time for this.
  #19  
Old July 23rd, 2005, 06:02 AM
richard pickworth
Guest
 
Posts: n/a

re: Including related files


shouldn't the declaration and implementation be in the header?
richard
"Connell Gauld" <connell@freebreakfast.co.uk> wrote in message
news:d7n3cs$v36$1@newsg4.svr.pol.co.uk...[color=blue]
> Hi,
> I have what feels like a really stupid question and I'm sorry if it is
> asked a lot.
> Imagine I have two classes cShip and cPassenger. They each have a
> definition in their own header cShip.h and cPassenger.h and their
> implementation in cShip.cpp and cPassenger.cpp. Now here is the header
> file for each:
>
> #ifndef CSHIP_H
> #define CSHIP_H
>
> class cShip
> {
> ...
> cPassenger * owner;
> ...
> };
>
> #endif
>
> ------Next file
>
> #ifndef CPASSENGER_H
> #define CPASSENGER_H
>
> class cPassenger
> {
> ...
> cShip * current_vehicle;
> ...
> };
>
> #endif
>
> Now the problem I have is how to join these files together with includes
> such that they compile. (The idea here is that a ship always has an owner
> but that a passenger can be in a ship that they don't own).
>
> Sorry if this is a really mundane question,
> Connell[/color]


  #20  
Old July 23rd, 2005, 06:06 AM
Richard Herring
Guest
 
Posts: n/a

re: Including related files


In message <429f1671.1022321343@news.individual.net>, Alf P. Steinbach
<alfps@start.no> writes[color=blue]
>
>It's generally not a good idea to solve design problems by applying
>technical kludges to the _symptoms_, whether the kludges be forward
>declarations, 'friend', 'void*', C-style casts, or whatever.[/color]

Hmm.

Friend's a kludge because it breaks encapsulation.
void* and casts are kludges because they break type safety.

But what does a forward declaration break?

--
Richard Herring
  #21  
Old July 23rd, 2005, 06:06 AM
Kristo
Guest
 
Posts: n/a

re: Including related files


Richard Herring wrote:[color=blue]
> In message <429f1671.1022321343@news.individual.net>, Alf P. Steinbach
> <alfps@start.no> writes[color=green]
> >
> >It's generally not a good idea to solve design problems by applying
> >technical kludges to the _symptoms_, whether the kludges be forward
> >declarations, 'friend', 'void*', C-style casts, or whatever.[/color]
>
> Hmm.
>
> Friend's a kludge because it breaks encapsulation.[/color]

It's only a kluge if you use it incorrectly. See FAQ 14.2.
[color=blue]
> void* and casts are kludges because they break type safety.[/color]

Yep, that's why the C++ cast operators are so ugly. :)
[color=blue]
> But what does a forward declaration break?[/color]

You'll often see a forward-declared class used to solve the problem of
mutual inclusion, i.e., when two headers #include each other. The
forward declaration allows it to compile, but more often than not
you're looking at a design problem.

Kristo

  #22  
Old July 23rd, 2005, 06:06 AM
Richard Herring
Guest
 
Posts: n/a

re: Including related files


In message <1118251993.505105.281790@g14g2000cwa.googlegroups .com>,
Kristo <kristo605@gmail.com> writes[color=blue]
>Richard Herring wrote:[color=green]
>> In message <429f1671.1022321343@news.individual.net>, Alf P. Steinbach
>> <alfps@start.no> writes[color=darkred]
>> >
>> >It's generally not a good idea to solve design problems by applying
>> >technical kludges to the _symptoms_, whether the kludges be forward
>> >declarations, 'friend', 'void*', C-style casts, or whatever.[/color]
>>
>> Hmm.
>>
>> Friend's a kludge because it breaks encapsulation.[/color]
>
>It's only a kluge if you use it incorrectly. See FAQ 14.2.[/color]

Indeed. I was just trying to find a reason why it might be called a
kludge at all.[color=blue]
>[color=green]
>> void* and casts are kludges because they break type safety.[/color]
>
>Yep, that's why the C++ cast operators are so ugly. :)
>[color=green]
>> But what does a forward declaration break?[/color]
>
>You'll often see a forward-declared class used to solve the problem of
>mutual inclusion, i.e., when two headers #include each other. The
>forward declaration allows it to compile, but more often than not
>you're looking at a design problem.[/color]

At that design level, yes, there may in some cases (I think "more often
than not" is too strong) be a problem with mutual *inclusion*.

But it's much more frequently used to solve legitimate problems of
mutual *reference*, cases where objects of two mutually-dependent
classes genuinely need to contain pointers or references to each other,
but nothing more; perhaps the most obvious is when a child needs to know
who its parent is.

But that wasn't really my point. The forward declaration itself doesn't
break anything[*]; on the contrary, it conveys the minimum possible
information about a class, hiding all the unnecessary detail. I'd say
that was a good and useful thing to be able to do, and it's unfair to
give it the same derogatory label as tricks involving casting to void*.

[*] except (in a different sense of the word) mutual-inclusion loops!

--
Richard Herring
Closed Thread