Partially specializing less<> with nested template class

This is what I would like to do: template <class P1, class P2> struct sp { template <typename T> struct nested { }; }; template <typename T> struct less; template <class P1, class P2, typename T> struct less<sp<P1, P2>::nested<T> > : binary_function<...> { bool operator()(...); }; However, it doesn't work. From my research, it seems that the problem is that T is in a non-deduced context, but I don't see why that would be an issue, since this is how I expect it to get instantiated: less<sp<p1, p2>::nested<int> > comp; Thus, I don't see any deduction as being necessary, but obviously I don't exactly understand how to get what I want. Dave

On 07/08/2004 12:58 PM, David B. Held wrote:
This is what I would like to do:
template <class P1, class P2> struct sp { template <typename T> struct nested { }; };
template <typename T> struct less;
template <class P1, class P2, typename T> struct less<sp<P1, P2>::nested<T> > : binary_function<...> { bool operator()(...); };
However, it doesn't work. From my research, it seems that the problem is that T is in a non-deduced context, but I don't see why that would be an issue, since this is how I expect it to get instantiated:
less<sp<p1, p2>::nested<int> > comp;
Thus, I don't see any deduction as being necessary, but obviously I don't exactly understand how to get what I want.
I very much sympathize with you; however, I have no answers :( I went through the same confusion with my managed_ptr's nested ownership template class. It was discussed here: http://groups.google.com/groups?hl=en&lr=&ie=UTF-8&selm=1044dnuf7ojhpc0%40corp.supernews.com In the above, David Abraham also expressed surprise at the need for deduction. A proposed standard change was also discussed here: http://groups.google.com/groups?hl=en&lr=&ie=UTF-8&selm=1072lb8ira3jg41%40corp.supernews.com However, there was no acknowledgement that it would work :(

Larry Evans wrote:
[...] I very much sympathize with you; however, I have no answers :( I went through the same confusion with my managed_ptr's nested ownership template class. It was discussed here:
http://groups.google.com/groups?hl=en&lr=&ie=UTF-8&selm=1044dnuf7ojhpc0%40corp.supernews.com [...]
It looks like Gabriel gives the best answer in that thread. My understanding is now that non-deduced contexts occur partly because specializations may be defined after instantiations of a given template class. And this is why simply adding another operator won't work. One way to eliminate non-deducibility would be to require that all specializations be defined before any template of that type is instantiated. However, this would make certain types of code impossible. Another solution would be say that argument deduction proceeds with only the specializations visible at the point of instantiation. However, that would probably be confusing. Another solution would be to have instantiation occur in a separate pass, after all possible specializations have been seen. Then it seems that it should be possible to deduce many contexts that are only non-deducible because of possible specializations. But people are likely to say that that is too expensive for the benefit gained. Dave

On 07/08/2004 02:24 PM, David B. Held wrote:
Larry Evans wrote: [snip]
It looks like Gabriel gives the best answer in that thread. My
Thanks Dave. I'll reread that.
understanding is now that non-deduced contexts occur partly because specializations may be defined after instantiations of a given template class. And this is why simply adding another operator won't work.
Do you mean like the :0: operator in http://groups.google.com/groups?hl=en&lr=&ie=UTF-8&selm=1072lb8ira3jg41%40corp.supernews.com ?

Larry Evans wrote:
[...] Do you mean like the :0: operator in
http://groups.google.com/groups?hl=en&lr=&ie=UTF-8&selm=1072lb8ira3jg41%40corp.supernews.com [...]
I didn't totally follow your reasoning, but I think I might have a more generic solution that I will post in c.l.c++.m. Dave

On 07/08/2004 03:53 PM, David B. Held wrote:
Larry Evans wrote:
[...] Do you mean like the :0: operator in
http://groups.google.com/groups?hl=en&lr=&ie=UTF-8&selm=1072lb8ira3jg41%40corp.supernews.com [...]
I didn't totally follow your reasoning, but I think I might have a more generic solution that I will post in c.l.c++.m.
Putting it another way, there's a 1 to 1 correspondence between templates nested to a depth N, and an N argument template. IOW, this is the same as saying there's a 1 to 1 correspondence between a function curried N times and an N argument function. Hence, if you can deduce the template arguments in an N template argument function, then you can deduce the template arguments in a function with 1 argument formed from templated nested to a depth N. IOW, just as the template arguments in: template < typename A1 , typename A2 ... , typename AN
void f( T<A1,A2,...,AN>& a) ; can be deduced; so can the template arguments in: template < typename A1 , typename A2 ... , typename AN
void f( T1<A1>:0:T2<A2>:0: ... :0:TN<AN>& a) ; because there's a 1 to 1 correspondence between: T<A1,A2,...,AN> and: T1<A1>:0:T2<A2>:0: ... :0:TN<AN> Since there is such a 1 to 1 correspondence, deducing 1 should be as possible as deducing the other. At least I think so :)

On Thu, Jul 08, 2004 at 01:44:28PM -0500, Larry Evans wrote:
On 07/08/2004 12:58 PM, David B. Held wrote:
This is what I would like to do:
template <class P1, class P2> struct sp { template <typename T> struct nested { }; };
template <typename T> struct less;
template <class P1, class P2, typename T> struct less<sp<P1, P2>::nested<T> > : binary_function<...> { bool operator()(...); };
However, it doesn't work. From my research, it seems that the problem is that T is in a non-deduced context, but I don't see why that would be an issue, since this is how I expect it to get instantiated:
less<sp<p1, p2>::nested<int> > comp;
Thus, I don't see any deduction as being necessary, but obviously I don't exactly understand how to get what I want.
I very much sympathize with you; however, I have no answers :(
Depending on how much control you have over various parts, you maybe can kludge it along the lines of: struct i_am_a_nested_thingy {}; template <class P1, class P2> struct sp { template <typename T> struct nested : public i_am_a_nested_thingy { typedef P1 NestedP1; typedef P2 NestedP2; }; }; template <typename T> struct less; template <typename T> struct less_helper; template <typename T> struct less_helper<T,true> : binary_function<...> { typedef typename T::NestedP1 P1; typedef typename T::NestedP2 P2; bool operator()(...); }; template <typename T> struct less_helper<T,false> { // default impl (if there is one) }; template <class P1, class P2, typename T> struct less<T> : public less_helper<T,is_base_and_derived<i_am_a_nested_thingy,T>::value> { }; ... less<sp<p1, p2>::nested<int> > comp; ... -- -Brian McNamara (lorgon@cc.gatech.edu)

"David B. Held" <dheld@codelogicconsulting.com> wrote in message news:cck1t5$g3r$1@sea.gmane.org...
This is what I would like to do:
template <class P1, class P2> struct sp { template <typename T> struct nested { }; };
template <typename T> struct less;
template <class P1, class P2, typename T> struct less<sp<P1, P2>::nested<T> > : binary_function<...> { bool operator()(...); };
Could you pull the definition of nested outside of sp template<typename P1, typename P2, typename T> struct sp_nested { }; then use a metafunction within sp: template<typename P1, typename P2> struct sp { template<typename T> struct nested { typedef sp_nested<P1, P2, T> type; }; }; ? You can then make less< sp_nested<P1, P2, T> > do pretty much whatever you want; the question is whether you can make the metafunction do whatever you needed the nested template to do. Jonathan

Jonathan Turkanis wrote:
[...] Could you pull the definition of nested outside of sp
template<typename P1, typename P2, typename T> struct sp_nested { };
then use a metafunction within sp:
template<typename P1, typename P2> struct sp { template<typename T> struct nested { typedef sp_nested<P1, P2, T> type; }; }; [...]
For syntax reasons, this is an undesirable solution. Although, you could get pretty close by modifying it a little: template<typename P1, typename P2> struct sp { template<typename T> struct nested : sp_nested<P1, P2, T> { }; };
[...] You can then make less< sp_nested<P1, P2, T> > do pretty much whatever you want; the question is whether you can make the metafunction do whatever you needed the nested template to do.
The problem is that sp_nested<> would be an implementation detail that I would not want to expose to the user. So I would not want the user to have to mention it in order to instantiate less<>. Dave

On 07/08/2004 04:34 PM, David B. Held wrote:
Jonathan Turkanis wrote:
[...] Could you pull the definition of nested outside of sp
template<typename P1, typename P2, typename T> struct sp_nested { };
then use a metafunction within sp:
template<typename P1, typename P2> struct sp { template<typename T> struct nested { typedef sp_nested<P1, P2, T> type; }; }; [...]
For syntax reasons, this is an undesirable solution. Although, you could get pretty close by modifying it a little:
template<typename P1, typename P2> struct sp { template<typename T> struct nested : sp_nested<P1, P2, T> { }; };
[...] You can then make less< sp_nested<P1, P2, T> > do pretty much
whatever you want; the question is whether you can make the metafunction do whatever you needed the nested template to do.
The problem is that sp_nested<> would be an implementation detail that I would not want to expose to the user. So I would not want the user to have to mention it in order to instantiate less<>.
FWIW, there's rm_nondeduced at: http://cvs.sourceforge.net/viewcvs.py/boost-sandbox/boost-sandbox/boost/mana... which is vaguely similar to some of previous ideas. It's used at: cycle_basis_mgr.hpp refcycle_prox_visitor_abs.hpp It's just another variation and hopefully you can maybe merge the ideas and distill the best from all of them.

Larry Evans wrote:
[...] FWIW, there's rm_nondeduced at:
http://cvs.sourceforge.net/viewcvs.py/boost-sandbox/boost-sandbox/boost/mana... [...]
So far I like Brian's solution best, even though it's a big hack. I don't really understand how your solution works. Could you give a simple contrived example in 20 lines or less? Dave

On 07/08/2004 04:58 PM, David B. Held wrote:
Larry Evans wrote:
[...] FWIW, there's rm_nondeduced at:
http://cvs.sourceforge.net/viewcvs.py/boost-sandbox/boost-sandbox/boost/mana... [...]
So far I like Brian's solution best, even though it's a big hack. I don't really understand how your solution works. Could you give a simple contrived example in 20 lines or less?
Not in 20 lines, and I found it wouldn't work after all. The way I used it, you'd have to be explicit with the 1st 2 arguments as shown by the attached file, held_nested_less.cpp. The output is in the attached held_nested_less.out. cd ~/prog_dev/ make -k install -d `dirname build/intel/debug/boost-root.ln/development_lje/libs/managed_ptr/test/held_nested_less.o` /opt/intel_cc_80/bin/icc -g -c -Iboost-root.ln/development_lje -Iboost-root.ln -Iboost-root.ln boost-root.ln/development_lje/libs/managed_ptr/test/held_nested_less.cpp -o build/intel/debug/boost-root.ln/development_lje/libs/managed_ptr/test/held_nested_less.o -MMD sed -e 's#^held_nested_less.o:#build/intel/debug/boost-root.ln/development_lje/libs/managed_ptr/test/held_nested_less.o:#' build/intel/debug/boost-root.ln/development_lje/libs/managed_ptr/test/held_nested_less.d> build/intel/debug/boost-root.ln/development_lje/libs/managed_ptr/test/held_nested_less.o.dep rm build/intel/debug/boost-root.ln/development_lje/libs/managed_ptr/test/held_nested_less.d install -d `dirname build/intel/debug/boost-root.ln/development_lje/libs/managed_ptr/test/held_nested_less.exe` /opt/intel_cc_80/bin/icc -g build/intel/debug/boost-root.ln/development_lje/libs/managed_ptr/test/held_nested_less.o build/intel/debug/boost-root.ln/development_lje/libs/io/filters/src/mout.o -o build/intel/debug/boost-root.ln/development_lje/libs/managed_ptr/test/held_nested_less.exe build/intel/debug/boost-root.ln/development_lje/libs/managed_ptr/test/held_nested_less.exe li=0 lfci=1 Compilation finished at Thu Jul 8 18:30:27

"David B. Held" <dheld@codelogicconsulting.com> wrote in message news:cckeh3$fd0$1@sea.gmane.org...
Jonathan Turkanis wrote:
[...] Could you pull the definition of nested outside of sp
template<typename P1, typename P2, typename T> struct sp_nested { };
then use a metafunction within sp:
template<typename P1, typename P2> struct sp { template<typename T> struct nested { typedef sp_nested<P1, P2, T> type; }; }; [...]
For syntax reasons, this is an undesirable solution.
I 'm not sure I see the syntax problem; if you mean that it's more cumbersome to use a metafunction, I agree.
Although, you could get pretty close by modifying it a little:
template<typename P1, typename P2> struct sp { template<typename T> struct nested : sp_nested<P1, P2, T> { }; };
Then specializing less< ... > for sp_nested wouldn't affect the implementation of less<nested>. <snip>
The problem is that sp_nested<> would be an implementation detail that I would not want to expose to the user. So I would not want the user to have to mention it in order to instantiate less<>.
I don't see the distinction between template <class P1, class P2, typename T> struct less<sp<P1, P2>::nested<T> > : binary_function<...> { bool operator()(...); }; and template <class P1, class P2, typename T> struct less<sp_nested<P1, P2, T> > : binary_function<...> { bool operator()(...); }; with respect to exposing implementation details. Jonathan

Jonathan Turkanis wrote:
"David B. Held" <dheld@codelogicconsulting.com> wrote in message news:cckeh3$fd0$1@sea.gmane.org...
[...] I 'm not sure I see the syntax problem; if you mean that it's more cumbersome to use a metafunction, I agree.
The user would have to write: sp<p1, p2>::nested<t>::type p; instead of: sp<p1, p2>::nested<t> p;
[...] Then specializing less< ... > for sp_nested wouldn't affect the implementation of less<nested>.
Ah, back to square one.
[...] I don't see the distinction between
template <class P1, class P2, typename T> struct less<sp<P1, P2>::nested<T> > : binary_function<...> { bool operator()(...); };
and
template <class P1, class P2, typename T> struct less<sp_nested<P1, P2, T> > : binary_function<...> { bool operator()(...); };
with respect to exposing implementation details.
The difference is that the component is called sp<>::nested<>. It's not called sp_nested<>, because they might want to typedef sp<> and instantiate the nested class over various types. By also exposing sp_nested<>, I'm asking them to call it by two different names depending on usage. It isn't totally evil, but it's less pleasant: typedef sp<p1, p2> my_sp; my_sp::nested<foo> p; my_sp::nested<bar> q; less<my_sp::nested<foo> > comp; vs.: less<sp_nested<p1, p2, foo> > comp; Somewhat defeats the point of the nice typedef. Dave

"David B. Held" <dheld@codelogicconsulting.com> wrote in message news:cckhc5$m3l$1@sea.gmane.org...
The difference is that the component is called sp<>::nested<>. It's not called sp_nested<>, because they might want to typedef sp<> and instantiate the nested class over various types. By also exposing sp_nested<>, I'm asking them to call it by two different names depending on usage. It isn't totally evil, but it's less pleasant:
typedef sp<p1, p2> my_sp; my_sp::nested<foo> p; my_sp::nested<bar> q;
less<my_sp::nested<foo> > comp;
Got it. One last try -- a variation on Brian's: struct nested_base { }; template <class P1, class P2> struct sp { template <typename T> struct nested : nested_base { typedef P1 p1; typedef P1 p2; typedef T t; }; }; template <typename T, typename Enabler = void> struct less { }; template<typename T> struct enable_if_nested : boost::enable_if< boost::is_base_and_derived<nested_base, T> > { }; template <typename T> struct less< T, typename enable_if_nested<T>::type > { ... }; Jonathan

On 07/08/2004 05:40 PM, Jonathan Turkanis wrote: [snip]
Got it. One last try -- a variation on Brian's:
struct nested_base { };
template <class P1, class P2> struct sp { template <typename T> struct nested : nested_base { typedef P1 p1; typedef P1 p2; typedef T t; }; };
template <typename T, typename Enabler = void> struct less { };
template<typename T> struct enable_if_nested : boost::enable_if< boost::is_base_and_derived<nested_base, T> > { };
template <typename T> struct less< T, typename enable_if_nested<T>::type > { ... };
There's another related problem which David might face, templated CTOR's. As I understand (from previous emails from David), P1 and P2 are policies and the T is the referent in a smart pointer. For any S which is a subtype of T, then the nested class would probably have: template <typename T> struct nested { template < typename S //subtype of T' > nested(sp<P1,P2>::nested<S>& a_sp) ; }; Now both P1 and P2 are in a non-deduced context and, AFAICT, there no workaround using some helper class like the above nested_base or Brian's i_am_a_nested_thingy. Maybe I'm wrong. If so, could someone please outline a solution? TIA.

Larry Evans wrote:
[...] template <typename T> struct nested { template < typename S //subtype of T' > nested(sp<P1,P2>::nested<S>& a_sp) ; };
Now both P1 and P2 are in a non-deduced context and, AFAICT, there no workaround using some helper class like the above nested_base or Brian's i_am_a_nested_thingy. Maybe I'm wrong. If so, could someone please outline a solution?
We (meaning Andrei and I, since it was his idea) cheated by using an overly general template argument and hoping for the best. I.e.: template <class SP> nested(SP& sp, typename SP::nested_type = 0); Where nested_type is just something we expect to find in SP. Of course, this is not foolproof, and someone could break it. But as you say, there is no foolproof solution in the current language that I can see (at least not one that is lightweight enough to be practical). Dave
participants (4)
-
Brian McNamara
-
David B. Held
-
Jonathan Turkanis
-
Larry Evans