has_member help

J

Jonathan Lee

I've been trying to find a way to enable templates for a class based
on
whether or not that class has a member function. So far, all the
examples
I can find are if class T has member "known_name". But I'm sorta
looking
for the opposite: if some known class has an overloaded member
function.
Any ideas on this? I'm not very good with these enable_if type
things...

As a concrete example, suppose I have

class BigInt {
public:
BigInt& operator+=(unsigned long);
BigInt& operator+=(long);
};

and I want to enable operator+(const BigInt&, T) if BigInt::eek:perator
+=(T)
exists (so, for unsigned long and long, but not int or unsigned int in
this
example).

Thanks
--Jonathan
 
A

Alf P. Steinbach /Usenet

* Jonathan Lee, on 12.08.2010 06:05:
I've been trying to find a way to enable templates for a class based
on
whether or not that class has a member function. So far, all the
examples
I can find are if class T has member "known_name". But I'm sorta
looking
for the opposite: if some known class has an overloaded member
function.
Any ideas on this? I'm not very good with these enable_if type
things...

As a concrete example, suppose I have

class BigInt {
public:
BigInt& operator+=(unsigned long);
BigInt& operator+=(long);
};

and I want to enable operator+(const BigInt&, T) if BigInt::eek:perator
+=(T)
exists (so, for unsigned long and long, but not int or unsigned int in
this example).

Not sure that the design makes good sense, but you can always do like


template< class Type >
BigInt operator+( BigInt const& a, Type b )
{
static_cast< BigInt& (BigInt::*operator+)( Type ) >( &BigInt::eek:perator+ );

BigInt result = a;
result += b;
return result;
}


Disclaimer: I haven't tried to compile that.

But it should fail to compile if BigInt lacks the appropriate operator, i.e.
prohibiting implicit conversion.


Cheers & hth.,

- Alf
 
S

Stefan van Kessel

template< class Type >
BigInt operator+( BigInt const& a, Type b )
{
static_cast< BigInt& (BigInt::*operator+)( Type ) >( &BigInt::eek:perator+ );

BigInt result = a;
result += b;
return result;
}


Disclaimer: I haven't tried to compile that.

Clever :) With the benefit of using a compiler, I can fix two minor
mistakes:

template< class Type >
BigInt operator+( BigInt const& a, Type b )
{
static_cast< BigInt& (BigInt::*)( Type ) >( &BigInt::eek:perator+= );

BigInt result = a;
result += b;
return result;
}
 
V

Vladimir Jovic

Stefan said:
template< class Type >
BigInt operator+( BigInt const& a, Type b )
{
static_cast< BigInt& (BigInt::*)( Type ) >( &BigInt::eek:perator+= );

BigInt result = a;
result += b;
return result;
}

Very nice. This is the first time I see something like this :)

So, what exactly happens there?
Is the signature
BigInt& operator+=(unsigned long);
static_cast-ed to this
BigInt& operator+=( Type );
?

What happens when Type is a type which doesn't have conversion operator
to long or unsigned long? Compile error?
 
F

Francesco S. Carta

Very nice. This is the first time I see something like this :)

So, what exactly happens there?
Is the signature
BigInt& operator+=(unsigned long);
static_cast-ed to this
BigInt& operator+=( Type );
?

What happens when Type is a type which doesn't have conversion operator
to long or unsigned long? Compile error?

Why don't you try it out to check if that code is actually doing what
it's expected to do _on your compiler_?

On-the-field testing, the best way to find out.
 
V

Vladimir Jovic

Francesco said:
Why don't you try it out to check if that code is actually doing what
it's expected to do _on your compiler_?

On-the-field testing, the best way to find out.

I tried, and it failed to compile. Hence the questions.



The example :
// code
#include <string>
class BigInt
{
public:
BigInt& operator+=(unsigned long a)
{
b += a;
}


unsigned long b;
};
template< class Type >
BigInt operator+( BigInt const& a, Type b )
{
static_cast< BigInt& (BigInt::*)( Type ) >( &BigInt::eek:perator+= );

BigInt result = a;
result += b;
return result;
}
int main()
{
BigInt nr;
nr.b = 5;

std::string type1( "abc" );
unsigned int type2( 22 );
unsigned long type3( 123 );

//const BigInt res1 = nr + type1;
//const BigInt res2 = nr + type2;
const BigInt res3 = nr + type3;
}
// end of code

The commented lines are breaking the compilation, with next error :
g++ dfg.cpp
dfg.cpp: In function ‘BigInt operator+(const BigInt&, Type) [with Type =
unsigned int]’:
dfg.cpp:37: instantiated from here
dfg.cpp:20: error: invalid static_cast from type ‘BigInt&
(BigInt::*)(long unsigned int)’ to type ‘BigInt& (BigInt::*)(unsigned int)’


Can someone modify my example to make it compile when type2 (int) is used?

I understand the type1 (std::string) should not compile.
 
B

Bart van Ingen Schenau

Very nice. This is the first time I see something like this :)

So, what exactly happens there?
Is the signature
                BigInt& operator+=(unsigned long);
static_cast-ed to this
                BigInt& operator+=( Type );
?

Sort of, but not really.
The pointer to a member function, that results from the expression
&BigInt::eek:perator+= is type-cast to a specific type of pointer to
member function, which accepts a Type argument and returns a BigInt
reference.
If the BigInt class does not have an operator+= overload that matches
the signature, then the conversion fails and the source code is ill-
formed (i.e. the compiler must give a diagnostic).
What happens when Type is a type which doesn't have conversion operator
to long or unsigned long? Compile error?

Yes, but also when Type does not match exactly with either long or
unsigned long.
So, you will also get a compile error if Type=int.

Bart v Ingen Schenau
 
F

Francesco S. Carta

I tried, and it failed to compile. Hence the questions.

Sorry, I might have misunderstood your question. You asked for "What
happens" and you pointed out "Compiler error?", hence I decided to tell
you to try - and that was exactly what happened, compiler error.

But now I understand you want to know what exactly is raising the
compile time error. I'll interleave some further lines:
The example :
// code
#include <string>
class BigInt
{
public:
BigInt& operator+=(unsigned long a)

This function as a well defined signature, it takes an unsigned long.
{
b += a;
}


unsigned long b;
};
template< class Type >
BigInt operator+( BigInt const& a, Type b )
{
static_cast< BigInt& (BigInt::*)( Type ) >( &BigInt::eek:perator+= );

This is the line breaking the compilation when Type is not "unsigned
long". That line does nothing more than attempting the conversion in
order to check the if "Type == unsigned long".
BigInt result = a;
result += b;
return result;
}
int main()
{
BigInt nr;
nr.b = 5;

std::string type1( "abc" );
unsigned int type2( 22 );
unsigned long type3( 123 );

//const BigInt res1 = nr + type1;

this tries:

static_cast< BigInt& (BigInt::*)(std::string) >( &BigInt::eek:perator+= );

and fails

//const BigInt res2 = nr + type2;

this tries:

static_cast< BigInt& (BigInt::*)(unsigned int) >( &BigInt::eek:perator+= );

and fails

const BigInt res3 = nr + type3;

this tries:

static_cast< BigInt& (BigInt::*)(unsigned long)>(&BigInt::eek:perator+=);

and succeeds

}
// end of code

The commented lines are breaking the compilation, with next error :
g++ dfg.cpp
dfg.cpp: In function ‘BigInt operator+(const BigInt&, Type) [with Type =
unsigned int]’:
dfg.cpp:37: instantiated from here
dfg.cpp:20: error: invalid static_cast from type ‘BigInt&
(BigInt::*)(long unsigned int)’ to type ‘BigInt& (BigInt::*)(unsigned int)’


Can someone modify my example to make it compile when type2 (int) is used?

Just remove that check, so that the template will be correctly
instantiated for any Type which can be implicitly converted to "unsigned
long"

Otherwise define:

BigInt& operator+=(unsigned int a);

If you want to keep full control about the accepted types, use the
latter solution, because the former should accept signed to unsigned
conversions which might be not what you want.

About this issue in general there is another solution which passes
through a template idiom that creates a static assert and uses that
assert with numeric_limits, but that's maybe overkill here, though that
would give deeper control on the properties of the types allowed in the
template at hand.
I understand the type1 (std::string) should not compile.

Right.

Disclaimer, all the above is straight from the top of my head, do the
appropriate tests to check if I got this post right.
 
J

Jonathan Lee

Clever :) With the benefit of using a compiler, I can fix two minor
mistakes:

template< class Type >
BigInt operator+( BigInt const& a, Type b )
{
        static_cast< BigInt& (BigInt::*)( Type ) >( &BigInt::eek:perator+= );

        BigInt result = a;
        result += b;
        return result;



}

Good ideas, Alf and Stefan. I was kinda hoping for something more
SFINAE-like, though, to just disqualify the template. In particular,
I have other templates which could or should be used instead.

I've seen some talk on the Boost mailing list for using a trait
called has_member in combination with enable_if, but a) it seems to
have disappeared from usage and b) it doesn't exactly do what I
want. I'll try and give it another go this evening. Maybe I can
adapt what you've presented here.

Thanks,
--Jonathan
 
F

Francesco S. Carta

Sorry, I might have misunderstood your question. You asked for "What
happens" and you pointed out "Compiler error?", hence I decided to tell
you to try - and that was exactly what happened, compiler error.

Self correction: the above should read "compile time error" and not
"compiler error", of course...
 
A

Anthony Williams

Jonathan Lee said:
I've been trying to find a way to enable templates for a class based
on
whether or not that class has a member function. So far, all the
examples
I can find are if class T has member "known_name". But I'm sorta
looking
for the opposite: if some known class has an overloaded member
function.
Any ideas on this? I'm not very good with these enable_if type
things...

As a concrete example, suppose I have

class BigInt {
public:
BigInt& operator+=(unsigned long);
BigInt& operator+=(long);
};

and I want to enable operator+(const BigInt&, T) if BigInt::eek:perator
+=(T)
exists (so, for unsigned long and long, but not int or unsigned int in
this
example).

I've been working on this problem for another context, so have some code
handy.

template<typename T>
struct has_member_op_plus
{
typedef char small_type;
struct large_type
{
small_type dummy[2];
};

struct fallback_type
{
int operator+=(int);
};
struct derived_type:
T, fallback_type
{};
template<int (fallback_type::*)(int)> struct tester;

template<typename U>
static small_type has_member(tester<&U::eek:perator+=>*);
template<typename U>
static large_type has_member(...);

static const bool value=sizeof(has_member<derived_type>(0))==sizeof(large_type);
};

has_member_op_plus<T>::value is true if T has at least one member
overload of operator+=, and false otherwise. If there is only overload
of operator+= and that is private then you get a compilation error.

template<typename T,typename P,typename R,bool=has_member_op_plus<T>::value>
struct has_member_op_plus_with_P_param
{
static const bool value=false;
};

template<typename T,typename P,typename R>
struct has_member_op_plus_with_P_param<T,P,R,true>
{
typedef char true_type;
struct false_type
{
true_type dummy[2];
};

template<R (T::*)(P)> struct tester;

template<typename U>
static true_type has_member(tester<&U::eek:perator+=>*);
template<typename U>
static false_type has_member(...);

static const bool value=sizeof(has_member<T>(0))==sizeof(true_type);
};

has_member_op_plus_with_P_param<T,P,R>::value is true if T has a member

R operator+=(P);

and false otherwise. If the specified operator+= is private then you get
a compilation error. If the specified operator+= is defined in a base
class then the result is "false". If the operator is a non-member
function such as

BigInt& operator+=(BigInt const&,int);

then the result is false.

So, in your case, you want:

template<typename T>
typename std::enable_if<
has_member_op_plus_with_P_param<BigInt,T,BigInt&>::value,
BigInt>::type
operator+(const BigInt& lhs, T rhs)
{
BigInt temp(lhs);
temp+=rhs;
return temp;
}

HTH,

Anthony
 
J

Jonathan Lee

I've been working on this problem for another context, so have some code
handy.

Thanks! That looks.. elaborate.. but it does exactly what I want.

--Jonathan
 

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
473,774
Messages
2,569,596
Members
45,144
Latest member
KetoBaseReviews
Top