Topic: Can't use polymorphic predicates in STL find_if?
Author: Stephen Cleary <scleary@jerviswebb.com>
Date: 2000/07/04 Raw View
In article <8ilba2$783$1@news-int.gatech.edu>,
vaps4bm@prism.gatech.edu (Brian McNamara!) wrote:
> ... You can make it work by being explicit about which
> version of find_if you call:
>
> struct Abs {
> virtual bool operator()( int x ) const=0;
> };
>
> struct Conc : public Abs {
> virtual bool operator()( int x ) const {
> return x==3;
> }
> };
>
> int main() {
> int a[] = { 1, 2, 3, 4 };
> Abs& pred = *(new Conc);
> //int* p = find_if( a, a+4, pred );
> int* p = find_if<int*,Abs&>( a, a+4, pred );
> return 0;
> }
This will (probably) work as expected for find_if, but for a good
generic solution, go envelope/letter as suggested earlier. The reason
I suggest this is that a lot of STL algorithm implementations will call
other algorithms -- without specifying explicit parameters.
For a bit more information, see "Predicates vs. Function Objects", by
Nicolai Josuttis (June 2000 C++ Report), available online at
www.creport.com
Nicolai's problem is not exactly like yours (he was trying to use a
Predicate that changed state), but the same things that caused his
problems will cause problems for you.
-Steve
Sent via Deja.com http://www.deja.com/
Before you buy.
---
[ comp.std.c++ is moderated. To submit articles, try just posting with ]
[ your news-reader. If that fails, use mailto:std-c++@ncar.ucar.edu ]
[ --- Please see the FAQ before posting. --- ]
[ FAQ: http://reality.sgi.com/austern_mti/std-c++/faq.html ]
Author: hinnant@anti-spam_metrowerks.com (Howard Hinnant)
Date: 2000/07/04 Raw View
In article <3953D1A9.44554B17@goingware.com>, "Michael D. Crawford"
<crawford@goingware.com> wrote:
| Howard Hinnant wrote:
| > For the specific case that you mention, it is a bug in your code.
| > However, you are brushing up very close to an open issue before the C++
| > standards committee.
|
| I'm OK with this being a bug in my code, but I think this is also a bug
| in Metrowerks. If the template is instantiated with the base class
| being passed by value, then the code should not have been able to
| compile, because it contains a pure virtual member function.
Sorry it has taken me so long to respond to this one. We (Metrowerks)
agree with you that there should have been a diagnostic. This will be
fixed in our next release. Thank you for bring this to our attention.
-Howard
---
[ comp.std.c++ is moderated. To submit articles, try just posting with ]
[ your news-reader. If that fails, use mailto:std-c++@ncar.ucar.edu ]
[ --- Please see the FAQ before posting. --- ]
[ FAQ: http://reality.sgi.com/austern_mti/std-c++/faq.html ]
Author: James Kuyper <kuyper@wizard.net>
Date: 2000/06/21 Raw View
Howard Hinnant wrote:
>
> In article <394CF8E0.AD4DD6BF@goingware.com>, "Michael D. Crawford"
> <crawford@goingware.com> wrote:
....
> | Here is the original code for find_if in Metrowerks:
>
> Please don't publish our code.
I understand your objection; however, could you suggest an alternative
way of discussing this that avoids violating your copyright? You have to
admit that this a fairly minor section of your code, and the standard
doesn't really give you much freedom to write it in a significantly
different way.
---
[ comp.std.c++ is moderated. To submit articles, try just posting with ]
[ your news-reader. If that fails, use mailto:std-c++@ncar.ucar.edu ]
[ --- Please see the FAQ before posting. --- ]
[ FAQ: http://reality.sgi.com/austern_mti/std-c++/faq.html ]
Author: hinnant@anti-spam_metrowerks.com (Howard Hinnant)
Date: 2000/06/21 Raw View
In article <394EBEB4.88BBF0DF@wizard.net>, James Kuyper
<kuyper@wizard.net> wrote:
| Howard Hinnant wrote:
| >
| > Please don't publish our code.
|
| I understand your objection; however, could you suggest an alternative
| way of discussing this that avoids violating your copyright? You have to
| admit that this a fairly minor section of your code, and the standard
| doesn't really give you much freedom to write it in a significantly
| different way.
Very minor and I'm sure no harm was done in this instance. I think just
publishing the standard interface (copied from the standard) of find_if
would have been sufficient in this discussion. After all, even the
standard does not specify the implementation of find_if.
Future versions of CodeWarrior will have increasingly sophisticated
implementations of even very simple algorithms to handle special cases
and/or extensions to the standard. I do not wish to set a precedent of
exposing such code except to our customers. Even simple looking code may
have been carefully crafted a specific way to meet some criteria that may
not be obvious by casual inspection.
If there are questions or concerns about a CodeWarrior specific
implementation, these are best brought up with Metrowerks support, or on a
Metrowerks specific news group.
-Howard
---
[ comp.std.c++ is moderated. To submit articles, try just posting with ]
[ your news-reader. If that fails, use mailto:std-c++@ncar.ucar.edu ]
[ --- Please see the FAQ before posting. --- ]
[ FAQ: http://reality.sgi.com/austern_mti/std-c++/faq.html ]
Author: kanze@gabi-soft.de
Date: 2000/06/22 Raw View
"Michael D. Crawford" <crawford@goingware.com> writes:
|> Is this a limitation of the STL standard or a bug in my compiler
|> (Metrowerks Codewarrior Pro 5.3 for Windows)?
|> What I want to do is have a member function in one of my classes that
|> takes a predicate that can be supplied to the STL find_if to search the
|> contents of a container it's got and return one of the elements in it.
It's a limitation (some would say a design flaw) of the STL. In
general, the STL uses value semantics for everything, which excludes
polymorphism.
The simple work-around is to use some sort of a handle class. (This is
often a good idea anyway for memory management reasons.)
--
James Kanze mailto:kanze@gabi-soft.de
Conseils en informatique orient e objet/
Beratung in objektorientierter Datenverarbeitung
Ziegelh ttenweg 17a, 60598 Frankfurt, Germany Tel. +49(069)63198627
---
[ comp.std.c++ is moderated. To submit articles, try just posting with ]
[ your news-reader. If that fails, use mailto:std-c++@ncar.ucar.edu ]
[ --- Please see the FAQ before posting. --- ]
[ FAQ: http://reality.sgi.com/austern_mti/std-c++/faq.html ]
Author: "Michael D. Crawford" <crawford@goingware.com>
Date: 2000/06/23 Raw View
My apologies for quoting your code.
Howard Hinnant wrote:
> For the specific case that you mention, it is a bug in your code.
> However, you are brushing up very close to an open issue before the C++
> standards committee.
I'm OK with this being a bug in my code, but I think this is also a bug
in Metrowerks. If the template is instantiated with the base class
being passed by value, then the code should not have been able to
compile, because it contains a pure virtual member function.
template <class T>
class VirtualUnaryPredicate{
public:
virtual bool operator()( const T &x ) = 0
{ return false; // default implementation };
};
BREditor *Foo::FindEditor( const VirtualUnaryPredicate &pred )
{
// this should cause a problem
deque< BREditor* >::iterator result = find_if( theQueue.begin(),
theQueue.end(),
pred );
...
either the line with the find_if should not compile, or I should get a
complaint from the compiler when it tries to instanatiate an actual
VirtualUnaryPredicate. What actually seems to happen is that one really
gets instantiated but the vtbl element for operater() is null.
It turns out that in my particular case I needed to define more
complicated kinds of finds (using predicates that take two arguments and
looping around the container and stuff) so it's not such a big deal to
write my own search algorithms.
Michael D. Crawford
GoingWare Inc. - Expert Software Development and Consulting
http://www.goingware.com
crawford@goingware.com
Tilting at Windmills for a Better Tomorrow.
---
[ comp.std.c++ is moderated. To submit articles, try just posting with ]
[ your news-reader. If that fails, use mailto:std-c++@ncar.ucar.edu ]
[ --- Please see the FAQ before posting. --- ]
[ FAQ: http://reality.sgi.com/austern_mti/std-c++/faq.html ]
Author: =?ISO-8859-1?Q?J=F6rg?= Barfurth <joerg.barfurth@attglobal.net>
Date: 2000/06/25 Raw View
Am 23.06.00, 23:53:09, schrieb "Michael D. Crawford"=20
<crawford@goingware.com> zum Thema Re: Can't use polymorphic predicates i=
n=20
STL find_if?:
> I'm OK with this being a bug in my code, but I think this is also a bug
> in Metrowerks. If the template is instantiated with the base class
> being passed by value, then the code should not have been able to
> compile, because it contains a pure virtual member function.
> template <class T>
> class VirtualUnaryPredicate{
> public:
> virtual bool operator()( const T &x ) =3D 0
> { return false; // default implementation };
> };
This shouldn't compile already (at least the standard requires a=20
diagnostic). You can define pure virtual member functions, and you can=20
define member function in the class body, but you cannot do both at once.=
=20
IIRC it does not even match the grammar, so it is purely a syntax error.
IOW: Definitions for pure virtual functions must appear outside the class=
=20
body.
BTW: Otherwise the compiler is not required to reject code that creates a=
=20
most derived object of an abstract class type. It must issue a diagnostic=
=20
though. Did MW actually warn about your code ?
-- J=F6rg
---
[ comp.std.c++ is moderated. To submit articles, try just posting with ]
[ your news-reader. If that fails, use mailto:std-c++@ncar.ucar.edu ]
[ --- Please see the FAQ before posting. --- ]
[ FAQ: http://reality.sgi.com/austern_mti/std-c++/faq.html ]
Author: "Michael D. Crawford" <crawford@goingware.com>
Date: 2000/06/19 Raw View
Is this a limitation of the STL standard or a bug in my compiler
(Metrowerks Codewarrior Pro 5.3 for Windows)?
What I want to do is have a member function in one of my classes that
takes a predicate that can be supplied to the STL find_if to search the
contents of a container it's got and return one of the elements in it.
The container is a deque< BREditor* > and I want to return a BREditor*
or NULL if one's not find. To be more complicated I actually hold a
number of these containers and I want to search all of them without
exposing that complication to client classes.
I thought that the member function could take an abstract base class as
its parameter, in which operator()( const BREditor * const &x ) is
virtual. And then to make these things more general (I do other kinds
of this stuff) I made the abstract base class itself a template.
My problem is that when I call find_if with a reference to a concrete
derived class the derived class appears to get sliced off and I crash.
If I write my own version of find_if in which the Predicate parameter is
a Predicate& it works.
But I would have thought providing a reference to the template would
have instantiated the find_if template as taking a reference parameter
and not have the predicate passed by value. That's why I'm asking if
this is a bug.
template < class T >
class VirtualUnaryPredicate{
public:
VirtualUnaryPredicate(){};
virtual ~VirtualUnaryPredicate(){};
virtual bool operator()( const T &x ) const = 0;
};
class PictureEditor{
public:
// note the predicate is a reference so it should work polymorphically
BREditor *FindEditor( VirtualUnaryPredicate< const BREditor* >
&predicate );
// complicated structures which contain deques of BREditor* here as
data members
};
the concrete class I use in the case of finding the editor object the
user clicked the mouse on is:
template< class ImgCoord, class ScrCoord >
class VectorVertexFinder: public VirtualUnaryPredicate< const BREditor*
>{
public:
// constructors and stuff
virtual bool operator()( const BREditor * const&x ) const
{
return true; // we'll crash before we get here in find_if
}
// data members
};
This code crashes:
BREditor *PictureEditor::FindEditor( VirtualUnaryPredicate< const
BREditor* > &predicate )
{
deque< BREditor* > edList = FetchAnEditorQueue();
deque< BREditor*>::iterator found = find_if( edList.begin()
edList.end(),
predicate );
if ( found != edList.end() )
return *found;
return NULL;
}
Here is the original code for find_if in Metrowerks:
template <class InputIterator, class Predicate>
inline
InputIterator
find_if(InputIterator first, InputIterator last, Predicate pred)
{
while (first != last && !pred(*first))
++first;
return first;
}
I originally took it that the Predicate parameter will instantiate the
template using whatever
type is supplied. But if I supply a Foo& the template appears to get
instantiated with Foo.
If I make my own find:
template <class InputIterator, class Predicate>
inline
InputIterator
MyFindIf(InputIterator first, InputIterator last, const Predicate &pred)
{
while (first != last && !pred(*first))
++first;
return first;
}
it works fine and the operator() in the derived class is called and
actually the whole chain (which is real complicated) works correctly.
Are you even supposed to be able to do this kind of thing?
I don't find any mention of whether it is OK to use virtual functions as
the operator() in any of my C++ books (Effective C++, more of the same
and stroustrup's special edition). The only mention of polymorphism and
templates is a discussion of containers that can hold different types by
making containers of base class pointers. I would have thought that if
you can't do this someone would say so and that's why I suspect it may
be a bug in my compiler.
It seems to me that I could solve the problem at hand by having the
member function take an adapter as its parameter, where the adapter
holds the real function as its data member. When we'd have:
bool Adapter::operator()( const T &x ) const
{
return mAdaptee( x );
}
which is not too bad except that every client of the function will have
to instantiate the adapter with the real functional. I'd considered my
original solution easier for clients to use.
It seems my adapter will only work if the adaptee can itself be
polymorphic.
Regards,
Mike Crawford
GoingWare Inc. - Expert Software Development and Consulting
http://www.goingware.com
crawford@goingware.com
Tilting at Windmills for a Better Tomorrow.
---
[ comp.std.c++ is moderated. To submit articles, try just posting with ]
[ your news-reader. If that fails, use mailto:std-c++@ncar.ucar.edu ]
[ --- Please see the FAQ before posting. --- ]
[ FAQ: http://reality.sgi.com/austern_mti/std-c++/faq.html ]
Author: hinnant@anti-spam_metrowerks.com (Howard Hinnant)
Date: 2000/06/20 Raw View
In article <394CF8E0.AD4DD6BF@goingware.com>, "Michael D. Crawford"
<crawford@goingware.com> wrote:
| Is this a limitation of the STL standard or a bug in my compiler
| (Metrowerks Codewarrior Pro 5.3 for Windows)?
For the specific case that you mention, it is a bug in your code.
However, you are brushing up very close to an open issue before the C++
standards committee.
| This code crashes:
|
| BREditor *PictureEditor::FindEditor( VirtualUnaryPredicate< const
| BREditor* > &predicate )
| {
| deque< BREditor* > edList = FetchAnEditorQueue();
|
| deque< BREditor*>::iterator found = find_if( edList.begin()
| edList.end(),
| predicate );
The rules for template argument deduction will instantiate find_if with
the predicate type: VirtualUnaryPredicate< const BREditor* > in your
code. This results in the slicing you have experienced. However, you can
explicitly specify the template parameters you want to get around this
behavior:
deque< BREditor*>::iterator found = find_if
<deque<BREditor*>::iterator,
const VirtualUnaryPredicate<const BREditor*>&
>
( edList.begin(), edList.end(), predicate );
| Here is the original code for find_if in Metrowerks:
Please don't publish our code.
| Are you even supposed to be able to do this kind of thing?
Issue 92 partially addresses this question, but we don't have an answer yet:
http://anubis.dkuug.dk/jtc1/sc22/wg21/docs/lwg-active.html#92
Angelika Langer's comment in this issue is specifically referring to your
situation.
-Howard
---
[ comp.std.c++ is moderated. To submit articles, try just posting with ]
[ your news-reader. If that fails, use mailto:std-c++@ncar.ucar.edu ]
[ --- Please see the FAQ before posting. --- ]
[ FAQ: http://reality.sgi.com/austern_mti/std-c++/faq.html ]
Author: Robert Klemme <robert.klemme@myview.de>
Date: 2000/06/20 Raw View
"Michael D. Crawford" schrieb:
>=20
> But I would have thought providing a reference to the template would
> have instantiated the find_if template as taking a reference parameter
> and not have the predicate passed by value. That's why I'm asking if
> this is a bug.
the standard defines call by value:
(quoting from section 25.1.2.)
template<class InputIterator, class Predicate>
InputIterator find_if(InputIterator first, InputIterator last,
Predicate pred);
you can circumvent this by using envelope letter where the envelope
holds just a pointer to the abstract base predicate and forwards the
call.
> template <class InputIterator, class Predicate>
> inline
> InputIterator
> find_if(InputIterator first, InputIterator last, Predicate pred)
> {
> while (first !=3D last && !pred(*first))
> ++first;
> return first;
> }
that's ok.
> Are you even supposed to be able to do this kind of thing?
interesting question...
> It seems to me that I could solve the problem at hand by having the
> member function take an adapter as its parameter, where the adapter
> holds the real function as its data member. When we'd have:
>=20
> bool Adapter::operator()( const T &x ) const
> {
> return mAdaptee( x );
> }
>=20
> which is not too bad except that every client of the function will have
> to instantiate the adapter with the real functional. I'd considered my
> original solution easier for clients to use.
>=20
> It seems my adapter will only work if the adaptee can itself be
> polymorphic.
that's exactly what i tried to suggest some way up.
robert
--=20
Robert Klemme
Software Engineer
-------------------------------------------------------------
myview technologies GmbH & Co. KG
Riemekestra=DFe 160 ~ D-33106 Paderborn ~ Germany
E-Mail: mailto:robert.klemme@myview.de
Telefon: +49/5251/69090-321 ~ Fax: +49/5251/69090-399
-------------------------------------------------------------
---
[ comp.std.c++ is moderated. To submit articles, try just posting with ]
[ your news-reader. If that fails, use mailto:std-c++@ncar.ucar.edu ]
[ --- Please see the FAQ before posting. --- ]
[ FAQ: http://reality.sgi.com/austern_mti/std-c++/faq.html ]
Author: vaps4bm@prism.gatech.edu (Brian McNamara!)
Date: 2000/06/20 Raw View
"Michael D. Crawford" <crawford@goingware.com> once said:
>Is this a limitation of the STL standard or a bug in my compiler
>(Metrowerks Codewarrior Pro 5.3 for Windows)?
A current "limitation" in STL, I'm pretty sure, though see below for a
"fix".
>But I would have thought providing a reference to the template would
>have instantiated the find_if template as taking a reference parameter
>and not have the predicate passed by value. That's why I'm asking if
>this is a bug.
g++ does the same thing. Digging through the standard reveals:
----------------------------------------------------------------------
14.8.2.3
[ rules for deducing template args... P is param, A is actual argument ]
If P is not a reference type:
...
--if A is a cv-qualified type, the top level cv-qualifiers of A's type
are ignored for type deduction.
...
----------------------------------------------------------------------
>Are you even supposed to be able to do this kind of thing?
It seems reasonable. You can make it work by being explicit about which
version of find_if you call:
struct Abs {
virtual bool operator()( int x ) const=0;
};
struct Conc : public Abs {
virtual bool operator()( int x ) const {
return x==3;
}
};
int main() {
int a[] = { 1, 2, 3, 4 };
Abs& pred = *(new Conc);
//int* p = find_if( a, a+4, pred );
int* p = find_if<int*,Abs&>( a, a+4, pred );
return 0;
}
The commented-out version fails as you have noted, but the explicit one
works.
--
Brian McNamara
---
[ comp.std.c++ is moderated. To submit articles, try just posting with ]
[ your news-reader. If that fails, use mailto:std-c++@ncar.ucar.edu ]
[ --- Please see the FAQ before posting. --- ]
[ FAQ: http://reality.sgi.com/austern_mti/std-c++/faq.html ]