 |
C++Talk.NET C++ language newsgroups
|
| View previous topic :: View next topic |
| Author |
Message |
Alan Lehotsky Guest
|
Posted: Fri Sep 17, 2004 10:49 am Post subject: template function specialization troubles |
|
|
I'm trying to write a templated function with the following behavior.
Given a type T and an integer width, write a function that creates a
constant of either type T (for POD T) or of type T::basetype for any
aggregate type having a T::basetype that's a POD. The constant is
composed of a bitstring of length 'width'.
The following works as long as I don't try to handle aggregate types.
But SFINAE doesn't stop gcc (2.95.3 or 3.4.2) from complaining about
the specialization
template<> inline T::basetype MASK<T>(int width) ....
producing about 80 lines of errors all told. I've elided some that I'm
pretty sure are irrelevant. How can I make the return type of the
function do the right thing here?
=================================================
struct composite {
int i;
typedef int basetype;
};
template <class T> inline T MASK (int width) {
if (width < 0 || width > (sizeof(T)* )
return T (0);
else if (width == (sizeof(T)* )
return ~T (0);
else
return T(~((~T(0)) << width));
}
// Specialization for composite data types
#ifdef BAD
template <> inline T::basetype
MASK<T>(int width)
{
return MASK<typename T::basetype>(width);
}
#endif
#include <iostream>
int main ()
{
int svar = -3;
std::cout << "MASK
<< std::endl;
std::cout << "MASK
std::cout << "MASK
char>(4) << std::endl;
std::cout << "MASK
std::endl;
std::cout << "MASK
std::endl;
#ifdef BAD
std::cout << "MASK
std::endl;
#endif
return 0;
}
=============================================
/tools/linux/gcc-3.4.2/bin/g++ -o mask mask.cxx -DBAD
mask.cxx:18: error: `T' has not been declared
mask.cxx:19: error: expected init-declarator before "MASK"
mask.cxx:19: error: expected `;' before "MASK"
mask.cxx: In function `int main()':
..........
mask.cxx: In function `T MASK(int) [with T = composite]':
mask.cxx:36: instantiated from here
mask.cxx:9: error: no matching function for call to
`composite::composite(int)'
mask.cxx:2: note: candidates are: composite::composite()
mask.cxx:2: note: composite::composite(const composite&)
mask.cxx:11: error: no matching function for call to
`composite::composite(int)'
mask.cxx:2: note: candidates are: composite::composite()
mask.cxx:2: note: composite::composite(const composite&)
mask.cxx:13: error: no matching function for call to
`composite::composite(int)'
mask.cxx:2: note: candidates are: composite::composite()
mask.cxx:2: note: composite::composite(const composite&)
[apl]aluminum$
--
--
Alan Lehotsky
[ See http://www.gotw.ca/resources/clcm.htm for info about ]
[ comp.lang.c++.moderated. First time posters: Do this! ]
|
|
| Back to top |
|
 |
Rob Williscroft Guest
|
Posted: Sat Sep 18, 2004 10:59 am Post subject: Re: template function specialization troubles |
|
|
Alan Lehotsky wrote in news:apl-039B6A.21201416092004
@news6.east.earthlink.net in comp.lang.c++.moderated:
| Quote: | struct composite {
int i;
typedef int basetype;
};
template <class T> inline T MASK (int width) {
if (width < 0 || width > (sizeof(T)* )
return T (0);
else if (width == (sizeof(T)* )
return ~T (0);
else
return T(~((~T(0)) << width));
}
// Specialization for composite data types
#ifdef BAD
template <> inline T::basetype
MASK<T>(int width)
{
return MASK<typename T::basetype>(width);
}
#endif
|
template < typename T >
inline typename T::basetype
MASK(int width, typename T::basetype * = 0 )
{
return MASK<typename T::basetype>(width);
}
The extra argument (typename T::basetype * = 0) *isn't* required
for a conforming compiler, but it helps with gcc 3.2, sorry
I don't have 2.95 so I can't check.
The *typename* is required by a conforming compiler, but again
I havent gcc 2.95 so you may need to #ifdef it.
HTH.
Rob.
--
http://www.victim-prime.dsl.pipex.com/
[ See http://www.gotw.ca/resources/clcm.htm for info about ]
[ comp.lang.c++.moderated. First time posters: Do this! ]
|
|
| Back to top |
|
 |
Alberto Barbati Guest
|
Posted: Sat Sep 18, 2004 11:12 am Post subject: Re: template function specialization troubles |
|
|
Alan Lehotsky wrote:
| Quote: |
template <class T> inline T MASK (int width) {
if (width < 0 || width > (sizeof(T)* )
return T (0);
else if (width == (sizeof(T)* )
return ~T (0);
else
return T(~((~T(0)) << width));
}
// Specialization for composite data types
#ifdef BAD
template <> inline T::basetype
MASK<T>(int width)
{
return MASK<typename T::basetype>(width);
}
#endif
|
This can't compile for at least three reasons:
1) the specialization syntax is incorrect, there's no <T> after MASK
2) if you fully specialize the template you can no longer refer to T
3) you missed the typename keyword before the first T::basetype
Syntax apart, yours cannot be a template specialization because you want
to replace T with T::basetype in the signature. A specialization can
only replace T with a "real" type (such as int, float, MyClass, etc.)
that doesn't depend on T.
| Quote: | #include <iostream
int main ()
{
int svar = -3;
std::cout << "MASK
std::endl;
|
What's "typeof"? Please stick to standard C++...
BTW, using std::endl is overkill, as it will flush the buffer. Usually
writing a "n" is more appropriate.
What I suggest is the following. Change the default implemention to:
template <class T> inline T MASK(int width, ...)
{ /* same as above */ }
then declare the replacement this way:
template <class T>
inline typename T::basetype MASK(int width, typename T::basetype* = 0)
{
return MASK<typename T::basetype>(width);
}
in this way you get SFINAE to do what you need. Notice that the second
implementation is an overload and not a specialization. The extra
parameter is used to resolve ambiguities. Here's how it works:
* For types without T::basetype, SFINAE will discard the second overload
and the compiler will select the first one
* For types with T::basetype the compiler has the choice between the two
overloads, but "T::basetype*=0" is preferred over "..."
Hope it helps,
Alberto
[ See http://www.gotw.ca/resources/clcm.htm for info about ]
[ comp.lang.c++.moderated. First time posters: Do this! ]
|
|
| Back to top |
|
 |
Alberto Barbati Guest
|
Posted: Sun Sep 19, 2004 10:52 am Post subject: Re: template function specialization troubles |
|
|
Rob Williscroft wrote:
| Quote: |
template < typename T
inline typename T::basetype
MASK(int width, typename T::basetype * = 0 )
{
return MASK
}
The extra argument (typename T::basetype * = 0) *isn't* required
for a conforming compiler, but it helps with gcc 3.2, sorry
I don't have 2.95 so I can't check.
|
What makes you say that? The extra parameter *is* required even on
conforming compilers. Without the extra parameter, for a T that has
T::basetype, you will get two overloads with exactly the same signature
apart from the return value:
T MASK(int)
T::basetype MASK(int)
Both overloads are equally good and the compiler will complain about the
ambigous call. (Notice that those are overloads and resolved as such,
there is no such thing as partial ordering of specializations for
function templates).
Even with the extra parameter, your suggestion is still incomplete,
because the compiler would have to choose between these two signatures:
T MASK(int)
T::basetype MASK(int, T::basetype* = 0)
again, no one is better than the other. Changing the declaration of the
first template by adding "..." as I said in my other post, solves the issue.
Alberto
[ See http://www.gotw.ca/resources/clcm.htm for info about ]
[ comp.lang.c++.moderated. First time posters: Do this! ]
|
|
| Back to top |
|
 |
Rob Williscroft Guest
|
Posted: Mon Sep 20, 2004 5:52 am Post subject: Re: template function specialization troubles |
|
|
Alberto Barbati wrote in news:YWV2d.9584$zF3.251629 (AT) twister2 (DOT) libero.it
in comp.lang.c++.moderated:
| Quote: | Rob Williscroft wrote:
template < typename T
inline typename T::basetype
MASK(int width, typename T::basetype * = 0 )
{
return MASK
}
The extra argument (typename T::basetype * = 0) *isn't* required
for a conforming compiler, but it helps with gcc 3.2, sorry
I don't have 2.95 so I can't check.
What makes you say that?
|
A mistake on my part.
| Quote: | The extra parameter *is* required even on
conforming compilers. Without the extra parameter, for a T that has
T::basetype, you will get two overloads with exactly the same
signature apart from the return value:
T MASK(int)
T::basetype MASK(int)
Both overloads are equally good and the compiler will complain about
the
ambigous call. (Notice that those are overloads and resolved as
such,
there is no such thing as partial ordering of specializations for
function templates).
|
Agreed.
| Quote: | Even with the extra parameter, your suggestion is still incomplete,
|
Yes, and unfortunatly your solution has the same problem.
| Quote: | because the compiler would have to choose between these two
signatures:
T MASK(int)
T::basetype MASK(int, T::basetype* = 0)
again, no one is better than the other. Changing the declaration of
the first template by adding "..." as I said in my other post, solves
the issue.
|
No, ... plays no part in overload resolution, as it isn't the target
of a conversion.
#include <iostream>
#include <ostream>
void f( int, ... )
{
std::cout << "f( int, ... )n";
}
void f( int, int = 0 )
{
std::cout << "f( int, 0 )n";
}
int main()
{
f( 1 );
}
Unfortunatly I have no solution for the OP as I can't get
a "has_type_member< T >" trait to work with gcc 3.2 or 3.3.
If 3.4 and better is an option:
#include <iostream>
#include <ostream>
struct composite
{
int i;
typedef int basetype;
};
#define DEFINE_HAS_TYPE_IMP( Name, Member )
template < typename T >
struct Name
{
private:
template < typename > struct empty_t_;
template < typename U >
static char helper_f( U *, empty_t_< typename U::Member > * = 0 );
static char (&helper_f( ... ))[2];
public:
enum { value =
sizeof( helper_f( (T *)0 ) ) == sizeof( char )
};
};
/**/
/*
----------------------------------------------------------------------- */
DEFINE_HAS_TYPE_IMP( has_basetype, basetype )
template < bool > struct disable_type {};
template <>
struct disable_type< false >
{
typedef void type;
};
template <class T>
inline T MASK(
int width,
typename disable_type< has_basetype< T >::value >::type * = 0
)
{
std::cout << "T width = " << width << "n";
return width * width;
}
template < typename T >
inline typename T::basetype
MASK( int width, typename T::basetype * = 0 )
{
std::cout << "T::basetype n";
return MASK< typename T::basetype >( width );
}
int main ()
{
std::cout << "MASK
MASK< int >(5);
std::cout << "MASK
MASK<char>(3);
std::cout << "MASK
MASK<unsigned char>(4);
std::cout << "MASK
MASK<composite>(7);
}
Rob.
--
http://www.victim-prime.dsl.pipex.com/
[ See http://www.gotw.ca/resources/clcm.htm for info about ]
[ comp.lang.c++.moderated. First time posters: Do this! ]
|
|
| Back to top |
|
 |
Carl Barron Guest
|
Posted: Mon Sep 20, 2004 6:04 am Post subject: Re: template function specialization troubles |
|
|
Alan Lehotsky <apl (AT) alum (DOT) mit.edu> wrote:
| Quote: | I'm trying to write a templated function with the following behavior.
Given a type T and an integer width, write a function that creates a
constant of either type T (for POD T) or of type T::basetype for any
aggregate type having a T::basetype that's a POD. The constant is
composed of a bitstring of length 'width'.
The following works as long as I don't try to handle aggregate types.
But SFINAE doesn't stop gcc (2.95.3 or 3.4.2) from complaining about
the specialization
template<> inline T::basetype MASK<T>(int width) ....
producing about 80 lines of errors all told. I've elided some that I'm
pretty sure are irrelevant. How can I make the return type of the
function do the right thing here?
|
I am guessing you need a built in integral type for one function and a
struct/class with a member type basetype for the other, if neither of
thess conditions occurs, then its an error.
boost provides is_integral to determine if we have an integral type T.
it also provides enable_if<bool,T> which we can use to generate a
compile time error if does not contain a member type basetype.
We have two functions with different return types so once we determine
which case we have integral, has basetype member, or error condition
then we are done.
template <class T>
struct Mask1
{
static T mask(int);
};
template <class T>
struct Mask2
{
static typename T::basetype mask(int);
};
template <bool B,class T>
struct ret_type {typedef T type;};
template <class T> struct ret_type<false,T>
{
typedef typename T::basetype type;
};
template <class T>
inline
typename ret_type<boost::is_integral::type
Mask(int width)
{
typedef typename boost::if_c
<
boost::is_integral
Mask1,
Mask2
return mask_type::mask(width);
}
[ See http://www.gotw.ca/resources/clcm.htm for info about ]
[ comp.lang.c++.moderated. First time posters: Do this! ]
|
|
| Back to top |
|
 |
Alberto Barbati Guest
|
Posted: Mon Sep 20, 2004 6:29 pm Post subject: Re: template function specialization troubles |
|
|
Rob Williscroft wrote:
| Quote: | Alberto Barbati wrote in news:YWV2d.9584$zF3.251629 (AT) twister2 (DOT) libero.it
Even with the extra parameter, your suggestion is still incomplete,
Yes, and unfortunatly your solution has the same problem.
because the compiler would have to choose between these two
signatures:
T MASK(int)
T::basetype MASK(int, T::basetype* = 0)
again, no one is better than the other. Changing the declaration of
the first template by adding "..." as I said in my other post, solves
the issue.
No, ... plays no part in overload resolution, as it isn't the target
of a conversion.
|
I guess you're right. I was deceived by VC7.1 that compiles my solution
without complaints and producing the desired behaviour. So strange,
maybe a compiler bug? Btw, Comeau (online) agrees with you.
Alberto
[ See http://www.gotw.ca/resources/clcm.htm for info about ]
[ comp.lang.c++.moderated. First time posters: Do this! ]
|
|
| Back to top |
|
 |
Mike Alexeev Guest
|
Posted: Mon Sep 20, 2004 7:13 pm Post subject: Re: template function specialization troubles |
|
|
Alan Lehotsky <apl (AT) alum (DOT) mit.edu> wrote
| Quote: | I'm trying to write a templated function with the following behavior.
Given a type T and an integer width, write a function that creates a
constant of either type T (for POD T) or of type T::basetype for any
aggregate type having a T::basetype that's a POD. The constant is
composed of a bitstring of length 'width'.
The following works as long as I don't try to handle aggregate types.
But SFINAE doesn't stop gcc (2.95.3 or 3.4.2) from complaining about
the specialization
template<> inline T::basetype MASK<T>(int width) ....
producing about 80 lines of errors all told. I've elided some that I'm
pretty sure are irrelevant. How can I make the return type of the
function do the right thing here?
=================================================
struct composite {
int i;
typedef int basetype;
};
template <class T> inline T MASK (int width) {
if (width < 0 || width > (sizeof(T)* )
return T (0);
else if (width == (sizeof(T)* )
return ~T (0);
else
return T(~((~T(0)) << width));
}
// Specialization for composite data types
#ifdef BAD
template <> inline T::basetype
MASK<T>(int width)
{
return MASK<typename T::basetype>(width);
}
#endif
#include <iostream
int main ()
{
int svar = -3;
std::cout << "MASK
std::endl;
std::cout << "MASK
std::cout << "MASK
char>(4) << std::endl;
std::cout << "MASK
std::endl;
std::cout << "MASK
std::endl;
#ifdef BAD
std::cout << "MASK
std::endl;
#endif
return 0;
}
=============================================
/tools/linux/gcc-3.4.2/bin/g++ -o mask mask.cxx -DBAD
mask.cxx:18: error: `T' has not been declared
mask.cxx:19: error: expected init-declarator before "MASK"
mask.cxx:19: error: expected `;' before "MASK"
mask.cxx: In function `int main()':
.........
mask.cxx: In function `T MASK(int) [with T = composite]':
mask.cxx:36: instantiated from here
mask.cxx:9: error: no matching function for call to
`composite::composite(int)'
mask.cxx:2: note: candidates are: composite::composite()
mask.cxx:2: note: composite::composite(const composite&)
mask.cxx:11: error: no matching function for call to
`composite::composite(int)'
mask.cxx:2: note: candidates are: composite::composite()
mask.cxx:2: note: composite::composite(const composite&)
mask.cxx:13: error: no matching function for call to
`composite::composite(int)'
mask.cxx:2: note: candidates are: composite::composite()
mask.cxx:2: note: composite::composite(const composite&)
[apl]aluminum$
|
Alan,
All you need is a helper struct to define the return type based on the
template type:
template <class T>
struct Result
{
typedef T type;
};
template <>
struct Result<composite>
{
typedef composite::basetype type;
};
template <class T> inline typename Result<T>::type MASK (int width) {
typedef typename Result<T>::type type;
if (width < 0 || width > (sizeof(type)* )
return type ();
else if (width == (sizeof(type)* )
return ~type ();
else
return type(~((~type()) << width));
}
Hope, this helps.
---
Thanks,
Mike Alexeev
[ See http://www.gotw.ca/resources/clcm.htm for info about ]
[ comp.lang.c++.moderated. First time posters: Do this! ]
|
|
| Back to top |
|
 |
Rob Williscroft Guest
|
Posted: Tue Sep 21, 2004 11:25 am Post subject: Re: template function specialization troubles |
|
|
Alberto Barbati wrote in news:YWV2d.9584$zF3.251629 (AT) twister2 (DOT) libero.it in
comp.lang.c++.moderated:
| Quote: | Both overloads are equally good and the compiler will complain about the
ambigous call. (Notice that those are overloads and resolved as such,
there is no such thing as partial ordering of specializations for
function templates).
|
Its irrelevent to the OP's problem (and my broken solution to it) but
I did find:
14.5.5.2 Partial ordering of function templates.
Its no use in the case under descusion as it uses a transformation
that ignores return types (except in the case of operator type()).
Rob.
--
http://www.victim-prime.dsl.pipex.com/
[ See http://www.gotw.ca/resources/clcm.htm for info about ]
[ comp.lang.c++.moderated. First time posters: Do this! ]
|
|
| Back to top |
|
 |
|
|
You cannot post new topics in this forum You cannot reply to topics in this forum You cannot edit your posts in this forum You cannot delete your posts in this forum You cannot vote in polls in this forum
|
|