placement new and delete

L

LuB

I am constantly creating and destroying a singular object used within
a class I wrote.

To save a bit of time, I am considering using 'placement new'. I guess
we could also debate this decision - but for the sake of this post ...
I'm searching for an answer that assumes said decision.

If I allocate memory in the class declaration:

char buffer[sizeof(ObjectX)];

and use 'placement new' inside of of an instance' method:

pObjectX = new (buffer) ObjectX;

then, when it comes time to clean up, I understand that I must
explicitly invoke the destructor .... but its not clear to me if I
need to use placement delete at all?

pObjectX->~ObjectX();
operator delete (pObjectX_, buffer); // ?????? is this or some
version of this required? or more-safe?

Especially considering, I am just going to create the object again and
again ... and I'm not relinquishing control of the memory space back
to the OS. ctor, dtor, ctor, dtor ... just not sure what delete does
in this case.

I guess I'm just not sure what operator delete (arg1, arg2) does? and
if its required when using placement new.

Thanks in advance,

-Luther
 
J

James Kanze

I am constantly creating and destroying a singular object used within
a class I wrote.
To save a bit of time, I am considering using 'placement new'. I guess
we could also debate this decision - but for the sake of this post ...

My first question would be: how will it save time?
I'm searching for an answer that assumes said decision.
If I allocate memory in the class declaration:
char buffer[sizeof(ObjectX)];
and use 'placement new' inside of of an instance' method:
pObjectX = new (buffer) ObjectX;

You likely get undefined behavior. You have to do something to
ensure that buffer is correctly aligned.
then, when it comes time to clean up, I understand that I must
explicitly invoke the destructor .... but its not clear to me if I
need to use placement delete at all?
pObjectX->~ObjectX();
operator delete (pObjectX_, buffer); // ?????? is this or some
version of this required? or more-safe?

I'm not sure what you're even trying to do here.
Especially considering, I am just going to create the object again and
again ... and I'm not relinquishing control of the memory space back
to the OS. ctor, dtor, ctor, dtor ... just not sure what delete does
in this case.

If you don't call it, it doesn't do anything.
I guess I'm just not sure what operator delete (arg1, arg2) does?

It gets called if the constructor in a placement new throws an
exception.
and if its required when using placement new.

*If* you have a placement new which actually allocates new
resources, you should provide a corresponding placement delete.
If in a new expression which uses your placement new function,
the constructor of the object exits with an exception, the
compiler will call your placement delete.

That's the only time it can or will be called.
 
I

Ian Collins

LuB said:
I am constantly creating and destroying a singular object used within
a class I wrote.

To save a bit of time, I am considering using 'placement new'. I guess
we could also debate this decision - but for the sake of this post ...
I'm searching for an answer that assumes said decision.
Have you considered giving the object its own new and delete operators?
 
G

Gianni Mariani

LuB said:
I am constantly creating and destroying a singular object used within
a class I wrote.

To save a bit of time, I am considering using 'placement new'. I guess
we could also debate this decision - but for the sake of this post ...
I'm searching for an answer that assumes said decision.

If I allocate memory in the class declaration:

char buffer[sizeof(ObjectX)];

Make sure you use the right alignment.
long buffer[ (sizeof(ObjectX)+sizeof(long)-1)/sizeof(long) ];
and use 'placement new' inside of of an instance' method:

pObjectX = new (buffer) ObjectX;

then, when it comes time to clean up, I understand that I must
explicitly invoke the destructor .... but its not clear to me if I
need to use placement delete at all?

pObjectX->~ObjectX();

..... that's it - do no more.
operator delete (pObjectX_, buffer); // ?????? is this or some
version of this required? or more-safe?

Don't so this ..
Especially considering, I am just going to create the object again and
again ... and I'm not relinquishing control of the memory space back
to the OS. ctor, dtor, ctor, dtor ... just not sure what delete does
in this case.

Whatever it is, it's probably not what you want to do.

I guess I'm just not sure what operator delete (arg1, arg2) does? and
if its required when using placement new.

operator new and operator delete concern themselves with memory allocation.

When using operator now - like so:

new T();

It will allocate memory using operator new and then run the constructor
(like placement new).
 
A

Alan Johnson

Gianni said:
LuB said:
I am constantly creating and destroying a singular object used within
a class I wrote.

To save a bit of time, I am considering using 'placement new'. I guess
we could also debate this decision - but for the sake of this post ...
I'm searching for an answer that assumes said decision.

If I allocate memory in the class declaration:

char buffer[sizeof(ObjectX)];

Make sure you use the right alignment.
long buffer[ (sizeof(ObjectX)+sizeof(long)-1)/sizeof(long) ];

How does that ensure correct alignment?
 
J

James Kanze

Gianni said:
LuB said:
I am constantly creating and destroying a singular object used within
a class I wrote.
To save a bit of time, I am considering using 'placement new'. I guess
we could also debate this decision - but for the sake of this post ...
I'm searching for an answer that assumes said decision.
If I allocate memory in the class declaration:
char buffer[sizeof(ObjectX)];
Make sure you use the right alignment.
long buffer[ (sizeof(ObjectX)+sizeof(long)-1)/sizeof(long) ];
How does that ensure correct alignment?

It doesn't, at least not on a Sun Sparc compiling in 32 bit
mode (and probably on a lot of other machines where long is 32
bits, and double 64).

The usual solution is to use a union:

union MaxAlign {
int i ;
long l ;
long double ld ;
double d ;
void* p ;
void (* pf)() ;
MaxAlign* ps ;
} ;
union {
char data[ sizeof( ObjectX ) ] ;
MaxAlign dummyForAlignment ;
} buffer;

new ( buffer.data ) ...

Technically, even that isn't guaranteed by the standard, but in
practice, it should work. (Logically, you shouldn't need double
if you have long double, but I've encountered cases where long
double has an alignment of 4, and double an alignment of 8.)

This may result in allocating too much memory for small types.
There are template meta-programming tricks which can be used to
avoid this, if it is a problem.
 
P

Pete Becker

James said:
This may result in allocating too much memory for small types.
There are template meta-programming tricks which can be used to
avoid this, if it is a problem.

Looking forward, the next revision of the standard incorporates TR1's
templates alignment_of and aligned_storage, as well as the alignof
keyword. With TR1, you'd write:

aligned_storage<sizeof(ObjectX),
alignment_of<ObjectX>::value>::type data;
ObjectX *ptr = new(&data) ObjectX;

With C++0x the same thing still works, but you can be a little less
verbose with:

aligned_storage<sizeof(ObjectX), alignof(ObjectX)>::type data;
ObjectX *ptr = new(&data) ObjectX;

or, if you want the compiler to figure out the optimal alignment:

aligned_storage<sizeof(ObjectX)>::type data;
ObjectX *ptr = new(&data) ObjectX;

--

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

JohnQ

*If* you have a placement new which actually allocates new
resources, you should provide a corresponding placement delete.
If in a new expression which uses your placement new function,
the constructor of the object exits with an exception, the
compiler will call your placement delete.


That's the only time it can or will be called.

He'll have to call the delete operator explicitely in his code somewhere to
get any behavior defined in the class's destructor. Use of placement delete
should be used symmetrically with placement new.

John
 
J

JohnQ

LuB said:
I am constantly creating and destroying a singular object used within
a class I wrote.

To save a bit of time, I am considering using 'placement new'. I guess
we could also debate this decision - but for the sake of this post ...
I'm searching for an answer that assumes said decision.

If I allocate memory in the class declaration:

char buffer[sizeof(ObjectX)];

and use 'placement new' inside of of an instance' method:

pObjectX = new (buffer) ObjectX;

then, when it comes time to clean up, I understand that I must
explicitly invoke the destructor .... but its not clear to me if I
need to use placement delete at all?

pObjectX->~ObjectX();

That's explicitely calling the destructor.
operator delete (pObjectX_, buffer); // ?????? is this or some
version of this required?
No.

or more-safe?
Especially considering, I am just going to create the object again and
again ... and I'm not relinquishing control of the memory space back
to the OS. ctor, dtor, ctor, dtor ... just not sure what delete does
in this case.

I guess I'm just not sure what operator delete (arg1, arg2) does? and
if its required when using placement new.

This is placement new's syntax:
pObjectX = new (buffer) ObjectX;

This is placement delete's syntax (Or so, how I categorize it. It's just an
explicit call to the destructor. I'm not sure if the standard actually
defines placement deleta as some internal call or not. Probably. The below
is the companion behavior to placement new.):
pObjectX->~ObjectX();

There's no requirement to overload the class's new and/or delete operators
to use placement syntax.

John
 
L

LuB

My first question would be: how will it save time?


It doesn't allocate memory. It essentially just returns a pointer.

You likely get undefined behavior. You have to do something to
ensure that buffer is correctly aligned.

Not sure I follow but then, maybe my example wasn't clear.

struct ObjectX { int x; int y; };

struct UserAnObject
{
char buffer[sizeof(ObjectX)];

void onMouseDown()
{
pObjectX = new (buffer) ObjectX;

// do something here

}

void onCaptureChanged()
{
// finish doing something here

pObjectX->~ObjectX();
operator delete (pObjectX_, buffer);
}

ObjectX* pOBjectX;

};

I'm not sure what you're even trying to do here.


See above for clarification.

If you don't call it, it doesn't do anything.


It gets called if the constructor in a placement new throws an
exception.


*If* you have a placement new which actually allocates new
resources,


No. My example does not.

you should provide a corresponding placement delete.
If in a new expression which uses your placement new function,
the constructor of the object exits with an exception, the
compiler will call your placement delete.

Not sure how it will call 'my placement delete' unless you're
referring to a class member. My example does not define a 'placement
delete'. I am wondering if I should invoke the global placement
delete ... (hope that makes sense).
That's the only time it can or will be called.

Thanks for your initial input ... hopefully, my comments make things a
little clearer?

Thanks,

-Luther
 
G

Gianni Mariani

Pete Becker wrote:
....
or, if you want the compiler to figure out the optimal alignment:

aligned_storage<sizeof(ObjectX)>::type data;
ObjectX *ptr = new(&data) ObjectX;

How does the proposal define "optimal" alignment?
 
J

James Kanze

"James Kanze" <[email protected]> wrote in message
He'll have to call the delete operator explicitely in his code
somewhere to get any behavior defined in the class's
destructor. Use of placement delete should be used
symmetrically with placement new.

[Hey, this one worked. Now I'll have to go back and figure
out where I posted it from:).]

I'm not too sure what you mean by "call the delete operator
explicitely", but placement new and placement delete are not
symmetrical. Placement new is called by means of a new
expression; there is no way to invoke placement delete by means
of a delete expression, which always uses a non-placement
delete.

The most frequent use of placement delete is when separating
memory management and construction/deletion, in e.g. a container
class. This uses the standard placement delete, the one with
the void* argument. And you never call delete on such objects,
you call the destructor explicitly.

In the case of user defined placement new which do require a
delete, e.g. when the argument is the address of a special
allocator or pool, you also have to redefine the non-placement
operator new and operator delete functions, and arrange somehow
in the non-placement operator delete function to handle the
special cases.
 
J

James Kanze

It doesn't allocate memory. It essentially just returns a pointer.

The memory has to be allocated somehow, in every case. You
can't construct an object without having the memory for it.

I had more or less assumed that the memory would be declared as
as local variable. In which case, the comparison isn't with
non-placement delete, but with just declaring a local instance
of the object.
Not sure I follow

If you've declared buffer as a local (or a static) object:
char buffer[ sizeof( ObjectX ) ] ;
you're not at all guaranteed that it will be correctly aligned
for an ObjectX; on my system, if very often won't be.
but then, maybe my example wasn't clear.
struct ObjectX { int x; int y; };
struct UserAnObject
{
char buffer[sizeof(ObjectX)];
void onMouseDown()
{
pObjectX = new (buffer) ObjectX;
// do something here
}
void onCaptureChanged()
{
// finish doing something here
pObjectX->~ObjectX();
operator delete (pObjectX_, buffer);
}
ObjectX* pOBjectX;
};

And where is UserAnObject declared? In this case (a bit
special), you're garanteed that the buffer has the same address
as the whole object (but only because UserAnObject is a
POD---add a constructor, for example, and you loose the
guarantee), but this doesn't necessarily mean that it is
sufficiently aligned for an ObjectX. Again, on my system, when
compiling in 32 bit mode, the compiler will ensure that all
UserAnObject are aligned on an address multiple of 4, because
this is necessary for the pointer it contains, but if ObjectX
contains a double, it requires an alignment on a multiple of 8.

If UserAnObject in fact contains virtual functions, the compiler
will insert the vptr before "char buffer[]", almost guaranteeing
that it will NOT be correctly aligned for a double if
UserAnObject is dynamically allocated.

Also: you don't really have to call operator delete here. The
standard guarantees that it is a no-op, but in general, if the
operator new called by the new expression doesn't allocate any
resources, you don't need to call operator delete.
See above for clarification.

OK. It's not necessary, and it's not usual to see it (although
the standard guarantees that it will work).

[...]
Not sure how it will call 'my placement delete' unless you're
referring to a class member. My example does not define a 'placement
delete'. I am wondering if I should invoke the global placement
delete ... (hope that makes sense).

For the standard placement new and delete, there's really never
any reason to call placement delete. For a user defined one,
however...

Consider something like:

union BlockHeader { Allocator* alloc, double forAlignment } ;

void*
operator new( size_t n, Allocator* pAlloc )
{
BlockHeader* p =
static_cast< BlockHeader* >(
pAlloc->alloc( n + sizeof( BlockHeader ) ) ;
if ( p == NULL ) {
throw std::bad_alloc() ;
}
p->alloc = pAlloc ;
return p + 1 ;
}

void*
operator new( size_t n )
{
BlockHeader* p =
static_cast< BlockHeader* >( malloc( n +
sizeof( BlockHeader ) ) ;
if ( p == NULL ) {
throw std::bad_alloc() ;
}
p->alloc = NULL ;
return p + 1 ;
}

void
operator delete( void* userPtr )
{
BlockHeader* p
= static_cast< BlockHeader* >( userPtr ) - 1 ;
if ( p->alloc == NULL ) {
free( p ) ;
} else {
p->alloc->free( p ) ;
}
}

According to the rules, if you do something like:

MyType* p = new ( someAllocator ) MyType ;

and the constructor of MyType exits via an exception, no
operator delete function will be called. If you have a
corresponding placement delete, however, it will be called.
Since in this case, you are actually allocating memory in your
placement new, you should provide a placement delete, so that it
will be called.
 
J

JohnQ

"James Kanze" <[email protected]> wrote in message
He'll have to call the delete operator explicitely in his code
somewhere to get any behavior defined in the class's
destructor. Use of placement delete should be used
symmetrically with placement new.

" [Hey, this one worked. Now I'll have to go back and figure
out where I posted it from:).]"

(No it didn't. Because the post was so short, I put the '>' symbols in by
hand. Notice they're missing the usual space after the symbols.)

"I'm not too sure what you mean by "call the delete operator
explicitely",

SomeClassObjectPtr->~SomeClassObject()

" but placement new and placement delete are not
symmetrical. Placement new is called by means of a new
expression; there is no way to invoke placement delete by means
of a delete expression, which always uses a non-placement
delete."

I'm probably using the terminology "placement delete" wrong. But it would
seem fitting in my context because placement new is object construction and
the above explicit call of the class delete is object destruction. I
understand that there is a delete operator with a void* argument hidden
somewhere (internal to the compiler?). Why object construction was
obfuscated in the placement new syntax befuddles me.

"The most frequent use of placement delete is when separating
memory management and construction/deletion, in e.g. a container
class. This uses the standard placement delete, the one with
the void* argument. And you never call delete on such objects,
you call the destructor explicitly."

Define "standard placement delete" please.

"In the case of user defined placement new which do require a
delete, e.g. when the argument is the address of a special
allocator or pool, you also have to redefine the non-placement
operator new and operator delete functions, and arrange somehow
in the non-placement operator delete function to handle the
special cases."

Define "user defined placement new" please. I don't understand how that can
be useful. So it can be redefined? What good would that do since the
mechanics of calling constructors is not exposed? When I think of placement
new, I think of the syntax that engages the behavior (construction), not the
actual implementation of placement new.

John
 
L

LuB

It doesn't allocate memory. It essentially just returns a pointer.

The memory has to be allocated somehow, in every case. You
can't construct an object without having the memory for it.

I had more or less assumed that the memory would be declared as
as local variable. In which case, the comparison isn't with
non-placement delete, but with just declaring a local instance
of the object.
Not sure I follow

If you've declared buffer as a local (or a static) object:
char buffer[ sizeof( ObjectX ) ] ;
you're not at all guaranteed that it will be correctly aligned
for an ObjectX; on my system, if very often won't be.


but then, maybe my example wasn't clear.
struct ObjectX { int x; int y; };
struct UserAnObject
{
char buffer[sizeof(ObjectX)];
void onMouseDown()
{
pObjectX = new (buffer) ObjectX;
// do something here
}
void onCaptureChanged()
{
// finish doing something here
pObjectX->~ObjectX();
operator delete (pObjectX_, buffer);
}
ObjectX* pOBjectX;
};

And where is UserAnObject declared? In this case (a bit
special), you're garanteed that the buffer has the same address
as the whole object (but only because UserAnObject is a
POD---add a constructor, for example, and you loose the
guarantee), but this doesn't necessarily mean that it is
sufficiently aligned for an ObjectX. Again, on my system, when
compiling in 32 bit mode, the compiler will ensure that all
UserAnObject are aligned on an address multiple of 4, because
this is necessary for the pointer it contains, but if ObjectX
contains a double, it requires an alignment on a multiple of 8.

If UserAnObject in fact contains virtual functions, the compiler
will insert the vptr before "char buffer[]", almost guaranteeing
that it will NOT be correctly aligned for a double if
UserAnObject is dynamically allocated.

Also: you don't really have to call operator delete here. The
standard guarantees that it is a no-op, but in general, if the
operator new called by the new expression doesn't allocate any
resources, you don't need to call operator delete.
See above for clarification.

OK. It's not necessary, and it's not usual to see it (although
the standard guarantees that it will work).

[...]
Not sure how it will call 'my placement delete' unless you're
referring to a class member. My example does not define a 'placement
delete'. I am wondering if I should invoke the global placement
delete ... (hope that makes sense).

For the standard placement new and delete, there's really never
any reason to call placement delete. For a user defined one,
however...

Consider something like:

union BlockHeader { Allocator* alloc, double forAlignment } ;

void*
operator new( size_t n, Allocator* pAlloc )
{
BlockHeader* p =
static_cast< BlockHeader* >(
pAlloc->alloc( n + sizeof( BlockHeader ) ) ;
if ( p == NULL ) {
throw std::bad_alloc() ;
}
p->alloc = pAlloc ;
return p + 1 ;
}

void*
operator new( size_t n )
{
BlockHeader* p =
static_cast< BlockHeader* >( malloc( n +
sizeof( BlockHeader ) ) ;
if ( p == NULL ) {
throw std::bad_alloc() ;
}
p->alloc = NULL ;
return p + 1 ;
}

void
operator delete( void* userPtr )
{
BlockHeader* p
= static_cast< BlockHeader* >( userPtr ) - 1 ;
if ( p->alloc == NULL ) {
free( p ) ;
} else {
p->alloc->free( p ) ;
}
}

According to the rules, if you do something like:

MyType* p = new ( someAllocator ) MyType ;

and the constructor of MyType exits via an exception, no
operator delete function will be called. If you have a
corresponding placement delete, however, it will be called.
Since in this case, you are actually allocating memory in your
placement new, you should provide a placement delete, so that it
will be called.

Thanks for the extended response. Everyone's responses have been quite
helpful.

So I'm taking a step back. Many people avoid certain idioms like the
plague ... such as using memset as a fast, cheap way to copy entire
objects, etc. It just opens up lots of potentially unwanted behavior
or conditions.

So - on a slightly deeper or higher level, what about this 'approach'?
We've talked about explicitly invoking the delete operator - but
underneath that ... is placement new a good option in this situation?

I could change the object architecture a little bit -- and edit my
class to have build and destroy functionality in methods ... instead
of ctor and dtor pairs - and I could just allocate said object with
the instance ... and explicitly objectX.build(...) and
objectX.destroy(...) the same object over and over again ... if that
approach would be better than using placement new.

I guess, I understand that I can't guarantee alignment ... and, I know
that were I writing Assembly, that means I'd have to go out of my way
to read the data ... but what does that mean in a normal compiler? I
thought the compiler would hide things like that from me. Does it mean
things will be slower than optimal? or does that mean things will
break? or does that mean things are undefined on different OSs or with
different compilers? Admittedly, its not a commonly used idiom (I
guess) and while I guess things like alignment might not be guaranteed
- I'm not sure what that means - in terms of "Go For It" ... but
realize it'll be problematic ..? or stay away from it - since you open
a bag of worms.

I didn't hear alot of negative reaction ... but then again, we were
focussed on the need to explicitly invoke the delete operator or not.
It seems like a good approach ... Allows me to Initialize in ctor and
Destroy in dtor (traditional OO) -- and its only one object (easy to
manage) ... so 'new' and 'delete' work well in general for my
situation ... but this idea of placement new ... is it really a good
optimization in this context?

For what its worth, the implementation doesn't really _have_ to be
portable. It is part of a proprietary windowing system ... but I'd
like it to be relatively safe, predictable, reliable and faster than
constantly allocating and releasing memory to the OS many times.

Thanks to all,

-Luther
 
J

James Kanze

[...]
So I'm taking a step back. Many people avoid certain idioms like the
plague ... such as using memset as a fast, cheap way to copy entire
objects, etc. It just opens up lots of potentially unwanted behavior
or conditions.
So - on a slightly deeper or higher level, what about this 'approach'?
We've talked about explicitly invoking the delete operator - but
underneath that ... is placement new a good option in this situation?

Maybe. Over all, I'd probably favor a pointer and classical
new, until the profiler said that allocation really was a
bottleneck. It has the advantage of being a simple, easy to
understand way of doing things.
I could change the object architecture a little bit -- and edit my
class to have build and destroy functionality in methods ... instead
of ctor and dtor pairs - and I could just allocate said object with
the instance ... and explicitly objectX.build(...) and
objectX.destroy(...) the same object over and over again ... if that
approach would be better than using placement new.

If you're always building the same type, just adding a
reinitialize function to it would seem to be the best solution.
I guess, I understand that I can't guarantee alignment ... and, I know
that were I writing Assembly, that means I'd have to go out of my way
to read the data ... but what does that mean in a normal compiler? I
thought the compiler would hide things like that from me.

It does when it knows about them. If you declare an ObjectX,
the compiler will assure that it is adequately aligned. The
problem here is that you are declaring a char[]; the compiler
will ensure that it is adequately aligned for a char[], but that
might not be enough for an ObjectX.
Does it mean
things will be slower than optimal? or does that mean things will
break? or does that mean things are undefined on different OSs or with
different compilers?

It's basically undefined or unspecified behavior. On an Intel
architecture, it means that things will be slower. On a Sparc
(and most other architectures I've seen), it means that the
program will core dump. It's quite possible that on some
architecture, you simply get wrong values, or end up modifying
something outside the object when you meant to change a value in
the object.
Admittedly, its not a commonly used idiom (I
guess) and while I guess things like alignment might not be guaranteed
- I'm not sure what that means - in terms of "Go For It" ... but
realize it'll be problematic ..? or stay away from it - since you open
a bag of worms.

You open a bag of worms. I've done similar things once or twice
in the past, when the profiler said they were needed, but you
generally have to be very, very careful.
I didn't hear alot of negative reaction ... but then again, we were
focussed on the need to explicitly invoke the delete operator or not.
It seems like a good approach ... Allows me to Initialize in ctor and
Destroy in dtor (traditional OO) -- and its only one object (easy to
manage) ... so 'new' and 'delete' work well in general for my
situation ... but this idea of placement new ... is it really a good
optimization in this context?

If you take the necessary steps to ensure correct alignment,
there's no reason why it can't be made to work. As I said, I've
done so in the past. But it is additional complexity for
nothing unless you really need it.
For what its worth, the implementation doesn't really _have_ to be
portable. It is part of a proprietary windowing system ... but I'd
like it to be relatively safe, predictable, reliable and faster than
constantly allocating and releasing memory to the OS many times.

Operator delete doesn't necessarily (or even usually) release
memory to the OS. Most of the time, it allocates from and frees
to an arena which it maintains itself, only going to the OS when
it doesn't have enough memory otherwise. If you're dealing with
fixed size objects, it's also fairly simple to implement your
own allocator, to reuse the previously deleted object.
 

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,755
Messages
2,569,537
Members
45,020
Latest member
GenesisGai

Latest Threads

Top