function template

N

nikola

Hi all,
I was working with a simple function template to find the min of two values.
But since I would like the two values to be different (type) I dont know
what kind of value (type) it will return. I tried to write something like
this:

template <class Type1, class Type2, class Type3>
Type3 findMin(Type1 x, Type2 y){

return (x < y) ? x : y;
}

but it says it cannot deduce template argument for 'Type3'
Can anyone help? Thanx
 
L

lallous

nikola said:
Hi all,
I was working with a simple function template to find the min of two values.
But since I would like the two values to be different (type) I dont know
what kind of value (type) it will return. I tried to write something like
this:

template <class Type1, class Type2, class Type3>
Type3 findMin(Type1 x, Type2 y){

return (x < y) ? x : y;
}

but it says it cannot deduce template argument for 'Type3'
Can anyone help? Thanx

Hello

Try calling it as:

findMin<int, int, int>(1, 2);

Like that you have specified all the types.
 
R

Rolf Magnus

nikola said:
Hi all,
I was working with a simple function template to find the min of two
values. But since I would like the two values to be different (type) I
dont know what kind of value (type) it will return. I tried to write
something like this:

template <class Type1, class Type2, class Type3>
Type3 findMin(Type1 x, Type2 y){

return (x < y) ? x : y;
}

but it says it cannot deduce template argument for 'Type3'

Well, first of all, the return type of a function is fixed at compile
time. And how would the compiler know which one to return?
Seond, the 2nd and 3rd argument of the ?: operator need to be of the
same type, too, so one of your arguments is converted to the other's
type anyway. Similar for operator<. So you can just write:

template <class Type>
Type findMin(Type x, Type y){
return (x < y) ? x : y;
}
 
G

Gianni Mariani

nikola said:
Hi all,
I was working with a simple function template to find the min of two values.
But since I would like the two values to be different (type) I dont know
what kind of value (type) it will return. I tried to write something like
this:

template <class Type1, class Type2, class Type3>
Type3 findMin(Type1 x, Type2 y){

return (x < y) ? x : y;
}

but it says it cannot deduce template argument for 'Type3'
Can anyone help? Thanx

This is a classic template issue.

What you really want is :

template <typename T1, typename T2>
typename Promote<T1,T2>::type min( const T1 & x, const T2 & y )
{
return x < y ? x : y;
}

Where Promote is a template that will perform the right "promotion" for
T1 and T2.

If the "typeof()" function was standard, you could simply do this:


template <typename T1, typename T2>
struct Promote
{
typedef typeof( T1() + T2() ) type;
};

.... but alas, things are not so simple in the standard world.

We need to figure it out ourselves so we have to use partial template
specialization.

Start with the basic template:


template <typename T1, typename T2>
struct Promote
{
};

// the same types are the same ! cool

template <typename T1>
struct Promote<T1,T1>
{
typedef T1 type;
};

// specify all the type promotions ...

template <> struct Promote<unsigned char,char> { typedef int type; };
template <> struct Promote<signed char,char> { typedef int type; };
template <> struct Promote<short,char> { typedef int type; };
template <> struct Promote<unsigned short,char> { typedef int type; };
template <> struct Promote<int,char> { typedef int type; };
template <> struct Promote<unsigned int,char> { typedef unsigned int
type; };
template <> struct Promote<long,char> { typedef long type; };
template <> struct Promote<unsigned long,char> { typedef unsigned long
type; };
template <> struct Promote<long long,char> { typedef long long type; };
template <> struct Promote<unsigned long long,char> { typedef unsigned
long long type; };
template <> struct Promote<float,char> { typedef float type; };
template <> struct Promote<double,char> { typedef double type; };
template <> struct Promote<long double,char> { typedef long double type; };
template <> struct Promote<char,unsigned char> { typedef int type; };
template <> struct Promote<signed char,unsigned char> { typedef int type; };
template <> struct Promote<short,unsigned char> { typedef int type; };
template <> struct Promote<unsigned short,unsigned char> { typedef int
type; };
template <> struct Promote<int,unsigned char> { typedef int type; };
template <> struct Promote<unsigned int,unsigned char> { typedef
unsigned int type; };
template <> struct Promote<long,unsigned char> { typedef long type; };
template <> struct Promote<unsigned long,unsigned char> { typedef
unsigned long type; };
template <> struct Promote<long long,unsigned char> { typedef long long
type; };
template <> struct Promote<unsigned long long,unsigned char> { typedef
unsigned long long type; };
template <> struct Promote<float,unsigned char> { typedef float type; };
template <> struct Promote<double,unsigned char> { typedef double type; };
template <> struct Promote<long double,unsigned char> { typedef long
double type; };
template <> struct Promote<char,signed char> { typedef int type; };
template <> struct Promote<unsigned char,signed char> { typedef int type; };
template <> struct Promote<short,signed char> { typedef int type; };
template <> struct Promote<unsigned short,signed char> { typedef int
type; };
template <> struct Promote<int,signed char> { typedef int type; };
template <> struct Promote<unsigned int,signed char> { typedef unsigned
int type; };
template <> struct Promote<long,signed char> { typedef long type; };
template <> struct Promote<unsigned long,signed char> { typedef unsigned
long type; };
template <> struct Promote<long long,signed char> { typedef long long
type; };
template <> struct Promote<unsigned long long,signed char> { typedef
unsigned long long type; };
template <> struct Promote<float,signed char> { typedef float type; };
template <> struct Promote<double,signed char> { typedef double type; };
template <> struct Promote<long double,signed char> { typedef long
double type; };
template <> struct Promote<char,short> { typedef int type; };
template <> struct Promote<unsigned char,short> { typedef int type; };
template <> struct Promote<signed char,short> { typedef int type; };
template <> struct Promote<unsigned short,short> { typedef int type; };
template <> struct Promote<int,short> { typedef int type; };
template <> struct Promote<unsigned int,short> { typedef unsigned int
type; };
template <> struct Promote<long,short> { typedef long type; };
template <> struct Promote<unsigned long,short> { typedef unsigned long
type; };
template <> struct Promote<long long,short> { typedef long long type; };
template <> struct Promote<unsigned long long,short> { typedef unsigned
long long type; };
template <> struct Promote<float,short> { typedef float type; };
template <> struct Promote<double,short> { typedef double type; };
template <> struct Promote<long double,short> { typedef long double type; };
template <> struct Promote<char,unsigned short> { typedef int type; };
template <> struct Promote<unsigned char,unsigned short> { typedef int
type; };
template <> struct Promote<signed char,unsigned short> { typedef int
type; };
template <> struct Promote<short,unsigned short> { typedef int type; };
template <> struct Promote<int,unsigned short> { typedef int type; };
template <> struct Promote<unsigned int,unsigned short> { typedef
unsigned int type; };
template <> struct Promote<long,unsigned short> { typedef long type; };
template <> struct Promote<unsigned long,unsigned short> { typedef
unsigned long type; };
template <> struct Promote<long long,unsigned short> { typedef long long
type; };
template <> struct Promote<unsigned long long,unsigned short> { typedef
unsigned long long type; };
template <> struct Promote<float,unsigned short> { typedef float type; };
template <> struct Promote<double,unsigned short> { typedef double type; };
template <> struct Promote<long double,unsigned short> { typedef long
double type; };
template <> struct Promote<char,int> { typedef int type; };
template <> struct Promote<unsigned char,int> { typedef int type; };
template <> struct Promote<signed char,int> { typedef int type; };
template <> struct Promote<short,int> { typedef int type; };
template <> struct Promote<unsigned short,int> { typedef int type; };
template <> struct Promote<unsigned int,int> { typedef unsigned int type; };
template <> struct Promote<long,int> { typedef long type; };
template <> struct Promote<unsigned long,int> { typedef unsigned long
type; };
template <> struct Promote<long long,int> { typedef long long type; };
template <> struct Promote<unsigned long long,int> { typedef unsigned
long long type; };
template <> struct Promote<float,int> { typedef float type; };
template <> struct Promote<double,int> { typedef double type; };
template <> struct Promote<long double,int> { typedef long double type; };
template <> struct Promote<char,unsigned int> { typedef unsigned int
type; };
template <> struct Promote<unsigned char,unsigned int> { typedef
unsigned int type; };
template <> struct Promote<signed char,unsigned int> { typedef unsigned
int type; };
template <> struct Promote<short,unsigned int> { typedef unsigned int
type; };
template <> struct Promote<unsigned short,unsigned int> { typedef
unsigned int type; };
template <> struct Promote<int,unsigned int> { typedef unsigned int type; };
template <> struct Promote<long,unsigned int> { typedef unsigned long
type; };
template <> struct Promote<unsigned long,unsigned int> { typedef
unsigned long type; };
template <> struct Promote<long long,unsigned int> { typedef long long
type; };
template <> struct Promote<unsigned long long,unsigned int> { typedef
unsigned long long type; };
template <> struct Promote<float,unsigned int> { typedef float type; };
template <> struct Promote<double,unsigned int> { typedef double type; };
template <> struct Promote<long double,unsigned int> { typedef long
double type; };
template <> struct Promote<char,long> { typedef long type; };
template <> struct Promote<unsigned char,long> { typedef long type; };
template <> struct Promote<signed char,long> { typedef long type; };
template <> struct Promote<short,long> { typedef long type; };
template <> struct Promote<unsigned short,long> { typedef long type; };
template <> struct Promote<int,long> { typedef long type; };
template <> struct Promote<unsigned int,long> { typedef unsigned long
type; };
template <> struct Promote<unsigned long,long> { typedef unsigned long
type; };
template <> struct Promote<long long,long> { typedef long long type; };
template <> struct Promote<unsigned long long,long> { typedef unsigned
long long type; };
template <> struct Promote<float,long> { typedef float type; };
template <> struct Promote<double,long> { typedef double type; };
template <> struct Promote<long double,long> { typedef long double type; };
template <> struct Promote<char,unsigned long> { typedef unsigned long
type; };
template <> struct Promote<unsigned char,unsigned long> { typedef
unsigned long type; };
template <> struct Promote<signed char,unsigned long> { typedef unsigned
long type; };
template <> struct Promote<short,unsigned long> { typedef unsigned long
type; };
template <> struct Promote<unsigned short,unsigned long> { typedef
unsigned long type; };
template <> struct Promote<int,unsigned long> { typedef unsigned long
type; };
template <> struct Promote<unsigned int,unsigned long> { typedef
unsigned long type; };
template <> struct Promote<long,unsigned long> { typedef unsigned long
type; };
template <> struct Promote<long long,unsigned long> { typedef long long
type; };
template <> struct Promote<unsigned long long,unsigned long> { typedef
unsigned long long type; };
template <> struct Promote<float,unsigned long> { typedef float type; };
template <> struct Promote<double,unsigned long> { typedef double type; };
template <> struct Promote<long double,unsigned long> { typedef long
double type; };
template <> struct Promote<char,long long> { typedef long long type; };
template <> struct Promote<unsigned char,long long> { typedef long long
type; };
template <> struct Promote<signed char,long long> { typedef long long
type; };
template <> struct Promote<short,long long> { typedef long long type; };
template <> struct Promote<unsigned short,long long> { typedef long long
type; };
template <> struct Promote<int,long long> { typedef long long type; };
template <> struct Promote<unsigned int,long long> { typedef long long
type; };
template <> struct Promote<long,long long> { typedef long long type; };
template <> struct Promote<unsigned long,long long> { typedef long long
type; };
template <> struct Promote<unsigned long long,long long> { typedef
unsigned long long type; };
template <> struct Promote<float,long long> { typedef float type; };
template <> struct Promote<double,long long> { typedef double type; };
template <> struct Promote<long double,long long> { typedef long double
type; };
template <> struct Promote<char,unsigned long long> { typedef unsigned
long long type; };
template <> struct Promote<unsigned char,unsigned long long> { typedef
unsigned long long type; };
template <> struct Promote<signed char,unsigned long long> { typedef
unsigned long long type; };
template <> struct Promote<short,unsigned long long> { typedef unsigned
long long type; };
template <> struct Promote<unsigned short,unsigned long long> { typedef
unsigned long long type; };
template <> struct Promote<int,unsigned long long> { typedef unsigned
long long type; };
template <> struct Promote<unsigned int,unsigned long long> { typedef
unsigned long long type; };
template <> struct Promote<long,unsigned long long> { typedef unsigned
long long type; };
template <> struct Promote<unsigned long,unsigned long long> { typedef
unsigned long long type; };
template <> struct Promote<long long,unsigned long long> { typedef
unsigned long long type; };
template <> struct Promote<float,unsigned long long> { typedef float
type; };
template <> struct Promote<double,unsigned long long> { typedef double
type; };
template <> struct Promote<long double,unsigned long long> { typedef
long double type; };
template <> struct Promote<char,float> { typedef float type; };
template <> struct Promote<unsigned char,float> { typedef float type; };
template <> struct Promote<signed char,float> { typedef float type; };
template <> struct Promote<short,float> { typedef float type; };
template <> struct Promote<unsigned short,float> { typedef float type; };
template <> struct Promote<int,float> { typedef float type; };
template <> struct Promote<unsigned int,float> { typedef float type; };
template <> struct Promote<long,float> { typedef float type; };
template <> struct Promote<unsigned long,float> { typedef float type; };
template <> struct Promote<long long,float> { typedef float type; };
template <> struct Promote<unsigned long long,float> { typedef float
type; };
template <> struct Promote<double,float> { typedef double type; };
template <> struct Promote<long double,float> { typedef long double type; };
template <> struct Promote<char,double> { typedef double type; };
template <> struct Promote<unsigned char,double> { typedef double type; };
template <> struct Promote<signed char,double> { typedef double type; };
template <> struct Promote<short,double> { typedef double type; };
template <> struct Promote<unsigned short,double> { typedef double type; };
template <> struct Promote<int,double> { typedef double type; };
template <> struct Promote<unsigned int,double> { typedef double type; };
template <> struct Promote<long,double> { typedef double type; };
template <> struct Promote<unsigned long,double> { typedef double type; };
template <> struct Promote<long long,double> { typedef double type; };
template <> struct Promote<unsigned long long,double> { typedef double
type; };
template <> struct Promote<float,double> { typedef double type; };
template <> struct Promote<long double,double> { typedef long double
type; };
template <> struct Promote<char,long double> { typedef long double type; };
template <> struct Promote<unsigned char,long double> { typedef long
double type; };
template <> struct Promote<signed char,long double> { typedef long
double type; };
template <> struct Promote<short,long double> { typedef long double type; };
template <> struct Promote<unsigned short,long double> { typedef long
double type; };
template <> struct Promote<int,long double> { typedef long double type; };
template <> struct Promote<unsigned int,long double> { typedef long
double type; };
template <> struct Promote<long,long double> { typedef long double type; };
template <> struct Promote<unsigned long,long double> { typedef long
double type; };
template <> struct Promote<long long,long double> { typedef long double
type; };
template <> struct Promote<unsigned long long,long double> { typedef
long double type; };
template <> struct Promote<float,long double> { typedef long double type; };
template <> struct Promote<double,long double> { typedef long double
type; };

//
// and now the min template ...

template <typename T1, typename T2>
typename Promote<T1,T2>::type min( const T1 & x, const T2 & y )
{
return x < y ? x : y;
}

That's a bit long winded. However, some people might arge that type
promotion is a bad thing and that the developer really needs to make
sure that the parameters to min (or max) are the same type, in which
case the template becomes easy to write.
 
P

Pete Becker

nikola said:
template <class Type1, class Type2, class Type3>
Type3 findMin(Type1 x, Type2 y){

return (x < y) ? x : y;
}

but it says it cannot deduce template argument for 'Type3'
Can anyone help? Thanx

#define findMin(x, y) ((x) < (y)) ? (x) : (y))

Otherwise you have to write template code that duplicates the promotion
rules, and that will be rather long-winded.
 
J

Jeff Schwab

Pete said:
#define findMin(x, y) ((x) < (y)) ? (x) : (y))

Otherwise you have to write template code that duplicates the promotion
rules, and that will be rather long-winded.

Gee, that's neat. Until somebody tries to

findMin( ++i, j );
 
P

Pete Becker

Jeff said:
Gee, that's neat. Until somebody tries to

findMin( ++i, j );

Yes, that's a limitation. The proposed template solutions also have
limitations: one requires you to specify the return type at each call
site, one requires a loooong list of template specializations, and one
won't compile. Which do you prefer?
 
J

Jeff Schwab

Pete said:
Yes, that's a limitation. The proposed template solutions also have
limitations: one requires you to specify the return type at each call
site, one requires a loooong list of template specializations, and one
won't compile. Which do you prefer?

The one with the long list of specializations, which can be (and now
have been) written in a single file, and henceforth hardly need be
noticed again.
 
P

Pete Becker

Jeff said:
The one with the long list of specializations, which can be (and now
have been) written in a single file, and henceforth hardly need be
noticed again.

Except when it has to be modified to handle additional types, which is a
common failing of brute force techniques.
 
D

Derek

The one with the long list of specializations, which
Except when it has to be modified to handle additional
types, which is a common failing of brute force
techniques.

True, but in this case we are forced to choose between
the lesser of two evils: the dreaded macro or a brute
force mess of template specializations. The best
compromise I have found is from the Blitz++ library.
The solution doesn't always adhere to C++ promotion
rules, but comes very close without (as much) brute
force:

http://cvs.sourceforge.net/viewcvs.py/blitz/blitz/blitz/promote.h

This common problem seems to indicate that C++ could
be made more template metaprogramming friendly.
 
P

Pete Becker

Derek said:
True, but in this case we are forced to choose between
the lesser of two evils: the dreaded macro or a brute
force mess of template specializations.

Yup. The first is a dogmatic objection and the second is a technical
issue.
This common problem seems to indicate that C++ could
be made more template metaprogramming friendly.

Or that dogma isn't a good basis for design decisions.
 
D

Derek

True, but in this case we are forced to choose between
Yup. The first is a dogmatic objection and the second is
a technical issue.


Or that dogma isn't a good basis for design decisions.

In this case I will concede the point and agree that a macro
is a perfectly acceptable solution. However, in less trivial
cases where macros can't be used, I still think C++ could be
improved in this department. But that's a whole different
discussion.
 
J

Jeff Schwab

Pete said:
Except when it has to be modified to handle additional types, which is a
common failing of brute force techniques.

No, it *never* needs to be modified to handle additional types. C++
only has so many built-in types. If other types have non-intuitive
semantics, specializations can be added separately. It already is
common to provide certainly traits classes as part of a complete type
(numeric_limits, iterator_traits, etc.). If additional, custom traits
classes are desired, they can be written separately.

The nature of the C++ type system has undergone a great deal of careful
scrutiny, and allows new functionality (like the OP's find_min function)
to be provided in easy-to-use libraries that can be extended as
necessary. The implementation of such functionality frequently does
require much more code than a C-like, macro-based solution, but this is
a reflection of the fact that so much more attention is being payed to
the details and potential use of the types involved.
 
P

Pete Becker

Jeff said:
No, it *never* needs to be modified to handle additional types. C++
only has so many built-in types.

Any one compiler that you use only has so many built-in types. But
different compilers, including other versions of whichever one you're
using, can support different sets of types (e.g. __int64 on several
compilers), and as C++ evolves it may well support additional standard
types (long long comes immediately to mind). And, of course, there are
also user-defined arithmetic classes. Never say never.
 
J

Jeff Schwab

Pete said:
Any one compiler that you use only has so many built-in types. But
different compilers, including other versions of whichever one you're
using, can support different sets of types (e.g. __int64 on several
compilers), and as C++ evolves it may well support additional standard
types (long long comes immediately to mind). And, of course, there are
also user-defined arithmetic classes. Never say never.

I say never. The fact that a compiler may (and many do) provide types
beyond those specified by the standard doesn't mean that specializations
for those types need to be defined in the same file as the usual
specializations. These additional types can be specialized separately,
just like any other, or a library vendor may opt to include them in the
same file for convenience.
 
P

Pete Becker

Jeff said:
I say never. The fact that a compiler may (and many do) provide types
beyond those specified by the standard doesn't mean that specializations
for those types need to be defined in the same file as the usual
specializations.

Nice try, but nobody said anything about "the same file." The point is
that you have to add a bunch more specializaitons for every type that
isn't in your original implementation.
These additional types can be specialized separately,
just like any other,

That doesn't solve the problem of having to add them for any additional
types you want to support.
or a library vendor may opt to include them in the
same file for convenience.

So are you planning to pay a library vendor to maintain your code, or
will they just do it out of the kindness of their hearts?
 
J

Jeff Schwab

Pete said:
Nice try,

Try at what? I'm expressing my own opinion, that's all. If you
disagree, I wish you nothing but the best with your own approach. Of
course, I'm not likely to license any code from you that has a
macro-based interface... ;)
but nobody said anything about "the same file." The point is
that you have to add a bunch more specializaitons for every type that
isn't in your original implementation.

Nonsense. Default implementations handle most cases. Additional
specializations may be provided for performance reasons, or in rare
cases may be truly necessary, but they are just part of generic
programming, as functions are part of procedural programming.
That doesn't solve the problem of having to add them for any additional
types you want to support.

You don't have to add specializations for all new types. Only the types
with which particular traits classes might be needed, and for which the
default template definitions don't work properly.
So are you planning to pay a library vendor to maintain your code, or
will they just do it out of the kindness of their hearts?

Eh? Necessary specializations of traits classes for new types aren't
"maintenance," they're separate entities, and may be sold separately.
If you're arguing that traits classes are more hassle than they're
worth, then there's really not much point in continuing this conversation.
 
P

Pete Becker

Jeff said:
Try at what?

At changing the subject.
Nonsense. Default implementations handle most cases.

Look again: every one of those approximately 100 specialiations is
needed. Every time you compare two objects of different types you need a
specialization. The default implementation only handles objects of the
same type. And that's the problem: this approach requires a
specialization for every pair of distinct types.
Additional
specializations may be provided for performance reasons, or in rare
cases may be truly necessary, but they are just part of generic
programming, as functions are part of procedural programming.

No, this approach requires a template specialization for every pair of
distinct types that the program compares.
You don't have to add specializations for all new types.

I didn't say "for all new types". I said "for any additional types YOU
WANT TO SUPPORT." (emphasis added for the reading impaired)
Only the types
with which particular traits classes might be needed, and for which the
default template definitions don't work properly.

The original question, if you recall, was about handling pairs of
arguments of different types. This approach requires a template
specialization for every such pair. So, yes, it's "only" those types,
but they're the only ones that are interesting. Handling pairs of the
same type is simple.
Eh? Necessary specializations of traits classes for new types aren't
"maintenance," they're separate entities, and may be sold separately.

Word games. Adding stuff in order to make your template work is
maintenance, regardless of whether it's "sold separately." Not that it's
at all clear why someone would be selling these things: the question was
how to write a template that compares two objects of different types,
and the proposed solution consists of a long list of template
specializations. Who do you think is going to sell template
specializations for class MyType? And how will they know that you need
to compare Mytype and YourType objects, so they can provide the
appropriate specializations?
If you're arguing that traits classes are more hassle than they're
worth, then there's really not much point in continuing this conversation.

I've said nothing about traits classes. What I said is that writing
N*(N-1) template specializations to handle all pairs of N argument types
isn't my idea of clean code. I prefer to let the compiler do that kind
of work -- it's much better at it. (Note that the sample implementation
doesn't get the platform-dependent promotion semantics right, while the
compiler almost certainly does).
 
D

Denis Remezov

Pete said:
Yes, that's a limitation. The proposed template solutions also have
limitations: one requires you to specify the return type at each call
site, one requires a loooong list of template specializations, and one
won't compile. Which do you prefer?

Another problem I've encountered with the macro approach is that the
compiler's choice of promotion is not always appropriate.
Here is a quick example:

//////////////////////////////////////////////////////
#include <iostream>

template <typename T>
T const& self_ref(T const& v) {
return v;
}

#define find_max_(a, b) ((a) > (b)? (a) : (b))

int main() {
unsigned int a = 0xffffffff;

double x = self_ref(find_max_(a, 5.f));

std::cout.setf(std::ios::fixed);
std::cout<<"a:\t"<<a<<std::endl;
std::cout<<"x:\t"<<x<<std::endl;

return 0;
}
//////////////////////////////////////////////////////


Let's say int is 4 bytes, float is IEEE 754 single, double is double format.
The output that I receive with several compilers is:
a: 4294967295
x: 4294967296.000000

This is not what I want.

Apparently, the float-integral conversion in the macro expression doesn't
consider a promotion to double; and it shouldn't (please correct me
if I'm wrong).

The purpose of self_ref is that without it gcc 3.3.3 did manage to get
the "right" result by optimising away the intermediate float value.

Denis
 

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

Forum statistics

Threads
474,169
Messages
2,570,920
Members
47,464
Latest member
Bobbylenly

Latest Threads

Top