Virtual inheritance and interfaces

D

Dave Rahardja

Since C++ is missing the "interface" concept present in Java, I've been using
the following pattern to simulate its behavior:


class Interface0
{
public:
virtual void fn0() = 0;
};

class Interface1
{
public:
virtual void fn1() = 0;
};

class Implementation0: public virtual Interface0, // note "public virtual"
public virtual Interface1
{
public:
virtual void fn0(); // implementation
virtual void fn1(); // implementation
/* ... */
};


I have consistently used public virtual inheritance whenever I'm implementing
(or extending) an interface, to ensure that there is only one instance of the
interface present in any class hierarchy.

Using virtual inheritance also allows me to create mixin classes. For example:


class DefaultInterface0Implementation : public virtual Interface0
{
public:
virtual void fn0(); // implementation
};


class Implementation1: public virtual Interface0,
public virtual Interface1,
private DefaultInterface0Implementation // mixin
{
public:
virtual void fn1(); // implementation

using DefaultInterface0Implementation::fn0(); // mixin behavior
/* ... */
};


This pattern has been successfully used in several projects.


Still, I can't help thinking that this pattern is overkill. Really, the only
time virtual inheritance is needed is when mixins are used (always at the
terminal implementation class definition). Virtual inheritance also costs time
(extra pointer indirection and construction sequence) and space (extra pointer
storage per object) at runtime. For objects that implement a handful of
interfaces, but maintain very little state, the added pointer storage can
represent 20-50% of the storage requirements.

There is also the side effect of not being able to downcast from a pointer to
an interface. However, I've found that with good OO design this is almost
never an issue (hint: serialization can be performed through a Serializable
interface).

Then why not select which interfaces to virtually inherit from at the terminal
implementation class definition? Because interfaces can extend other
interfaces:


class Interface2 { /* ... */ };
class Interface2A: public virtual Interface2 { /* ... */ };

class Interface2Mixin: public virtual Interface2 { /* implementation */ };

class Implementation: public virtual Interface2A, private Interface2Mixin
{ /* ... */ };


This pattern forces Interface2A to virtually inherit from Interface2.

Not using virtual inheritance consistently also causes trouble when _new_
mixin classes are developed. When a new mixin class is defined for future use,
the developer must search for all classes that derive from a particular
interface, and change their inheritance relationship to virtual. This causes a
lot of unneccessary code churning.


Although the rules I use are "always correct" for my purposes, they may not be
the most efficient for real life.

Does anyone have a better set of rules that I can consider using?

-dr
 
A

Alan Johnson

Dave said:
Since C++ is missing the "interface" concept present in Java, I've been using
the following pattern to simulate its behavior:


class Interface0
{
public:
virtual void fn0() = 0;
};

class Interface1
{
public:
virtual void fn1() = 0;
};

class Implementation0: public virtual Interface0, // note "public virtual"
public virtual Interface1
{
public:
virtual void fn0(); // implementation
virtual void fn1(); // implementation
/* ... */
};


I have consistently used public virtual inheritance whenever I'm implementing
(or extending) an interface, to ensure that there is only one instance of the
interface present in any class hierarchy.

Using virtual inheritance also allows me to create mixin classes. For example:


class DefaultInterface0Implementation : public virtual Interface0
{
public:
virtual void fn0(); // implementation
};


class Implementation1: public virtual Interface0,
public virtual Interface1,
private DefaultInterface0Implementation // mixin
{
public:
virtual void fn1(); // implementation

using DefaultInterface0Implementation::fn0(); // mixin behavior
/* ... */
};


This pattern has been successfully used in several projects.


Still, I can't help thinking that this pattern is overkill. Really, the only
time virtual inheritance is needed is when mixins are used (always at the
terminal implementation class definition). Virtual inheritance also costs time
(extra pointer indirection and construction sequence) and space (extra pointer
storage per object) at runtime. For objects that implement a handful of
interfaces, but maintain very little state, the added pointer storage can
represent 20-50% of the storage requirements.

There is also the side effect of not being able to downcast from a pointer to
an interface. However, I've found that with good OO design this is almost
never an issue (hint: serialization can be performed through a Serializable
interface).

Then why not select which interfaces to virtually inherit from at the terminal
implementation class definition? Because interfaces can extend other
interfaces:


class Interface2 { /* ... */ };
class Interface2A: public virtual Interface2 { /* ... */ };

class Interface2Mixin: public virtual Interface2 { /* implementation */ };

class Implementation: public virtual Interface2A, private Interface2Mixin
{ /* ... */ };


This pattern forces Interface2A to virtually inherit from Interface2.

Not using virtual inheritance consistently also causes trouble when _new_
mixin classes are developed. When a new mixin class is defined for future use,
the developer must search for all classes that derive from a particular
interface, and change their inheritance relationship to virtual. This causes a
lot of unneccessary code churning.


Although the rules I use are "always correct" for my purposes, they may not be
the most efficient for real life.

Does anyone have a better set of rules that I can consider using?

-dr

Forgive me if I misunderstand what you are doing, but I don't see why
virtual inheritance is needed in any of your examples. In my
understanding, virtually inheriting from an interface (for purposes of
this thread interface is defined as a class containing nothing but pure
virtual functions) is somewhat pointless.

If a derived object winds up inheriting the same interface along
multiple paths, how does that matter? It shouldn't affect which
function gets called in a dynamic dispatch. Nor should it cause any
space overhead if your compiler optimizes empty base classes (common in
most compilers).
 
D

Dave Rahardja

Forgive me if I misunderstand what you are doing, but I don't see why
virtual inheritance is needed in any of your examples. In my
understanding, virtually inheriting from an interface (for purposes of
this thread interface is defined as a class containing nothing but pure
virtual functions) is somewhat pointless.

Here's an example:

class Interface
{
public:
virtual void fn() = 0;
};

class Mixin: public Interface
{
public:
virtual void fn() {}
};

class Implementation: public Interface, private Mixin
{
public:
using Mixin::fn;
};

int main()
{
Implementation imp; // <- error, abstract class.
Interface* pi = &imp; // <- error, ambiguous.
}


The problem goes away when both Mixin and Implementation inherit virtually
from Interface.
If a derived object winds up inheriting the same interface along
multiple paths, how does that matter? It shouldn't affect which
function gets called in a dynamic dispatch. Nor should it cause any
space overhead if your compiler optimizes empty base classes (common in
most compilers).

Although the compiler won't allocate space for the empty base classes (aside
from the vtable pointer), it cannot get rid of the extra pointer indirection.
Take this example:

class I { /* contains pure virtuals */ };
class A: public virtual I { /* implements some of I */ };
class B: public virtual I { /* implement rest of I */ };

class M { /* independent class */ };

class X: public M, public A { /* implements rest of I */ };
class Y: public A, private B {};

int main()
{
X x;
Y y;

A* pax = &x;
I* piax = pax;

A* pay = &y;
I* piay = pay;
}

In all cases, the compiler needs to find the data area (or at least the vtable
pointer) associated with I part of the object. Getting from a "pointer to A"
to a "pointer to I" cannot be achieved without indirection, because the
storage position of the A data area (or vtable pointer) relative to the I data
area (or vtable pointer) depends on what concrete class (X or Y) it happens to
be a part of.

-dr
 
M

Maarten Kronenburg

"Dave Rahardja" wrote in message
Since C++ is missing the "interface" concept present in Java, I've been using
the following pattern to simulate its behavior:
Dave,
Just mentioning that virtual inheritance is only usefull when an abstract
class is multiply inherited, that is shared, see The C++ Programming
Language, Stroustrup, 3rd ed., 15.2.4.
About the cost of virtual functions, see also:
http://www.parashift.com/c++-faq-lite/virtual-functions.html
In my opinion this cost may be an issue only for very small objects.
Regards, Maarten.
 
M

Michael

Since C++ is missing the "interface" concept present in Java, I've been using
the following pattern to simulate its behavior:

class Interface0
{
public:
virtual void fn0() = 0;

};

class Interface1
{
public:
virtual void fn1() = 0;

};

class Implementation0: public virtual Interface0, // note "public virtual"
public virtual Interface1
{
public:
virtual void fn0(); // implementation
virtual void fn1(); // implementation
/* ... */

};

Two points:
1) It is typically a good idea to declare a virtual destructor in any
class you're planning on inheriting from. This would include your
interfaces.

2) I would also agree with Alan, that you don't need virtual
inheritance *unless* you are planning to inherit from the same thing
in multiple ways. I would suggest just using regular public
inheritance unless you actually need the virtual inheritance. Such an
approach would save some poor maintenance programmer a lot of head
scratching, and increase the probability that your code isn't broken
by subsequent modifications. One of the great dangers of life is when
a maintenance programmer decides - correctly or not - that you're full
of **** and that you throw in all kinds of things you don't need; in
such a case he/she will probably just go through and globally remove
all the virtual inheritance, including the one or two places you
really do need it. It is somewhat better to use what you need, so
that anyone reading your code and finding virtual inheritance will
stop and think 'hmmm, there must be a good reason for this, let me
tread lightly.'

Michael
 
D

Dave Rahardja

"Dave Rahardja" wrote in message
Dave,
Just mentioning that virtual inheritance is only usefull when an abstract
class is multiply inherited, that is shared, see The C++ Programming
Language, Stroustrup, 3rd ed., 15.2.4.

I know that. The problem is that I may not know _when_ an abstract class will
be multiply inherited from _in the future_.
About the cost of virtual functions, see also:
http://www.parashift.com/c++-faq-lite/virtual-functions.html
In my opinion this cost may be an issue only for very small objects.

I'm not concerned about the cost of virtual functions. I'm concerned about the
cost of virtual inheritance.

-dr
 
D

Dave Rahardja

Two points:
1) It is typically a good idea to declare a virtual destructor in any
class you're planning on inheriting from. This would include your
interfaces.

Only if you plan on destroying objects through those interfaces. But point
taken. Maybe interfaces should have the following format by default:

class Interface
{
public:
virtual void fn() = 0; // etc

private:
~Interface();
};

....which will prevent destruction through the interface.

2) I would also agree with Alan, that you don't need virtual
inheritance *unless* you are planning to inherit from the same thing
in multiple ways. I would suggest just using regular public
inheritance unless you actually need the virtual inheritance. Such an
approach would save some poor maintenance programmer a lot of head
scratching, and increase the probability that your code isn't broken
by subsequent modifications. One of the great dangers of life is when
a maintenance programmer decides - correctly or not - that you're full
of **** and that you throw in all kinds of things you don't need; in
such a case he/she will probably just go through and globally remove
all the virtual inheritance, including the one or two places you
really do need it. It is somewhat better to use what you need, so
that anyone reading your code and finding virtual inheritance will
stop and think 'hmmm, there must be a good reason for this, let me
tread lightly.'

Actually, the reason I'm using this pattern is to _anticipate_ future
extensions of the interfaces, so that mixin classes will not cause a ripple of
edits to occur. It is convenient and semantically correct to inherit virtually
from all pure abstract classes intended to act as interfaces (in fact, this is
analogous to what Java does automatically with interfaces). The question is
whether it is practical "in real life".

Like I said, this technique has paid off in ease of maintenance (rule of
thumb: is it an interface? If so, inherit virtually, always), but it comes at
a (slight) runtime cost.

Is it worth it? Well, it depends on your application. What I'd like to learn
are different methods that people have used to address this problem.

As for a maintenance programmer thinking I'm full of ****, I'm sure this is
not the only technique I use that exposes me to that risk ;-). Hey, if there's
one thing I've learned, it's that it's always the _last_ guy to touch the code
who's full of **** ;-P

-dr
 
R

Rolf Magnus

Dave said:
I know that. The problem is that I may not know _when_ an abstract class
will be multiply inherited from _in the future_.


I'm not concerned about the cost of virtual functions. I'm concerned about
the cost of virtual inheritance.

Well, you were talking about an extra pointer indirection. Calling a virtual
member function on a compiler implementing the typcial vtable model
involves three already.
 
R

Rolf Magnus

Dave said:
Only if you plan on destroying objects through those interfaces. But point
taken.

If you have virtual member functions, it's usually a good idea to also spend
the class a virtual destructor, too.
Maybe interfaces should have the following format by default:

class Interface
{
public:
virtual void fn() = 0; // etc

private:
~Interface();
};

...which will prevent destruction through the interface.

Actually this will make it unusable, because you can't derive from it
anymore. The destructor should be protected instead of private.
Btw: Why exactly do you want to prevent destroying the object through the
interface?
 
D

Dave Rahardja

Actually this will make it unusable, because you can't derive from it
anymore. The destructor should be protected instead of private.
Btw: Why exactly do you want to prevent destroying the object through the
interface?

Doh, maybe that should be protected.

Because sometimes it doesn't make any sense to destroy certain objects through
particular interfaces.

But like I said, point taken.

-dr
 
M

Maarten Kronenburg

"Dave Rahardja" wrote
I know that. The problem is that I may not know _when_ an abstract class will
be multiply inherited from _in the future_.
I'm not concerned about the cost of virtual functions. I'm concerned about the
cost of virtual inheritance.
Yes, and so has Marshall Cline on:
http://www.parashift.com/c++-faq-lite/virtual-functions.html
Having said that I never used this, if I read 15.4.2 correctly, virtual
inheritance doesn't change the vtable mechanism at all. It just changes the
number of base class objects. If that is true, the base class itself is not
affected at all, as long as you made the right member functions virtual
(including the destructor). The runtime cost of virtual inheritance is then
negative: you win runtime, because fewer base class objects have to be
constructed.
Please correct me if I'm wrong.
Regards, Maarten.
 
P

Pete Becker

Maarten said:
The runtime cost of virtual inheritance is then
negative: you win runtime, because fewer base class objects have to be
constructed.

Fewer than what?

Assuming that the answer is non-virtual inheritance, that's a different
class hierarchy, suitable for a different set of problems. So comparing
runtime code isn't really appropriate, because they do two different things.

--

-- Pete
Roundhouse Consulting, Ltd. (www.versatilecoding.com)
Author of "The Standard C++ Library Extensions: a Tutorial and
Reference." (www.petebecker.com/tr1book)
 
D

Dave Rahardja

"Dave Rahardja" wrote
Yes, and so has Marshall Cline on:
http://www.parashift.com/c++-faq-lite/virtual-functions.html

That part of the FAQ specifically ignores virtual inheritance and RTTI. As
mentioned many times in the page:

| Note: the above discussion is simplified considerably, since it doesn't
| account for extra structural things like multiple inheritance, virtual
| inheritance, RTTI, etc., nor does it account for space/speed issues such
| as page faults, calling a function via a pointer-to-function, etc.
Having said that I never used this, if I read 15.4.2 correctly, virtual
inheritance doesn't change the vtable mechanism at all. It just changes the
number of base class objects. If that is true, the base class itself is not
affected at all, as long as you made the right member functions virtual
(including the destructor). The runtime cost of virtual inheritance is then
negative: you win runtime, because fewer base class objects have to be
constructed.
Please correct me if I'm wrong.
Regards, Maarten.

Using virtual inheritance absolutely _does_ change the vtable mechanism: it
adds an extra indirection. Let's take the following example:

class A { /* pure virtual functions */ };
class B {};
class C : public virtual A { /* complete implementation of A */ };
class D: public B, public C {};


The data structure for class C may be represented as:

+-------------------+ -.
| Ptr to A vtable | +- A part
+-------------------+ -'
| Ptr to A part | |
| - - - - - - - - - | +- C part
| C data | |
+-------------------+ -


While the data structure for class D may be represented as:

+-------------------+ -.
| Ptr to A vtable | +- A part
+-------------------+ -'
| B data | +- B part
+-------------------+ -'
| Ptr to A part | |
| - - - - - - - - - | +- C part
| C data | |
+-------------------+ -.
| D data | +- D part
+-------------------+ -'


Notice that the position of the A part relative to the C part has moved,
depending on which object is instantiated. If you had a function that must
deal with C pointers that internally upcasts the pointer to an A pointer:

void fn(C* cptr)
{
A* aptr = cptr;

/* ... */
}

int main()
{
C c;
D d;
fn(&c);
fn(&d);
}

How would the function's code know how to find the A part of the object,
having only a pointer to C? It can't, unless C contains a pointer to the A
part of the object. Therefore, calls to virtual functions defined in class A
must undergo one additional indirection to find the correct vtable, for a
total of four indirections, analogous to:

ptr_to_object->ptr_to_a_part->ptr_to_a_vtable[virtual_fn_index]();

-dr
 
M

Maarten Kronenburg

"Dave Rahardja" wrote
Using virtual inheritance absolutely _does_ change the vtable mechanism: it
adds an extra indirection. Let's take the following example:

class A { /* pure virtual functions */ };
class B {};
class C : public virtual A { /* complete implementation of A */ };
class D: public B, public C {};


The data structure for class C may be represented as:

+-------------------+ -.
| Ptr to A vtable | +- A part
+-------------------+ -'
| Ptr to A part | |
| - - - - - - - - - | +- C part
| C data | |
+-------------------+ -
In my opinion the C object only consists of an A part, a C part and one
pointer to the C vtable. There is no Ptr to A part, only an A part. Every
object of base class A or a class derived from it only gets one pointer to
the vtable of that class. Therefore always only one vtable indirection takes
place, no matter how many times it is derived. See also More Effective C++,
Scott Meyers, item 24.
The virtual in virtual inheritance in your example could also be omitted. It
only makes a change if the base class is multiply inherited by some derived
class. See also 10.1 in the standard N2143.
It would make a change if you had:
class B : public virtual A {...}
because then class D would have inherited A two times, and the virtual
inheritance would make having one A part in D, instead of two.
This is of course if I understand the book and the standard correctly.
Hope this helps,
Regards, Maarten.
 
M

Maarten Kronenburg

"Pete Becker" wrote
Fewer than what?

Assuming that the answer is non-virtual inheritance, that's a different
class hierarchy, suitable for a different set of problems. So comparing
runtime code isn't really appropriate, because they do two different things.
If they would not do two different things, comparing runtime would be rather
pointless.
 
D

Dave Rahardja

"Dave Rahardja" wrote
In my opinion the C object only consists of an A part, a C part and one
pointer to the C vtable. There is no Ptr to A part, only an A part. Every
object of base class A or a class derived from it only gets one pointer to
the vtable of that class. Therefore always only one vtable indirection takes
place, no matter how many times it is derived. See also More Effective C++,
Scott Meyers, item 24.
The virtual in virtual inheritance in your example could also be omitted. It
only makes a change if the base class is multiply inherited by some derived
class. See also 10.1 in the standard N2143.
It would make a change if you had:
class B : public virtual A {...}
because then class D would have inherited A two times, and the virtual
inheritance would make having one A part in D, instead of two.
This is of course if I understand the book and the standard correctly.
Hope this helps,
Regards, Maarten.

I'm afraid you've missed the point of my example. The fact that D has only one
copy of A is immaterial--what I was trying to show was that the "relative
distance" of a base class (A) and a class that virtually inherits from it (C)
can vary depending on the concrete class.

A function such as fn() in my previous example would have no way to determine
where the A vtable (or A's data for that matter) given a C pointer. Thus the
necessity of an additional pointer to a virtual base class.

-dr
 
P

Pete Becker

Maarten said:
"Pete Becker" wrote
things.
If they would not do two different things, comparing runtime would be rather
pointless.

No, different ways of implementing the same thing may have different
size and speed implications. But that's not the situation here: having
multiple bases of the same type is not the same as having a single
virtual base of that type. There are times when you want one and times
when you want the other. (and getting a complaint from the compiler
about ambituity is not, in itself, a reason to change to a virtual base).

--

-- Pete
Roundhouse Consulting, Ltd. (www.versatilecoding.com)
Author of "The Standard C++ Library Extensions: a Tutorial and
Reference." (www.petebecker.com/tr1book)
 
M

Maarten Kronenburg

"Dave Rahardja" wrote
I'm afraid you've missed the point of my example. The fact that D has only one
copy of A is immaterial--what I was trying to show was that the "relative
distance" of a base class (A) and a class that virtually inherits from it (C)
can vary depending on the concrete class.

A function such as fn() in my previous example would have no way to determine
where the A vtable (or A's data for that matter) given a C pointer. Thus the
necessity of an additional pointer to a virtual base class.

Yes, I now understand that virtual inheritance (and any multiple
inheritance) may also generate a "pointer" in the object, but this
inheritance pointer is more an offset for determining where the data members
are, and totally different from the vtable pointer. This also happens if
there are no virtual functions at all. Every object only needs one vtable
pointer to the vtable of its class (that is the runtime type). A virtual
function may call the base class version of that virtual function, like:
class A
{ virtual void f1()
{ ... };
};
class B : public A
{ void f1()
{ A::f1();
...
};
};
But this calling A::f1() is resolved at compile time, and not at runtime, so
the vtable of A is never needed in a B object. Which virtual function is
called only depends on the runtime type of the object (that is to which
vtable its vtable pointer points), and not on the type of some pointer
pointing to the object itself.
The type of the pointer pointing to the object is only used at compile time
(static binding), and never for the runtime type (dynamic binding).
So we have:
1. the type of the pointer pointing to the object (compile-time type, static
binding)
2. the vtable that the vtable pointer is pointing to (run-time type, dynamic
binding)
3. the offset to the base class data in multiple or virtual inheritance
Now the point is that in my understanding these three things should be
considered totally independent. In particular, calling a virtual function
only involves 2, and never 3, and therefore requires always only one vtable
indirection, and nothing more. It seems that 3 is only involved in
determining where the data members of the different classes should be found.
But agreed that the multiple or virtual inheritance offset pointer may also
cost runtime. But using virtual inheritance only seems to be usefull when
the number of base class objects can be reduced, as mentioned in the
examples. But if it is only used with one base class derivation, as in your
example, then it seems to me that it only generates extra overhead for
finding the base class member data, but not for the vtable indirection.
But I agree that reading Item 24 in More Effective C++ suggests that an
object could contain more than one vtable pointer, but I don't see any
reason for that, because an object can only have one runtime type, and
explicitly calling another version of the virtual function is resolved at
compile time, as mentioned above.
Now what about downcasting with dynamic_cast? As far as I understand it, a
dynamic_cast does not change the runtime type of an object, it only checks
the runtime type to see if the dynamic_cast can return a legal pointer (that
is if the type casted to is the runtime type or some base class type of it).
So even from this new dynamic_casted pointer, the runtime type remains
identical, and still no other vtable pointers than the one of the runtime
type are needed.
Regards, Maarten.
 
R

Rolf Magnus

Maarten said:
"Dave Rahardja" wrote

Yes, I now understand that virtual inheritance (and any multiple
inheritance) may also generate a "pointer" in the object, but this
inheritance pointer is more an offset for determining where the data
members are, and totally different from the vtable pointer. This also
happens if there are no virtual functions at all. Every object only needs
one vtable pointer to the vtable of its class (that is the runtime type).
A virtual function may call the base class version of that virtual
function, like: class A
{ virtual void f1()
{ ... };
};
class B : public A
{ void f1()
{ A::f1();
...
};
};
But this calling A::f1() is resolved at compile time, and not at runtime,
so the vtable of A is never needed in a B object. Which virtual function
is called only depends on the runtime type of the object (that is to which
vtable its vtable pointer points), and not on the type of some pointer
pointing to the object itself.
The type of the pointer pointing to the object is only used at compile
time (static binding), and never for the runtime type (dynamic binding).

However, it is needed for B::f1().
So we have:
1. the type of the pointer pointing to the object (compile-time type,
static binding)
2. the vtable that the vtable pointer is pointing to (run-time type,
dynamic binding)
3. the offset to the base class data in multiple or virtual inheritance

That offset can be determined at compile time.

Now the point is that in my understanding these three things should be
considered totally independent. In particular, calling a virtual function
only involves 2, and never 3, and therefore requires always only one
vtable indirection, and nothing more.

However, that usually resolves to three levels of pointer indirections.
But I agree that reading Item 24 in More Effective C++ suggests that an
object could contain more than one vtable pointer, but I don't see any
reason for that, because an object can only have one runtime type, and
explicitly calling another version of the virtual function is resolved at
compile time, as mentioned above.

Have a look at http://www.cse.wustl.edu/~mdeters/seminar/fall2005/mi.html

It explains how vtables are handled by GCCs C++ front end, especially for
multiple and virtual inheritance. That should explain it.
Now what about downcasting with dynamic_cast? As far as I understand it, a
dynamic_cast does not change the runtime type of an object, it only checks
the runtime type to see if the dynamic_cast can return a legal pointer
(that is if the type casted to is the runtime type or some base class type
of it). So even from this new dynamic_casted pointer, the runtime type
remains identical, and still no other vtable pointers than the one of the
runtime type are needed.

The dynamic_cast doesn't have to return the same address your provided. It
might add an offset.
 

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,769
Messages
2,569,576
Members
45,054
Latest member
LucyCarper

Latest Threads

Top