The use of const reference instear of getter

T

Turin

Dear all;

As far as I understand the idea behind getter methods, it is used to
make sure that private memers of a class is returned appropriately to
the calling object.

However, if all I am interested in when making a member private is to
disallow the modification of the value of that member (read-only
member), then how about doing the following:

class A {
public :
const int& ref_to_priv_member;
A() : ref_to_priv_member(priv_member) {
/* Blah */
}

private :
int private_member;
};

I would like to know the opinion of C++ experts on this and if there
are any side-effects of this.
Also from the perferformance point of view, isn't using this mor
effecient than using a getter?

Looking forwards to hearing your opinions.
 
P

Pascal J. Bourguignon

Turin said:
Dear all;

As far as I understand the idea behind getter methods, it is used to
make sure that private memers of a class is returned appropriately to
the calling object.

However, if all I am interested in when making a member private is to
disallow the modification of the value of that member (read-only
member), then how about doing the following:
[...]
I would like to know the opinion of C++ experts on this and if there
are any side-effects of this.
Also from the perferformance point of view, isn't using this mor
effecient than using a getter?

Looking forwards to hearing your opinions.
class A {
public :
const int& ref_to_priv_member;

Instead, do this:

inline int ref_to_priv_member(void) const { return private_member; }

this way you have only a right-value, and if the implementation should
change, you won't have to change the client code (only recompile). On
the other hand, inline member functions will be optimized out and
you'll get the same code as with a data member.
 
E

Erik Wikström

Dear all;

As far as I understand the idea behind getter methods, it is used to
make sure that private memers of a class is returned appropriately to
the calling object.

However, if all I am interested in when making a member private is to
disallow the modification of the value of that member (read-only
member), then how about doing the following:

class A {
public :
const int& ref_to_priv_member;
A() : ref_to_priv_member(priv_member) {
/* Blah */
}

private :
int private_member;
};

I would like to know the opinion of C++ experts on this and if there
are any side-effects of this.
Also from the perferformance point of view, isn't using this mor
effecient than using a getter?

Any good compiler will probably reduce a getter function to the same
thing, with the exception that you can, if you want, easily make changes
to the getter function (such as changing the returned value is certain
cases).
 
E

Erik Wikström

Turin said:
Dear all;

As far as I understand the idea behind getter methods, it is used to
make sure that private memers of a class is returned appropriately to
the calling object.

However, if all I am interested in when making a member private is to
disallow the modification of the value of that member (read-only
member), then how about doing the following:
[...]
I would like to know the opinion of C++ experts on this and if there
are any side-effects of this.
Also from the perferformance point of view, isn't using this mor
effecient than using a getter?

Looking forwards to hearing your opinions.
class A {
public :
const int& ref_to_priv_member;

Instead, do this:

inline int ref_to_priv_member(void) const { return private_member; }

Nitpick:
inline is superfluous since the function is defined in the class
definition, and in C++ we usually write foo() and not foo(void), the
latter is considered bad style.
 
A

AnonMail2005

Dear all;

As far as I understand the idea behind getter methods, it is used to
make sure that private memers of a class is returned appropriately to
the calling object.

However, if all I am interested in when making a member private is to
disallow the modification of the value of that member (read-only
member), then how about doing the following:

class A {
  public :
    const int& ref_to_priv_member;
    A() : ref_to_priv_member(priv_member) {
      /* Blah */
    }

  private :
    int private_member;

};

I would like to know the opinion of C++ experts on this and if there
are any side-effects of this.
Also from the perferformance point of view, isn't using this mor
effecient than using a getter?

Looking forwards to hearing your opinions.

I use the same rule of thumb in this case as I do to determine whether
I should pass an object by const reference to a function - built in
types get passed by value, all others get passed by const reference.

And, BTW, std::string is not a built in type ;).

HTH
 
J

Jerry Coffin

[ ... ]
However, if all I am interested in when making a member private is to
disallow the modification of the value of that member (read-only
member), then how about doing the following:

class A {
public :
const int& ref_to_priv_member;
A() : ref_to_priv_member(priv_member) {
/* Blah */
}

private :
int private_member;
};

I would like to know the opinion of C++ experts on this and if there
are any side-effects of this.
Also from the perferformance point of view, isn't using this mor
effecient than using a getter?

Looking forwards to hearing your opinions.


This works perfectly well, and doesn't really have any side-effects.

Contrary to the claims elsethread, there's also no real problem in the
(IME unlikely) event that you need to change what's returned -- instead
of using an (explicit) member function, you can create a small proxy
class something like this:

class A {

struct ref_to_priv_member {
friend class A;
operator const int &() { return private_member; }
private:
int private_member;

};
};

Now, if you need to modify what ref_to_priv_member returns, you can do
it in your operator, without any effect on external code.

This does have a couple of _minor_ side effects. First of all, since
only one user-defined operator will be considered in an implicit
conversion sequence, if you were depending on (for example) returning a
reference to constant int, and having that used to construct some other
object (implicitly) this would break the code. IMO, this is a positive
far more often than a negative -- on the rare occasion that such a
conversion is needed, you're probably better off making it explicit,
such as adding a conversion to the inner class that directly creates the
type you want (or adding a ctor to that type that constructs it directly
from this inner class). At the same time, this prevents such implicit
conversions from happening by accident, making the code substantially
safer.

Second, (and usually more importantly) this keeps you from having ugly
code everywhere on the off chance that some day it'll become useful to
have a member function to produce the desired value instead of allowing
"reading a value" to look like reading a value.
 
E

Erik Wikström

Erik Wikström said:
Dear all;

As far as I understand the idea behind getter methods, it is used to
make sure that private memers of a class is returned appropriately to
the calling object.

However, if all I am interested in when making a member private is to
disallow the modification of the value of that member (read-only
member), then how about doing the following:
[...]
I would like to know the opinion of C++ experts on this and if there
are any side-effects of this.
Also from the perferformance point of view, isn't using this mor
effecient than using a getter?

Looking forwards to hearing your opinions.

class A {
public :
const int& ref_to_priv_member;

Instead, do this:

inline int ref_to_priv_member(void) const { return private_member; }

Nitpick:
inline is superfluous since the function is defined in the class
definition, and in C++ we usually write foo() and not foo(void), the
latter is considered bad style.

Why?

Is it because it reminds of the C ancestry?

Since I learned C first, I'm always afraid that with foo() it would
accept any number of arguments...

Well, for one it is inconsistent. If I write foo(bar) it means that foo
takes one argument of type bar, regardless if bar = void or not (in
other words foo(void) means foo takes one argument of type void). So for
me that means that the only way to call foo would be to somehow supply
an argument of type void.
 
J

James Kanze

[ ... ]
However, if all I am interested in when making a member
private is to disallow the modification of the value of that
member (read-only member), then how about doing the
following:
class A {
public :
const int& ref_to_priv_member;
A() : ref_to_priv_member(priv_member) {
/* Blah */
}
private :
int private_member;
};
I would like to know the opinion of C++ experts on this and
if there are any side-effects of this. Also from the
perferformance point of view, isn't using this more effecient
than using a getter?
Looking forwards to hearing your opinions.
This works perfectly well, and doesn't really have any
side-effects.

With most compilers, it will increase the size of the object.
(And since the original poster mentionned performance, I expect
that with most compilers, compared to a getter, it will generate
slower code. Internally, the reference will be implemented as a
pointer, and that ain't good for optimization.)

The other problem is consistency. When you need to return
something that isn't really a member variable, you use a
function call; for the member variable, you use a reference.
Isn't that exposing internal details which the client code
should not be concerned with?
 
J

Juha Nieminen

Turin said:
I would like to know the opinion of C++ experts on this and if there
are any side-effects of this.
Also from the perferformance point of view, isn't using this mor
effecient than using a getter?

You have got it completely backwards: Using such a member reference is
going to increase the size of the objects instantiated from the class
(which a member function won't do), it will make instantiating your
class slower (because the reference needs to be initialized) and
accessing the element will be slower (because the compiler will most
probably be unable to optimize the indirection away, as it has no way of
knowing at compile time where the reference is pointing to).

An inline member function will not increment the size of the class,
will have zero overhead at instantiation (and basically anything else),
and will have zero overhead to access the member variable (at least if
the compiler is optimizing).

If what you fear is that a member function will cause slow code,
inlining such a simple member function is one of the easiest
optimizations for any C++ compiler to do. (The only situation where it
will not inline it is when you have all optimizations turned off, eg.
for debugging purposes.)

From an object-oriented point of view your reference is also exposing
the internal implementation details of your class, which the getter
function isn't. (In other words, you could later change the
implementation of your getter function to something completely different
without breaking anything, but you won't be able to do that with your
reference solution.)
 
J

Jerry Coffin

[ ... ]
I don't consider a method declaration/definition like:

int GetAge( ) { return m_Age; }

to really be that much of a journey into "ugly-land". Nor do I consider:

If you never actually used it, this wouldn't be particularly ugly -- but
when you actually use it, ugliness sets in very quickly.
int theirAge = theObj.GetAge( )

to be really stupendously superior to:

int theirAge = theObj.Age;

I don't either -- I consider it substantially _inferior_. As long as the
only use was as you've shown above, it wouldn't be particularly awful,
but that's rarely the case. What gets truly ugly is things like:

a.setSomething(b.getSomething()+b.getSomethingElse()*c.GetSomething());

which strikes me (at least) as a whole lot less readable than:

a.something = b.something+b.somethingElse*c.something;
And yet, to yield a minor gain in readability you suggest the OP does the
following, for each potential member variable which might need to be turned
into a function one day (to avoid *changing* the code-base):


And you somehow consider that the above is not in the realm of "ugly code" ?
Is this ugly code more acceptable because there's presumably less of it? How
ugly does this get when there are 20 or 30 members that are "converted" in
this way?

First of all, if you honestly have 20 or 30 members, chances are pretty
good that what you have is already ugly and poorly designed -- I can
count on my fingers the number of times I've written a class with that
many member variables.

Second, if you're doing this very much, you (of course) gather the
majority of the code into a template, so most of it ends up something
like:

read_only<int> a;
read_only<float> b;

That doesn't take care of the "friend" declarations but they're pretty
easy to handle in similar fashion (e.g. it's pretty trivial to create a
macro that defines the proper instantiation of read_only and a friend
declaration for it).

That, of course, is assuming that you really need a friend declaration
at all -- my experience is that it's really fairly rare. When you make
data private, it's normally to enforce some set of constraints on that
data. IME, it's _usually_ better to enforce those constraints directly.
In fact, about 90% of the time, what's really wanted is simply to ensure
that that data is always within a defined range. For that, you can use a
template like this:

#include <exception>
#include <iostream>
#include <functional>

template <class T, class less=std::less<T> >
class bounded {
const T lower_, upper_;
T val_;

bool check(T const &value) {
return less()(value, lower_) || less()(upper_, value);
}

void assign(T const &value) {
if (check(value))
throw std::domain_error("Out of Range");
val_ = value;
}

public:
bounded(T const &lower, T const &upper)
: lower_(lower), upper_(upper) {}

bounded(bounded const &init)
: lower_(init.lower), upper_(init.upper)
{
assign(init);
}

bounded &operator=(T const &v) { assign(v); return *this; }

operator T() const { return val_; }

friend std::istream &operator>>(std::istream &is, bounded &b) {
T temp;
is >> temp;

if (b.check(temp))
is.setstate(std::ios::failbit);
else
b.val_ = temp;
return is;
}
};

This lets you express your constraint directly, something like:

bounded<int> x(1,1024);

to say that x should be an int in the range [1..1024). While there
certainly _are_ situations in which that's not what's desired (and it's
not what the OP asked about) I find it fits the bill a whole lot of the
time, and does so much more cleanly than anything with explicit get/set
member functions.

I've also done a version where the range is given as template parameters
instead of ctor parameters. With a few restrictions (e.g. no floating
point ranges) this can cure most performance when/if they arise (though
IME, it's pretty rare). Specifically, this eliminates bounds checking in
the (typical) case of assigning one such bounded object to another of
the same type.
I would rather go back and modify every affected line of my code (ie. to
call a member function rather than access a member variable) rather than
implement *that*.

Unfortunately I think this is an example where "style" is given too much
weight over substance, and the substance (the complexity, maintainability,
and performance of the code) suffers as a result.

Having actual facts based upon years of working with and profiling such
code handicaps me in my desire to agree with you.
 
J

Jerry Coffin

[ ... ]
With most compilers, it will increase the size of the object.
(And since the original poster mentionned performance, I expect
that with most compilers, compared to a getter, it will generate
slower code. Internally, the reference will be implemented as a
pointer, and that ain't good for optimization.)

There _may_ be compilers for which this is true (I certainly can't test
with every one) but a quick test shows that it's not necessarily the
case. I put together a quick bit of test code that created a const
reference to a public variable, and compared the code generated for
accessing the variable directly and via the const reference. Unless I
misread the code, there was on difference between the two.
The other problem is consistency. When you need to return
something that isn't really a member variable, you use a
function call; for the member variable, you use a reference.
Isn't that exposing internal details which the client code
should not be concerned with?

IMO, you've got things exactly backwards: the user is accessing
something that _is_ a member variable, and forcing them to access it via
a function is exposing an internal detail with which they should not be
concerned.

In fact, the representation as a member variable is itself an internal
detail. If what the client is doing (at least conceptually) is just
accessing some data, then the code to do so should _look_ like it's just
accessing data. The previous existence of that data vs. generating it on
the fly is an implementation detail that should be hidden.
 
J

James Kanze

[ ... ]
With most compilers, it will increase the size of the object.
(And since the original poster mentionned performance, I expect
that with most compilers, compared to a getter, it will generate
slower code. Internally, the reference will be implemented as a
pointer, and that ain't good for optimization.)
There _may_ be compilers for which this is true (I certainly can't test
with every one) but a quick test shows that it's not necessarily the
case. I put together a quick bit of test code that created a const
reference to a public variable, and compared the code generated for
accessing the variable directly and via the const reference. Unless I
misread the code, there was on difference between the two.

I'm curious which compiler you tested. A very quick test showed
that the extra reference increased the size of the object with
Sun CC, g++ and VC++.
IMO, you've got things exactly backwards: the user is
accessing something that _is_ a member variable, and forcing
them to access it via a function is exposing an internal
detail with which they should not be concerned.
In fact, the representation as a member variable is itself an
internal detail. If what the client is doing (at least
conceptually) is just accessing some data, then the code to do
so should _look_ like it's just accessing data. The previous
existence of that data vs. generating it on the fly is an
implementation detail that should be hidden.

Well, that is really the question. When I mentionned
consistency, the idea I had in mind is that in many cases, you
will be required to use the functional notation, even if the
thing in question is conceptually an "attribute". Since some
attributes require functional access, consistency says that all
should.

It's true that there are ways around this, as you pointed out.
In practice, I've not seen them used much. The idea is
interesting, however: attribute access should always use
variable access syntax, even if it means introducing some sort
of proxy.
 
J

Jerry Coffin

[ ... ]
Why is the *user* insisting that it has to be a member-variable? What
business is it of the user to decide what bits of the object are stored
and what bits are calculated?

As I said in the part of the post that you snipped, the fact that it's a
member variable is, itself, an implementation detail the user/client
should be able to ignore.

The real question is whether (at least at a conceptual level) the user
is asking for some data, or asking for an action to be carried out. IMO,
when the user is simply asking for some data, the code they write should
look like it's just accessing data, even if (as an implementation
detail) that data happens to be generated on the fly instead of just
returned from storage.
 
J

Jerry Coffin

[ ... ]
I'm curious which compiler you tested. A very quick test showed
that the extra reference increased the size of the object with
Sun CC, g++ and VC++.

I tested with g++ and VC++. I didn't look at the size of the object
file, but at the generated code -- the size of the object file can be
affected by all sorts of things unrelated to the actual code.

[ ... ]
The idea is
interesting, however: attribute access should always use
variable access syntax, even if it means introducing some sort
of proxy.

I'll admit that sometimes it may be more work than it's worth -- but I
think the use of an accessor function should be recognized for what it
really is. Accessor functions represent a compromise in which you make a
class easier to implement at the expense of being more difficult (and
ugly) to use. That's directly contrary to (what I'd see as) the basic
point of using objects to start with though -- that it's worthwhile to
do extra work in implementing a class to make it easier to use.

Of course, there are limits to this: making an class 100 times more
difficult to implement in the interest of making it .01 percent easier
to use is only justified if you use that class a LOT. Again, however,
this should be seen for what it is: a decision that at least in this
case, the ideal just isn't worth the effort. I, however, think most code
can quite economically approach the ideal much more closely than is
routinely seen today -- but most people have gotten so accustomed to
such compromises that they've lost sight of the ideal (or, often, never
really even considered what the ideal should be).

There's also the simple fact that most languages lack the features
necessary to do the job correctly. In a mixed-language environment, it
may be reasonable to pick a greatest common denominator feature set
that's consistent across the languages. This may dictate accessor
functions throughout. Again, this should be seen for what it is: it's
not really the "right" way to use C++, but simply a compromise in which
you sacrifice readability of the C++ portion of the job for consistency
with the part implemented in the other language.

IMO, this is _usually_ a poor compromise though -- if consistency
matters that much for you, chances are pretty good that you'd be better
off just using one language throughout. Again, real-world compromises
often render that impractical though...
 
J

James Kanze

[ ... ]
I'm curious which compiler you tested. A very quick test
showed that the extra reference increased the size of the
object with Sun CC, g++ and VC++.
I tested with g++ and VC++. I didn't look at the size of the
object file, but at the generated code -- the size of the
object file can be affected by all sorts of things unrelated
to the actual code.

I didn't look at the size of the object file. I looked at the
size of the object. In an array. Basically:

class A1
{
public:
int const& r ;
A1( int i ) : r( myI ), myI( i ) {}

private:
int myI ;
} ;

class A2
{
public:
A2( int i ) : myI( i ) {}
int r() const { return myI ; }

private:
int myI ;
} ;

On all of my machines, sizeof( A2 ) is 4. Depending on the
machine, sizeof( A1 ) is either 8 or 16.

For occasional use, it doesn't matter, but if you've got a large
array of the things, it could make a significant difference.

Most of the time, too, I don't have intermodule optimization
turned on, so if the constructor isn't inline, and the compiler
can't see what the reference was initialized with, it will have
to generate an additional level of indirection for each access.
(If the constructor is inline, of course, it shouldn't be too
difficult for the compiler to opimize, but it wouldn't surprise
me either if some didn't.)
[ ... ]
The idea is
interesting, however: attribute access should always use
variable access syntax, even if it means introducing some sort
of proxy.
I'll admit that sometimes it may be more work than it's worth -- but I
think the use of an accessor function should be recognized for what it
really is. Accessor functions represent a compromise in which you make a
class easier to implement at the expense of being more difficult (and
ugly) to use. That's directly contrary to (what I'd see as) the basic
point of using objects to start with though -- that it's worthwhile to
do extra work in implementing a class to make it easier to use.

Yes. In many ways, and accessor function can be considered a
hack around language constraints. If the element is logically
considered to be an attribute; of course.

[A number of interesting considerations deleted...]

In the end, I guess, it's a question of making the rules simple
(either none or all of the data is public), and following
general practice (which isn't that bad), even though there are
better (cleaner, more elegant) solutions.

(In our case, we use functions for access even in our pure data
classes. Justified in this particular case by the fact that the
actual data class itself is automatically generated, with code
to support marshalling and data base accesses, but without any
code to support constraints, etc. The access functions are
virtual, and the actual code derives from the generated class to
implement the additional constraints. I'm not sure how to make
that work with your idea using proxies.)
 
J

James Kanze

Why is the *user* insisting that it has to be a
member-variable? What business is it of the user to decide
what bits of the object are stored and what bits are
calculated?

The user isn't. The design says that this type has such and
such attributes. The design of std::list<>, for example, says
that a list has an attribute size. It's logically (read-only)
data, not a function.

In my own code, I use functions, rather than public access, for
a variety of technical reasons (and for consistency, in the
cases where the technical reasons don't actually apply). But
the naming conventions still hold: a function has a verb or verb
phrase as a name, data (such as an attribute) a noun. If the
function returns something that isn't logically an attribute of
the object, it will have a verb phrase as a name, something like
get...(), or calculate...(), or create...(). For attributes,
however, I use the convention:

Type dataName() const ;
Type /* or void */ dataName( Type const& newValue ) ;

(If it's not too expensive, and might make sense in some cases,
I'll have the setter return the old value, so that the client
code can save and restore it.)

There are reasons why you might prefer the function---I gave an
example in my latest response to Jerry, where the access had to
be virtual. But the fact remains that it is compromise, based
on technical reasons; from the design point of view, I'm dealing
with attributes, not behavior.
 
J

Jerry Coffin

[ ... ]
There are reasons why you might prefer the function---I gave an
example in my latest response to Jerry, where the access had to
be virtual. But the fact remains that it is compromise, based
on technical reasons; from the design point of view, I'm dealing
with attributes, not behavior.

Perhaps you could go into more detail about exactly what you're doing --
I haven't quite gotten straight why you can't use a proxy for your
situation. I'm sure you're well aware that a conversion operator can be
virtual, so there's clearly more to it than that...
 
J

James Kanze

[ ... ]
There are reasons why you might prefer the function---I gave an
example in my latest response to Jerry, where the access had to
be virtual. But the fact remains that it is compromise, based
on technical reasons; from the design point of view, I'm dealing
with attributes, not behavior.
Perhaps you could go into more detail about exactly what
you're doing -- I haven't quite gotten straight why you can't
use a proxy for your situation. I'm sure you're well aware
that a conversion operator can be virtual, so there's clearly
more to it than that...

The case which comes immediately to mind is a class which is a
data holder, where the data must be persistent (in a data base),
and transportable over the network. The basic class is defined
by a simple structure:
[ClassName]
AttributeType AttributeName
...
in a separate file. A special program converts this to the base
class, with all of the code for accessing the data base and
marshalling and unmarshalling the data for transmission. It
also generates getters and setters for each attribute, which
simply return or assign to the attribute field. These functions
are virtual, however, and the final class overrides those where
there are stricter constraints (you can't close an order that's
been executed, but not booked, for example).

Other solutions may be possible, but just making the getters and
setters virtual certainly seems to be the easiest solution, and
because the general convention in C++ is to use functions for
getting and setting, it really doesn't seem to conflict with
user expectations. In other words, it is consistent with the
convention.

If I understand you correctly, fundamentally, you disagree with
the convention. I can understand that; it's not perfect. On
the other hand, it's very well established, and it doesn't cause
any real problems, either. So IMHO, there's no point in
fighting to change it; the eventual advantages simply aren't
worth the effort. And changing your code, without changing the
established convention, is counter-productive; because you don't
follow the established convention, people have more trouble
reading and understanding the code.
 
J

Jerry Coffin

[ ... ]
If I understand you correctly, fundamentally, you disagree with
the convention. I can understand that; it's not perfect. On
the other hand, it's very well established, and it doesn't cause
any real problems, either. So IMHO, there's no point in
fighting to change it; the eventual advantages simply aren't
worth the effort. And changing your code, without changing the
established convention, is counter-productive; because you don't
follow the established convention, people have more trouble
reading and understanding the code.

I feel guilty if I don't at least make a token attempt -- nothing is
likely to get better if the (apparently few) people who recognize a
problem don't make any attempt at improving the situation.

Yes, I suppose it's a waste of time to try to fight something most
people consider settled. People respond to hype, not reality. The fact
that this addresses one of the most serious problems in programming
means it gets ignored. If you're going to take over the world, you have
to start by addressing problems nobody's ever really had...
 
J

James Kanze

[ ... ]
If I understand you correctly, fundamentally, you disagree
with the convention. I can understand that; it's not
perfect. On the other hand, it's very well established, and
it doesn't cause any real problems, either. So IMHO,
there's no point in fighting to change it; the eventual
advantages simply aren't worth the effort. And changing
your code, without changing the established convention, is
counter-productive; because you don't follow the established
convention, people have more trouble reading and
understanding the code.
I feel guilty if I don't at least make a token attempt --
nothing is likely to get better if the (apparently few) people
who recognize a problem don't make any attempt at improving
the situation.
Yes, I suppose it's a waste of time to try to fight something
most people consider settled. People respond to hype, not
reality. The fact that this addresses one of the most serious
problems in programming means it gets ignored. If you're going
to take over the world, you have to start by addressing
problems nobody's ever really had...

:)

Still, I think that there are enough real problems that we have
to choose; we can't solve them all. And (maybe just because of
habit) this one doesn't seem that serious---I may need an extra
(), but I still do make the distinction you're concerned about
in my naming conventions. (Actually, while it hadn't occurred
to me that the () were wrong, until your posting, I often had
the impression that I was the only person who felt that there
was a distinction here---that a "getter" wasn't conceptually a
function, and shouldn't have a verb, like "get", in its name.)
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

No members online now.

Forum statistics

Threads
473,982
Messages
2,570,190
Members
46,740
Latest member
AdolphBig6

Latest Threads

Top