sorry if this gets posts multiple times, I'm using attbi :(
Is this legal code? It compiles in Comeau C++ in strict mode. --------------- prerequisites:
1. a template meta-function 'is_built_in' that returns whether or not a type is a built-in type (including void) 2. a template meta-function 'is_ptr' which returns whether or not a type is a pointer. 3. a template meta-function 'is_ref' which returns whether or not a type is a reference. 4. a template meta-function 'is_array' which returns whether or not a type is an array 5. a template meta-function 'is_ptr_to_mem' which returns whether or not a type is a pointer-to-member (data member or member function) 6. (here is the annoying one) a template meta-function 'is_function' which returns whether or not a type is a function type. (I have these generated by the preprocessor up to 50 arguments). function pointers and function references are caught by is_ptr and is_ref. --------------- Basically, these are all of the types (that I can think of, let me know if I've missed one.) that can't be in a pointer-to-member type: int X::* (except an enumeration) --------------- Here is my 'is_enum' implementation:
int main(int argc, char* argv[]) { test<int>(); test<my_sample_enum>(); test<my_sample_struct>(); // etc. return 0;
}
What do you think? Has this problem already been solved? I just read in Modern C++ Design that there was no known way to detect if a type was an enum or not. (in the TypeTraits part, I believe). Lastly, is this legal code? Or should the psuedo-call to 'check' be ambiguous? I'm not sure, but this implementation relies on the fact that 'void (some_enum::*)(void)' is an invalid type. Like I said before, this code (plus the definition of the prerequisites above) compiles with Comeau C++ is strict mode, and yields results as intended. (i.e. correct results)
Paul Mensonides
[ Send an empty e-mail to c++-h...@netlab.cs.rpi.edu for info ] [ about comp.lang.c++.moderated. First time posters: do this! ]
> template<class U> static small check(void (U::*)(void)); > template<class U> static large check(...); > public: > static const bool value = > sizeof(check<T>(0)) == sizeof(large)
Cool, an is_class<T> implementation! Impressive.
> What do you think? Has this problem already been solved?
Yes, the boost type_traits library already has is_enum.
> I just read in Modern > C++ Design that there was no known way to detect if a type was an enum or not. > (in the TypeTraits part, I believe).
There is. An enum is a user-defined type that is convertible to int without an user-defined conversion (and two user-defined conversions in a row are illegal, so you can detect that.) See
for an implementation (by John Maddock, who invented it AFAIK.)
> Lastly, is this legal code? Or should the > psuedo-call to 'check' be ambiguous? I'm not sure, but this implementation > relies on the fact that 'void (some_enum::*)(void)' is an invalid type.
It's legal; see 14.8.2/2.
[ Send an empty e-mail to c++-h...@netlab.cs.rpi.edu for info ] [ about comp.lang.c++.moderated. First time posters: do this! ]
> --------------- > Basically, these are all of the types (that I can think of, let me know if I've > missed one.) that can't be in a pointer-to-member type: int X::* (except an > enumeration) > ---------------
Oops, I forgot is_array_incomplete.
Paul Mensonides
[ Send an empty e-mail to c++-h...@netlab.cs.rpi.edu for info ] [ about comp.lang.c++.moderated. First time posters: do this! ]
> Cool, an is_class<T> implementation! Impressive.
As long as you have another is_enum, yes.
> > What do you think? Has this problem already been solved?
> Yes, the boost type_traits library already has is_enum.
I looked at it shortly, does it handle function types (not function pointer or reference types)?
> > I just read in Modern > > C++ Design that there was no known way to detect if a type was an enum or not. > > (in the TypeTraits part, I believe).
> There is. An enum is a user-defined type that is convertible to int > without an user-defined conversion (and two user-defined conversions > in a row are illegal, so you can detect that.) See
Ah, yes. I see what you mean. Force double user-defined conversion.
> for an implementation (by John Maddock, who invented it AFAIK.)
> > Lastly, is this legal code? Or should the > > psuedo-call to 'check' be ambiguous? I'm not sure, but this implementation > > relies on the fact that 'void (some_enum::*)(void)' is an invalid type.
> It's legal; see 14.8.2/2.
Good, I like mine better, it's not as messy.
Paul Mensonides
[ Send an empty e-mail to c++-h...@netlab.cs.rpi.edu for info ] [ about comp.lang.c++.moderated. First time posters: do this! ]
> "Peter Dimov" <pdi...@mmltd.net> wrote in message > news:7dc3b1ea.0203160932.2757e843@posting.google.com... > > "Leavings" <leavi...@attbi.com> wrote in message > <news:000001c1cc83$e154d5e0$7772e50c@c161550a>... > > [...] > > > template<class U> static small check(void (U::*)(void)); > > > template<class U> static large check(...); > > > public: > > > static const bool value = > > > sizeof(check<T>(0)) == sizeof(large) > > > > Cool, an is_class<T> implementation! Impressive.
I looked at 14.8.2/2 (indicated by Peter) and wondered if it is possible to use Paul technique in order to detect whether some type T contains some type member. This is known challenge by Andrei Alexandrescu (look for post named "Reiterated question").
The code might look like the following:
// // detects if some type T has a type member named key_type // template<typename T> struct has_key_type { private: typedef char (&yes)[1]; typedef char (&no) [2];
public: static const bool result = sizeof(check<T>(0)) == sizeof(yes);
};
struct A1 {}; struct A2 { typedef int key_type; };
typedef int test_A1[has_key_type<A1>::result]; // fail typedef int test_A2[has_key_type<A2>::result]; // ok ?
I tested this code with the online Comeau C/C++ 4.3 BETA#2 strict mode and it compiled as expected (?). When commenting out the test_A1 line the code compiled fine.
is the above code is legal in standard C++?
Thanks, Rani
[ Send an empty e-mail to c++-h...@netlab.cs.rpi.edu for info ] [ about comp.lang.c++.moderated. First time posters: do this! ]
"Rani Sharoni" <rani_shar...@hotmail.com> wrote in message
news:df893da6.0203170237.3760f3c4@posting.google.com... > I looked at 14.8.2/2 (indicated by Peter) and wondered if it is > possible to use Paul technique in order to detect whether some type T > contains some type member. > This is known challenge by Andrei Alexandrescu (look for post named > "Reiterated question"). > > The code might look like the following: > > // > // detects if some type T has a type member named key_type > // > template<typename T> > struct has_key_type > { > private: > typedef char (&yes)[1]; > typedef char (&no) [2]; > > template<class U> static yes check(typename U::key_type *); > template<class U> static no check(...); > > public: > static const bool result = > sizeof(check<T>(0)) == sizeof(yes); > }; > > > struct A1 {}; > struct A2 { typedef int key_type; }; > > typedef int test_A1[has_key_type<A1>::result]; // fail > typedef int test_A2[has_key_type<A2>::result]; // ok ? > > > I tested this code with the online Comeau C/C++ 4.3 BETA#2 strict mode > and it compiled as expected (?). When commenting out the test_A1 line > the code compiled fine. > > is the above code is legal in standard C++?
It compiles with Comeau 4.2.45.2c beta #5 also. I'm not sure if it is legal, but is seems to me that it should be. The entire reason that I came up with it, is that it would be ridiculous for something to be an error, if the overload mechanism itself causes a declaration to be an error.
Say that I have some function 'f' somewhere:
template<class T> void f(int x);
Say that in another totally separate place I have another 'f':
template<class T> void f(void (T::*)(void));
Now, if overload resolution doesn't remove non-sensical declarations from the overload set, I would not be able to call the first f with anything but a class type.
int main(int argc, char* argv) { f<int>(0); return 0;
}
I though of this while I was trying to figure out a way to cause the compiler *not* to error based on a non-sensical declaration, but instead choose something else. This led to my original 'is_enum' implementation, which I didn't know what already solved in the Boost library. So I re-wrote 'is_enum', and re-engineered the old is_enum to be an 'is_class', thanks to the suggestion of Peter Dimov earlier in this thread.
Actually, I already integrated into an parameter list type extraction utility that I have been using, and it works like a charm. Good idea.
Paul Mensonides
[ Send an empty e-mail to c++-h...@netlab.cs.rpi.edu for info ] [ about comp.lang.c++.moderated. First time posters: do this! ]
> "Peter Dimov" <pdi...@mmltd.net> wrote in message > news:7dc3b1ea.0203160932.2757e843@posting.google.com... > > "Leavings" <leavi...@attbi.com> wrote in message > <news:000001c1cc83$e154d5e0$7772e50c@c161550a>... > > [...] > > > template<class U> static small check(void (U::*)(void)); > > > template<class U> static large check(...); > > > public: > > > static const bool value = > > > sizeof(check<T>(0)) == sizeof(large) > > > > Cool, an is_class<T> implementation! Impressive. > > As long as you have another is_enum, yes.
No, no need for is_enum. The quoted code is a complete implementation of is_class. You are using elimination to implement is_enum given is_class, whereas boost.type_traits does the opposite.
> > Yes, the boost type_traits library already has is_enum. > > I looked at it shortly, does it handle function types (not function pointer or > reference types)?
I believe it does.
[ Send an empty e-mail to c++-h...@netlab.cs.rpi.edu for info ] [ about comp.lang.c++.moderated. First time posters: do this! ]
> I looked at 14.8.2/2 (indicated by Peter) and wondered if it is > possible to use Paul technique in order to detect whether some type T > contains some type member. > This is known challenge by Andrei Alexandrescu (look for post named > "Reiterated question"). > > The code might look like the following: [...] > template<class U> static yes check(typename U::key_type *); > template<class U> static no check(...); > > public: > static const bool result = > sizeof(check<T>(0)) == sizeof(yes); > > is the above code is legal in standard C++?
Amazingly, it think that it is. You are the proud inventor of the has_nested_type<> technique. :-)
[ Send an empty e-mail to c++-h...@netlab.cs.rpi.edu for info ] [ about comp.lang.c++.moderated. First time posters: do this! ]
> I looked at 14.8.2/2 (indicated by Peter) and wondered if it is > possible to use Paul technique in order to detect whether some type T > contains some type member. > This is known challenge by Andrei Alexandrescu (look for post named > "Reiterated question").
I wonder whether this might also useful in writting a default_constructible traits class:
int main() { static_assert< default_constructible< A >::result >(); static_assert< !default_constructible< B >::result >(); }
Comeau 4.3 BETA#2 refuses to compile it saying,
"6478.c", line 10: error: no default constructor exists for class "B" template <class U> static yes check( typename size_type_wrapper< sizeof( new U ) >::type * );
Is this correct? I would have thought that as new B is illegal, sizeof( new B ) would be, and so typename size_type_wrapper< sizeof( new T ) >::type ought to be illegal too.
-- Richard Smith
[ Send an empty e-mail to c++-h...@netlab.cs.rpi.edu for info ] [ about comp.lang.c++.moderated. First time posters: do this! ]
rani_shar...@hotmail.com (Rani Sharoni) wrote in message <news:df893da6.0203170237.3760f3c4@posting.google.com>... > "Paul Mensonides" <pmens...@attbi.com> wrote in message <news:8jQk8.49732$702.17430@sccrnsc02>... > I looked at 14.8.2/2 (indicated by Peter) and wondered if it is > possible to use Paul technique in order to detect whether some type T > contains some type member. > This is known challenge by Andrei Alexandrescu (look for post named > "Reiterated question").
> The code might look like the following:
> // > // detects if some type T has a type member named key_type > // > template<typename T> > struct has_key_type > { > private: > typedef char (&yes)[1]; > typedef char (&no) [2];
> typedef int test_A1[has_key_type<A1>::result]; // fail > typedef int test_A2[has_key_type<A2>::result]; // ok ?
> I tested this code with the online Comeau C/C++ 4.3 BETA#2 strict mode > and it compiled as expected (?). When commenting out the test_A1 line > the code compiled fine.
> is the above code is legal in standard C++?
The first paragraph of 14.8.3 (in particular, this sentence: "... If, for a given function template, argument deduction fails, no such function is added to the set of candidate functions for that template."), in conjunction with already mentioned here 14.8.2, para 2 clearly indicate that the above is legal - which is unbelievably cool and exciting!
Of course, the real-world compilers spoil the excitement a bit. GCC 2.95.3-5 (don't have 3.1) and Borland C++ 5.5.1 (incorrectly) reject the code with the complaint that 'A1' has no 'key_type' memeber, both MSVC 6.5 and VC 7.0 refuse to compile 'check<T>(0)' expression (saying "'T' : illegal use of this type as an expression"), and Metrowerks CodeWarrior 7.2 compiles the code, but resolves the overloads incorrectly, resulting in has_key_type<A1>::result == true. Of course, that's what bug reports are for (I am going to report to Metrowerks, and it would be cool if somebody reported to GCC team and Borland).
Yet, after a few minutes of tweaking I've managed to produce a version that compiles cleanly (and gives the right answer) under Comeau C/C++ 4.2.45.2 for MS_WINDOWS_x86, gcc 2.95.3-5, MSVC 6.5 and VC 7.0:
> > is the above code is legal in standard C++? > > It compiles with Comeau 4.2.45.2c beta #5 also. I'm not sure if it is legal,
It is.
> but is seems to me that it should be. The entire reason that I came up with it, > is that it would be ridiculous for something to be an error, if the overload > mechanism itself causes a declaration to be an error. > > Say that I have some function 'f' somewhere: > > template<class T> void f(int x); > > Say that in another totally separate place I have another 'f': > > template<class T> void f(void (T::*)(void)); > > Now, if overload resolution doesn't remove non-sensical declarations from the > overload set,
It does, see section 14.8.3 para 1: "... If, for a given function template, argument deduction fails, no such function is added to the set of candidate functions for that template."
> I would not be able to call the first f with anything but a class > type. > > int main(int argc, char* argv) { > f<int>(0); > return 0; > } >
Yep.
> I though of this while I was trying to figure out a way to cause the compiler > *not* to error based on a non-sensical declaration, but instead choose something > else.
You and Rani Sharoni totally rock :).
-- Aleksey
[ Send an empty e-mail to c++-h...@netlab.cs.rpi.edu for info ] [ about comp.lang.c++.moderated. First time posters: do this! ]
"Markus Werle" <numerical.simulat...@web.de> wrote in message
news:a74mpl$mba$1@nets3.rz.RWTH-Aachen.DE... > Paul Mensonides wrote: > > > > > Good, I like mine better, it's not as messy. > > > > Show it to us, if possible, please. > We like to learn. > > Markus
My original version relied on the fact that enums cannot be in the 'object' in a pointer-to-member signature, so it simply eliminated all of the other things that won't match either, like pointers-to-members, pointers, references, arrays, incomplate arrays, function types, and primitive types (including void).
Which effectively, eliminates everything but classes, which Peter Dimov observed, and converted to an 'is_class' implementation. Then Rani Sharoni made another modification of the basic idiom that resulted in a 'has_nested_type_named_xxx' implementation (unfortunately, you can't parametize the name of the possible nested class).
Ah, the joys of C++ and template meta-programming.
Which reminds me, how do you change the color of a Button on Win32? ( just kidding :) )
Paul Mensonides
[ Send an empty e-mail to c++-h...@netlab.cs.rpi.edu for info ] [ about comp.lang.c++.moderated. First time posters: do this! ]
> > "Peter Dimov" <pdi...@mmltd.net> wrote in message > > news:7dc3b1ea.0203160932.2757e843@posting.google.com... > > > "Leavings" <leavi...@attbi.com> wrote in message > > <news:000001c1cc83$e154d5e0$7772e50c@c161550a>... > > > [...] > > > > template<class U> static small check(void (U::*)(void)); > > > > template<class U> static large check(...); > > > > public: > > > > static const bool value = > > > > sizeof(check<T>(0)) == sizeof(large) > > > > > > Cool, an is_class<T> implementation! Impressive. > > > > As long as you have another is_enum, yes. > > No, no need for is_enum. The quoted code is a complete implementation > of is_class. You are using elimination to implement is_enum given > is_class, whereas boost.type_traits does the opposite.
Yeah, I see what you mean, but I was thinking about 'is_enum'. I didn't look to deeply into the boost version, and this is what I came up with.
Which doesn't use my original mechanism at all, just the double-user-defined-conversion idea. Unfortunately, you still have to eliminate types that can't be used as return types (or parameter types), like function types. That is the really annoying thing, because C++ has no mechanism to produce linearized lists--i.e. functions that have arbitrary numbers of parameters. I'm using the preprocessor to generate 'is_function' partial specializations, but given the unholy case of a function type with more than, say, 50 parameters, this version above will fail. In other words, it is not bulletproof.
> > > Yes, the boost type_traits library already has is_enum. > > > > I looked at it shortly, does it handle function types (not function pointer or > > reference types)? > > I believe it does.
Okay, I'll look closer. Right now, I'm looking at what I can do with template template parameters, and deducing the numbers and types of parameters that they have. Which could be passed arbitrarily to another template by a wrapper class.
e.g....
template<int I> struct sizer { char value[I];
};
// no impl. template<template<class> class T> sizer<1> binding(T<int>*); template<template<class, class> class T> sizer<2> binding(T<int, int>*); template<template<class, class, class> class T> sizer<3> binding(T<int, int, int>*); // etc.
I can generate this 'linearization' type stuff with Cpp. The annoying part is nested explicit specialization, and lack of template typedefs. In order to properly partial bind a template template parameter, I need template typedefs. I can map all possible constructor calls, operator= calls, etc, an inherit everything else, but type A != type B, and also importantly, the super class loses its 'explicit' constructors or they all become explicit. If the template that you are sending a partially bound template to knows that it might get a partially bound template, then it can bind through a typedef. It can even check if a nested type of a certain name exists with the 'has_nested_type_named_xxx' template and use it if it does:
> Show it to us, if possible, please. > We like to learn.
Ooops! I overlooked You used 2 different from: headers. Unfortunately mods were too fast this time, so the cancel request (5 min later) was not successful.
Please forget my message.
Markus
[ Send an empty e-mail to c++-h...@netlab.cs.rpi.edu for info ] [ about comp.lang.c++.moderated. First time posters: do this! ]
> I wonder whether this might also useful in writting a default_constructible > traits class: > > template <typename T> > struct default_constructible > { > private: > typedef char (&yes)[1]; > typedef char (&no)[2]; > > template <unsigned> struct size_type_wrapper { typedef T type; }; > > template <class U> static yes check( typename size_type_wrapper< > sizeof( new T ) >::type * ); > template <class U> static no check( ... ); > > public: > static const bool result = > sizeof( check<T>(0) ) == sizeof(yes); > }; > > template <bool Val> struct static_assert; > template <> struct static_assert<true> {}; > > > struct A { }; > struct B { B(int) {} }; > > int main() > { > static_assert< default_constructible< A >::result >(); > static_assert< !default_constructible< B >::result >(); > } > > Comeau 4.3 BETA#2 refuses to compile it saying, > > "6478.c", line 10: error: no default constructor exists for class "B" > template <class U> static yes check( typename size_type_wrapper< > sizeof( new U ) >::type * ); > > Is this correct? I would have thought that as new B is illegal, sizeof( new > B ) would be, and so typename size_type_wrapper< sizeof( new T ) >::type > ought to be illegal too.
I don't think that this is correct. Here are the exact conditions (taken from 14.8.2/2) for such techniques:
Type deduction may fail for the following reasons: Attempting to create an array with an element type that is void, a function type, or a reference type, or attempting to create an array with a size that is zero or negative. Attempting to use a type that is not a class type in a qualified name. Attempting to use a type in the qualifier portion of a qualified name that names a type when that type does not contain the specified member, or if the specified member is not a type where a type is required. Attempting to create a pointer to reference type. Attempting to create a reference to a reference type or a reference to void. Attempting to create "pointer to member of T" when T is not a class type. Attempting to perform an invalid conversion in either a template argument expression, or an expression used in the function declaration. Attempting to create a function type in which a parameter has a type of void. Attempting to create a cv-qualified function type.
I can't find any condition that suits your needs.
But the last condition is quite interesting; I think that it can be exploited in order to produce is_function<T>. The code might be something like:
typedef int test[ is_function<void(int,char)>::result]; typedef int test[!is_function<int>::result]; typedef int test[!is_function<int&>::result];
The is_reference is needed because attempting to create a pointer to reference type also fails the type deduction.
I tested this code on Comeau C/C++ 4.3 BETA#2 strict mode and it compile fine. Using this is_function it is easy to implement the is_function_ptr (detects pointer to function) and is_function_ref (detects reference to function).
Paul, I think that this might save you some work and it's not limited for 50 arguments functions.
Rani
[ Send an empty e-mail to c++-h...@netlab.cs.rpi.edu for info ] [ about comp.lang.c++.moderated. First time posters: do this! ]
> I don't think that this is correct. Here are the exact conditions > (taken from 14.8.2/2) for such techniques:
I'm not so sure. It also says, immediately preceding:
"If substitution in a template parameter or in the function type of the function template results in an invalid type, type deduction fails."
What exactly is an "invalid type"? I think that is pretty open-ended. I take that to mean "anything that that makes no sense for a certain type T but is syntactically legal and semantically legal for some other type T." For instance,
Is this *not* an invalid type if T has no default ctor? It looks invalid to me. The expression 'new T' is non-sensical therefore the type should be 'invalid'.
It then goes on to list cases where type deduction *may* fail. I don't know if that means where type deduction is *allowed* to fail, excluding all others.
I think the template meta-function 'hackery' constitutes the minority case, and a function like the one above, which is *legal* sometimes, could theoretically totally invalidate the overload set.
Later, at 14.8.3 it says,
"If, for a given function template, argument deduction fails, no such function is added to the set of candidate functions for the template."
Earlier, at 13.3.2 (Viable functions)
"From the set of candidate functions constructed for a given context (13.3.1), a set of viable functions is chosen, from which the best function will be selected by comparing argument conversion sequences for the best fit (13.3.3). The selection of viable functions considers relationships between arguments and function parameters other than the ranking of conversion sequences."
This is what I'm getting out of the above.... Given two overloaded templates:
struct X { X(int, int); // no default ctor
};
template<unsigned I> struct Z { };
template<class T> void f(Z<sizeof(new T)>*) { }
// and
template<class T> void f(int) { };
Both of these functions are 'viable' candidates, regardless of conversion sequences (even an exact match). So, unless T has a default ctor, supposedly the entire resolution is bad, because 'supposedly' the first 'f' has not been removed from the overload set for being non-sensical. Given the definitions above, Comeau C++ compiles this line:
f<X>(0);
My question is why? What removed the first 'f' from the candidate functions? Obviously, the int argument is an exact match and is the best function. However, 'supposedly' nothing removed the first 'f' from the set of candidate functions, which 'supposedly' the replacement of 'T' is supposed to happen before overload resolution...
14.8.3
"For each function template, if the argument deduction and checking succeeds, the template-arguments (deduced and/or explicit) are used to instantiate a single function template specialization which is added to the candidate functions set to be used in overload resolution."
According to the Table 9 (pg. 223) 'exact match' is a conversion sequence. It seems that Comeau C++ (in strict mode) is bypassing the normal overload mechanism because it knows that 'f(int)' will win.
Which is right? It seems to me that 'Z<sizeof(new T)>*' could be an 'invalid type' if instantiated with a certain T, which would remove it from the candidate set, which would allow 'f(int)' to be selected by the overload resolution mechanism. Otherwise, Comeau C++ should have errored when attempting to instantiate the first 'f' to be added to the candidate set then discarded by the 'better match' of int.
What do you think?
Paul Mensonides
[ Send an empty e-mail to c++-h...@netlab.cs.rpi.edu for info ] [ about comp.lang.c++.moderated. First time posters: do this! ]
> But the last condition is quite interesting; I think that it can be > exploited in order to produce is_function<T>. The code might be > something like:
> The is_reference is needed because attempting to create a pointer to > reference type also fails the type deduction.
> I tested this code on Comeau C/C++ 4.3 BETA#2 strict mode and it > compile fine. > Using this is_function it is easy to implement the is_function_ptr > (detects pointer to function) and is_function_ref (detects reference > to function).
> Paul, I think that this might save you some work and it's not limited > for 50 arguments functions.
Now this I like. Who knew what a poorly implemented 'is_enum' idea could turn into an unbounded function-type checker. :)
Unfortunately, though this solves one problem, it doesn't solve a general 'function_traits' type of class. :( That requires partial specializations to extract parameter types, etc.
Nevertheless, this is the work of genius, and it compiles and works on Comeau C++ 4.2.45.2c Beta #5 also.
Paul Mensonides
P.S. Rani, see the thread "Template template parameters and function template overload resolution" at comp.std.c++. There is a related type of problem there with deducing nested template types, etc.
[ Send an empty e-mail to c++-h...@netlab.cs.rpi.edu for info ] [ about comp.lang.c++.moderated. First time posters: do this! ]
"leavings" <leavi...@attbi.com> wrote in message <news:000001c1cfd7$e14b20c0$7772e50c@c161550a>... > "If substitution in a template parameter or in the function type of the function > template results in an invalid type, type deduction fails."
> What exactly is an "invalid type"? I think that is pretty open-ended. I take > that to mean "anything that that makes no sense for a certain type T but is > syntactically legal and semantically legal for some other type T." > [...] > It then goes on to list cases where type deduction *may* fail. I don't know if > that means where type deduction is *allowed* to fail, excluding all others.
I don't know what are the exact reasons for specifying such list in 14.8.2/2, but notice that is_class, has_key_type and is_pointer all used some explicit mentioned case form that list and indeed compiled with Comeau. I think that the standard is not very clear whether the listed cases (14.8.2/2) are the only cases in which the deduction fails in such fashion.
If you are right then this "close to" is_pod class could be implemented:
template<typename T> struct is_legal_union_member { private: template<typename U> union UnionHelper { T pod_member; };
public: static const bool result = sizeof(check<T>(0)) == sizeof(yes);
};
struct A2 { A2(); }; typedef int test[!is_legal_union_member<A2>::result];
I'm not sure that it's that easy. Indeed as I expected Comeau C/C++ 4.3 BETA#2 rejected that code. Notice that even when I replaced the … with an int the compiler still rejected the code which is not very consistent with your example (compiler bug?).
Rani
[ Send an empty e-mail to c++-h...@netlab.cs.rpi.edu for info ] [ about comp.lang.c++.moderated. First time posters: do this! ]
> Okay, I'll look closer. Right now, I'm looking at what I can do with template > template parameters, and deducing the numbers and types of parameters that they > have. Which could be passed arbitrarily to another template by a wrapper class. > > e.g.... > > template<int I> struct sizer { > char value[I]; > };
sizeof(sizer<1>) is not guaranteed to be different from sizeof(sizer<2>); I use char (&) [N] which is guaranteed to have size N.
> // no impl. > template<template<class> class T> > sizer<1> binding(T<int>*); > template<template<class, class> class T> > sizer<2> binding(T<int, int>*); > template<template<class, class, class> class T> > sizer<3> binding(T<int, int, int>*);
There is no need for the dummy pointer parameter; leave the argument list empty to avoid the (rare) possibility where int is not a valid template argument for T.
> Also, I'm working on partial binding. > > template<int> struct bind; > > template<> struct bind<2> { > template<template<class, class> class result, class T> struct args { > template<class T1> struct type : result<T1, T> { > typedef result<T1, T> partial_bind; > // hoard of ctors, template ctors, and assignments > }; > }; > };
I'm not sure that I get it. Is this a compile time bind2nd, with a 'metafunction' expressed as a template template parameter? If so, you might want to take a look at Alexey's (soon-to-be-in-Boost) MPL library.
If you are familiar with Boost.Bind, you'll probably be interested in my compile-time port (can do mpl::bind<F, T, _1>::type) as well.
[...]
> I can generate this 'linearization' type stuff with Cpp. The annoying part is > nested explicit specialization, and lack of template typedefs.
The approach that MPL adopts is to use classes with nested templates as metafunctions and _not_ template template parameters (as Loki does.)
That is,
template<class T, class U> struct is_same; // is_same<T, U>::type returns true_type or false_type
is expressed as
struct is_same { template<class T, class U> struct apply;
};
and invoked as is_same::template apply<T, U>::type. (Some use the term 'quoted metafunction' for this idiom.)
Now it's possible to write bind2nd without typedef templates:
"Rani Sharoni" <rani_shar...@hotmail.com> wrote in message
news:df893da6.0203201424.33047743@posting.google.com... > I don't know what are the exact reasons for specifying such list in > 14.8.2/2, but notice that is_class, has_key_type and is_pointer all > used some explicit mentioned case form that list and indeed compiled > with Comeau. I think that the standard is not very clear whether the > listed cases (14.8.2/2) are the only cases in which the deduction > fails in such fashion. > > If you are right then this "close to" is_pod class could be > implemented:
[snip]
> I'm not sure that it's that easy.
What's wrong with it being 'that easy'? :)
> Indeed as I expected Comeau C/C++ 4.3 BETA#2 rejected that code. > Notice that even when I replaced the with an int the compiler still > rejected the code which is not very consistent with your example > (compiler bug?).
The question remains, what is *supposed* to happen here:
You can't have it both ways, either template type deduction removes the first 'check' or it should be a compile-time error. I think that it would be a dangerous mechanism to have it be a compile-time error, because just a declaration like this could destroy an otherwise perfectly valid function call--that is not ambiguous in the 'typical' sense.
More explicitly, if the above compiles, then so should this:
Template type deduction is supposed to happen 'before' overload resolution, in order to determine which instantiation of which template functions should be added to the overload set. There is absolutely no way that the above can *not* fail the type deduction checking, nor, if you take only the explicit reasons for failure, there is no way that it *can* fail the explicit argument checking phase. In either case, this is prior to overload resolution. So basically, it should either invalidate the overload set entirely, or it should be gotten rid of by failing template type deduction.
Obviously, I'm byassed toward having any non-sensical type caused by this checking to eliminate the function before overload resolution. This would open up a world of possibilities, since you can put almost anything inside 'sizeof'.
On a more general note, I don't think that this 'type' of programming should not be considered as a valid use of the language. I.e. uses upon which decisions are made by the committee(s). Rather I think the possibilities encourage library writers to abstract their code even more a reduce coupling between the library and library user.
I agree that I didn't expect this type of stuff to compile, but the 'check(int)' surprises me. I think that this a clear cut case of the standard *not* being explicit, because this is kind of like 'pre-overload resolution'-time, and in this area it is *very* unclear except on the order in which things are 'supposed' to happen--at the very least that the result is the same 'as if' things happened in this specific order. As far as Comeau C++ is concerned, I've gotten all kinds of weird errors (sometimes they are just blantantly wrong) from overload ambiguity to compiler assertions that fail. For instance, the 'has_nested_XXX' meta-function fails if you pass it a type that has a 'template' named XXX instead of a regular class (or vice-versa):
I don't necessary trust Comeau (like I usually do) in this area--the template-type-deduction-twilight-zone. I think that it is exibiting 'weirdo' behavior. Obviously, this needs to be clarified, and I hope that it is done in the more 'open-ended' fashion. :)
By the way, I got the above stuff to work properly, but it took a heck of a lot of 'hackery'...
template<class> struct split { }; // no nested 'type'
template< template<class> class T, class T1 > struct split< T<T1> > { struct type { };
};
template< template<class, class> class T, class T1, class T2 > struct split< T<T1, T2> > { struct type { };
This seems to work, why (as opposed to the other one) I don't know. I haven't tried to fix the 'has_nested_XXX' template to handle the odd-ball nested template yet, but I suppose it could use partial specialization with a 'bool' argument that uses the has_template_XXX as a default parameter:
I don't know whether or not that will work; this seems to be a "cross your fingers" area with Comeau C++. (Not that I'm knocking them, nothing else will even get close.)
Paul Mensonides
[ Send an empty e-mail to c++-h...@netlab.cs.rpi.edu for info ] [ about comp.lang.c++.moderated. First time posters: do this! ]
I realize that there is no guarantee that there is a size difference, but why is there for the reference?
> > // no impl. > > template<template<class> class T> > > sizer<1> binding(T<int>*); > > template<template<class, class> class T> > > sizer<2> binding(T<int, int>*); > > template<template<class, class, class> class T> > > sizer<3> binding(T<int, int, int>*); > > There is no need for the dummy pointer parameter; leave the argument > list empty to avoid the (rare) possibility where int is not a valid > template argument for T.
I know. :) This was my mistake, my implementation doesn't have it, but I was copying out of my head. I have it encapsulated in a macro.
Incidently, where would an 'int' not be valid? It uses a pointer, which doesn't require a complete type (granted, I realize that it is unnecessary), but other than that, the only thing I can think of is:
template<template<int> class T> sizer<1> binding( T<int>* ); // error
> > Also, I'm working on partial binding. > > > > template<int> struct bind; > > > > template<> struct bind<2> { > > template<template<class, class> class result, class T> struct args { > > template<class T1> struct type : result<T1, T> { > > typedef result<T1, T> partial_bind; > > // hoard of ctors, template ctors, and assignments > > }; > > }; > > }; > > I'm not sure that I get it. Is this a compile time bind2nd, with a > 'metafunction' expressed as a template template parameter? If so, you > might want to take a look at Alexey's (soon-to-be-in-Boost) MPL > library.
It is a compile-time 'bind2nd' for template-template-parameters. I'm basically using a mechanism that 'binds' forces a template-template-parameter that takes two arguments into one that takes only one. Actually, it's more of a bind2nd, bind3rd, bind4th, etc..
The fun part, is reconstruction of default parameters given an example to work from. Which I realize that is not a good idea in some cases.
For instance, basic_string has these parameters (i.e. no-extension ones)...
class T, class traits = std::char_traits<T>, class Allocator = std::allocator<T>
Given the instruction to reconstruct default parameters, the partial binding mechanism with pull apart the default arguments looking for (in this case) 'T'. So, for instance, a bind<3> will bind the third argument 'partially', so when it is actually instantiated with 'T' later (after it has been passed as a template-template-parameter) it will still bind as 'std::allocator<T>' rather than 'std::allocator<int>' or whatever the example was.
> If you are familiar with Boost.Bind, you'll probably be interested in > my compile-time port (can do mpl::bind<F, T, _1>::type) as well. > > [...] > > > I can generate this 'linearization' type stuff with Cpp. The annoying part is > > nested explicit specialization, and lack of template typedefs. > > The approach that MPL adopts is to use classes with nested templates > as metafunctions and _not_ template template parameters (as Loki > does.)
I'm just doing meta-programming 'with' template-template-parameters because I haven't yet tried it. :) We have even fewer constructs for manipulating them then we do for regular 'types'. In other words, the solution is not necessarily for the purpose of a specific use, but more to serve its own end (and my curiousity).
> That is, > > template<class T, class U> struct is_same; // is_same<T, U>::type > returns true_type or false_type > > is expressed as > > struct is_same > { > template<class T, class U> struct apply; > }; > > and invoked as is_same::template apply<T, U>::type. (Some use the term > 'quoted metafunction' for this idiom.) > > Now it's possible to write bind2nd without typedef templates: > > template<class F, class A1> struct bind2nd > { > template<class A2> struct apply > { > typedef typename F::template apply<A1, A2>::type type; > }; > };
But this doesn't work if the destination is not expecting it. Take Loki, for instance, GenScatterHierarchy and GenLinearHierarchy require template-template-parameters. That is a given, unless I modify Loki myself just for a 'specific' case. Each of these template-template-parameters take a certain number of parameters, this also is fixed. In other words, there is absolutely no way to pass the name of a template that takes more or less than the required number of parameters--unless I partial bind the quote-unquote template-type. This is not an example of what I'm using it for, just a simple example to show the direction I'm going. If the implementation 'knows' that there is a nested template class with a typedef inside the 'type' passed to the template, then obviously it can use that. But it cannot if it doesn't (say, not written by me, for example).
Besides, I'm just doing it for fun (for now). Also, how far I can go deconstructing a default argument and rebuilding it is interesting.
Paul Mensonides
[ Send an empty e-mail to c++-h...@netlab.cs.rpi.edu for info ] [ about comp.lang.c++.moderated. First time posters: do this! ]
Assuming for the moment that this case is legal (hopefully there might be a more definitive answer in the thread on comp.std.c++), what about the following example: should this compile?
template <template <class> class Expr, class T> struct does_expr_compile { private: typedef char (&yes)[1]; typedef char (&yes)[2];
template <class T> struct default_construction { enum { value = sizeof( new T ) }; };
int main() { does_expr_compile< default_construction, T >::value; }
If so, I think this gives us an arbitrary does-this-expression-compile template. And the template metafunctions such as default_construction might even be constructible using some sort of lambda type trickery. It would be cool to be able to simply write something like,
does_expr_compile< _new< _T >, T >::value
-- Richard Smith
[ Send an empty e-mail to c++-h...@netlab.cs.rpi.edu for info ] [ about comp.lang.c++.moderated. First time posters: do this! ]
> I though of this while I was trying to figure out a way to cause the compiler > *not* to error based on a non-sensical declaration, but instead choose something > else. This led to my original 'is_enum' implementation, which I didn't know > what already solved in the Boost library. So I re-wrote 'is_enum', and > re-engineered the old is_enum to be an 'is_class', thanks to the suggestion of > Peter Dimov earlier in this thread.
Just for sake of interest, there is a second technique that one can use to this sort of 'is this non-sensical?' compile time testing: it uses partial specialisation instead of overloading, and is less clean, but it is conceivable that it might have some other advantage. Anyway here it is an example of it,
which compiles fine in Comeau 4.3 BETA#2. I've posted a few other examples of using this technique here before, but I've never managed to get people very interested in them ;-(.