templated deletePointer in for_each algorithm

S

shaun

I am working on code where I am handed a vector of pointers vector<T*>
or a map<std::string, T*>, and I have to delete the objects and set the
pointers to zero. I have been using a 'for' loop and thought it might be
instructive to write a 'deletePointer' which can be used in an algorithm
or standalone.
(code at end of mail)

I discovered I could not simply

for_each(v.begin(),v.end(),deletePointer);

but had to put in a type adapter for the vector, and this works but is
cumbersome.
However the syntax to use for deleting the pointers in the 'values' of
the map completely escapes me. Can anyone help?

thanks

shaun

#include <iostream>
#include <string>
#include <vector>
#include <map>
#include <utility>
#include <algorithm>


template <class T>
void deletePointer(T* &myPointer){
delete myPointer;
myPointer = NULL;
}
//adapter, general template for non-pointers
template <class T>
struct TypeOf{
typedef void pointee;
};
//adapter for pointer
template <class T>
struct TypeOf<T*>{
typedef T pointee;
};

using namespace std;
void print (string * elem){
cout<<*elem<<" ";
}

void printPointer(string * p){
cout<<hex<<p<<" ";
}

int main (int argc, char * const argv[]) {
string * pMyString = new string;
*pMyString = "hello";
cout << "Heres the newed string :"<<*pMyString<<endl;
cout << "with pointer value :"<<hex<<pMyString<<endl;
//Deletion with resetting the pointer:
deletePointer(pMyString);
//
cout << "The string has been deleted, and now.."<<endl;
cout << "the pointer value is :"<<hex<<pMyString<<endl;
//set up map and vector
map<int, string*> myMap;
vector<string *> myVec;
typedef string * PString;
for (int i(0);i not_eq 10; ++i){
PString pString=new string;
PString pString2=new string;
*pString = "burt";
*pString2 = "smith";
myVec.push_back(pString);
myMap.insert(make_pair(i,pString2));
}
for_each(myVec.begin(),myVec.end(),print);
cout<<endl;
for_each(myVec.begin(),myVec.end(),printPointer);
cout<<endl;
for_each(myVec.begin(),myVec.end(),deletePointer<
TypeOf<PString>::pointee >);
for_each(myVec.begin(),myVec.end(),printPointer);
cout<<endl;

//Now: how to delete a map of pointers using for_each?

return 0;
}
 
M

mlimber

shaun said:
I am working on code where I am handed a vector of pointers vector<T*>
or a map<std::string, T*>, and I have to delete the objects and set the
pointers to zero. I have been using a 'for' loop and thought it might be
instructive to write a 'deletePointer' which can be used in an algorithm
or standalone.
(code at end of mail)

I discovered I could not simply

for_each(v.begin(),v.end(),deletePointer);

but had to put in a type adapter for the vector, and this works but is
cumbersome.
However the syntax to use for deleting the pointers in the 'values' of
the map completely escapes me. Can anyone help?

thanks

shaun

#include <iostream>
#include <string>
#include <vector>
#include <map>
#include <utility>
#include <algorithm>


template <class T>
void deletePointer(T* &myPointer){
delete myPointer;
myPointer = NULL;
}
//adapter, general template for non-pointers
template <class T>
struct TypeOf{
typedef void pointee;
};
//adapter for pointer
template <class T>
struct TypeOf<T*>{
typedef T pointee;
};

using namespace std;
void print (string * elem){
cout<<*elem<<" ";
}

void printPointer(string * p){
cout<<hex<<p<<" ";
}

int main (int argc, char * const argv[]) {
string * pMyString = new string;
*pMyString = "hello";
cout << "Heres the newed string :"<<*pMyString<<endl;
cout << "with pointer value :"<<hex<<pMyString<<endl;
//Deletion with resetting the pointer:
deletePointer(pMyString);
//
cout << "The string has been deleted, and now.."<<endl;
cout << "the pointer value is :"<<hex<<pMyString<<endl;
//set up map and vector
map<int, string*> myMap;
vector<string *> myVec;
typedef string * PString;
for (int i(0);i not_eq 10; ++i){
PString pString=new string;
PString pString2=new string;
*pString = "burt";
*pString2 = "smith";
myVec.push_back(pString);
myMap.insert(make_pair(i,pString2));
}
for_each(myVec.begin(),myVec.end(),print);
cout<<endl;
for_each(myVec.begin(),myVec.end(),printPointer);
cout<<endl;
for_each(myVec.begin(),myVec.end(),deletePointer<
TypeOf<PString>::pointee >);
for_each(myVec.begin(),myVec.end(),printPointer);
cout<<endl;

//Now: how to delete a map of pointers using for_each?

return 0;
}

Better would probably be to use a smart pointer such as
std::tr1::shared_ptr (aka boost::shared_ptr) instead of raw pointers in
your containers. Then deleting takes care of itself. If you don't have
control over that, you could just write a class to delete the first or
second element of an iterator to std::pair, e.g.,

template <class T, class U>
void deleteFirst( std::pair<T,U*>& aPair )
{
deletePointer( aPair.first );
}

template <class T, class U>
void deleteSecond( std::pair<T,U*>& aPair )
{
deletePointer( aPair.second );
}

Cheers! --M
 
M

mlimber

mlimber said:
template <class T, class U>
void deleteFirst( std::pair<T,U*>& aPair )

That should be:

void deleteFirst( std::pair<T*,U>& aPair )

Cheers! --M
 
S

shaun

I'm afraid these are raw pointers, I have no option to use a smart
pointer. After some messing around, I arrived to the following code
which gets rid of my adapter. In the code, I do at least know when I
have a vector or a map, so I can use the separate functions
"deleteMapPointer" and "deleteVecPointer". It would, of course, be nicer
to overload a single "deletePointer" function. But someone a lot smarter
than me will have to work that out...

#include <iostream>
#include <string>
#include <vector>
#include <map>
#include <utility>
#include <algorithm>


template <class T>
void deletePointer(T* &myPointer){
delete myPointer;
myPointer = NULL;
}

template <class T>
void deleteMapPointer(typename T::value_type &myPair){
delete myPair.second;
myPair.second = NULL;
}

template <class T>
void deleteVecPointer(typename T::value_type &myVal){
delete myVal;
myVal = NULL;
}

using namespace std;
void print (string * elem){
cout<<*elem<<" ";
}

void printPointer(string * p){
cout<<hex<<p<<" ";
}

int main (int argc, char * const argv[]) {
string * pMyString = new string;
*pMyString = "hello";
cout << "Heres the newed string :"<<*pMyString<<endl;
cout << "with pointer value :"<<hex<<pMyString<<endl;
//Deletion with resetting the pointer:
deletePointer(pMyString);
//
cout << "The string has been deleted, and now.."<<endl;
cout << "the pointer value is :"<<hex<<pMyString<<endl;
//set up map and vector
typedef map<int, string*> MapType;
MapType myMap;
typedef vector<string *> VecType;
VecType myVec;
typedef string * PString;
for (int i(0);i not_eq 10; ++i){
PString pString=new string;
PString pString2=new string;
*pString = "burt";
*pString2 = "smith";
myVec.push_back(pString);
myMap.insert(make_pair(i,pString2));
}
for_each(myVec.begin(),myVec.end(),print);
cout<<endl;
for_each(myVec.begin(),myVec.end(),printPointer);
cout<<endl;
for_each(myVec.begin(),myVec.end(),deleteVecPointer<VecType>);
for_each(myVec.begin(),myVec.end(),printPointer);
cout<<endl;
for_each(myMap.begin(),myMap.end(),deleteMapPointer<MapType>);
cout<<hex<<myMap[2]<<endl;
string myFalse("burt");
return 0;
}
 
K

Kaz Kylheku

shaun said:
I am working on code where I am handed a vector of pointers vector<T*>
or a map<std::string, T*>, and I have to delete the objects and set the
pointers to zero. I have been using a 'for' loop and thought it might be
instructive to write a 'deletePointer' which can be used in an algorithm
or standalone.
(code at end of mail)

I discovered I could not simply

for_each(v.begin(),v.end(),deletePointer);

That won't work, because deletePointer is a template function, and you
need that to be instantiated. C++ can deduce the template arguments in
some limited situations, but not here.

As an example of a limited situation, consider taking the address of a
template function:

void (*pFunc)(string &*) = &deletePointer;

Here, the template arguments are deduced from the pointer type. Of
course, calls to template functions also work; the template arguments
can be deduced from the argument expressions.

But in your case, you just have to specify the template arguments
explicitly. Template argument deduction will not consider a match
between v.begin(), v.end() and deletePointer. These are unrelated
expressions, that happen to be different arguments to the same function
call. If you allow template deduction betwen these, where does it stop,
right? I mean, what if you had this:

x(y(z(vec.begin(), 42), 5), w(deletePointer));

should C++ deduce the template instantiation here based on the type of
vec.begin()?

In theory, it's possible. Template argument deduction /could/ walk the
entire full expression tree, and search for a valid, unique combination
of expansions for each of the templates which occur in that expression.

But that capability doesn't exist in the current C++.
for_each(myVec.begin(),myVec.end(),printPointer);
cout<<endl;

//Now: how to delete a map of pointers using for_each?

So you just have to be satisfied with;

for_each (myVec.begin(), myVec.end(), deletePointer<string>);
 
K

Kaz Kylheku

shaun said:
for_each(myVec.begin(),myVec.end(),printPointer);
cout<<endl;

//Now: how to delete a map of pointers using for_each?

By defining a functor which binds the function to the pair->second.

First, write this class:

template <class PAIR>
class take_second {
typedef void (*func_type)(typename PAIR::second_type &);
func_type func;
public:
take_second(func_type f) : func(f) { }
void operator()(PAIR &p)
{
func(p.second);
}
};

Now, you can do this:

for_each(myMap.begin(), myMap.end(),
take_second<map<int, string
*>::value_type>(deletePointer));

The third argument of for_each here constructs a temporary object of
type take_second<T> where T is the pair<const int, string *> (the
value_type of the map). As a constructor argument, the deletePointer
function is passed. The functor remembers that function. The for_each
function calls the functor for every pair<> in the map, and the
functor, in turn, selects the second member of the pair and invokes the
remembered function on it.
 
K

Kaz Kylheku

Kaz said:
By defining a functor which binds the function to the pair->second.

First, write this class:

template <class PAIR>
class take_second {
typedef void (*func_type)(typename PAIR::second_type &);
func_type func;
public:
take_second(func_type f) : func(f) { }
void operator()(PAIR &p)
{
func(p.second);
}
};

Now, you can do this:

for_each(myMap.begin(), myMap.end(),
take_second<map<int, string
*>::value_type>(deletePointer));

And now, one more improvement. Hide this all with a new flavor of the
for_each function which knows that it's supposed to iterate over the
second field:

template <class ITER, class FUNC>
void for_each_second(ITER begin, ITER end, FUNC func)
{
for_each(begin, end, take_second<typename ITER::value_type>(func));
}

Now you can write:

for_each_second(myMap.begin(), myMap.end(),
deletePointer<string>);

It would be nice if take_second didn't have the restriction that it
wraps an ordinary C++ function; that is, if it could transparently wrap
either a functor or a regular C++ function.
 
S

shaun roe

"Kaz Kylheku said:
And now, one more improvement. Hide this all with a new flavor of the
for_each function which knows that it's supposed to iterate over the
second field:

template <class ITER, class FUNC>
void for_each_second(ITER begin, ITER end, FUNC func)
{
for_each(begin, end, take_second<typename ITER::value_type>(func));
}

Now you can write:

for_each_second(myMap.begin(), myMap.end(),
deletePointer<string>);

It would be nice if take_second didn't have the restriction that it
wraps an ordinary C++ function; that is, if it could transparently wrap
either a functor or a regular C++ function.

This looks very sexy; I can see that the "take_second" can be reused in
the other algorithms. I dont understand the

typedef void (*func_type)(typename PAIR::second_type &)

or

take_second(func_type f) : func(f){}

syntax. Do you have reference to point me to?

I also kept picking at this problem over the day...my own solution
arrived eventually, and is below. The function deletePointer farms out
the deletion to overloaded delP functions. One is overloaded such that
it only picks out the std::pair type and acts accordingly.
Many thanks,
shaun

template <class T>
void delP(T &v, typename T::second_type const *){
delete v.second;
v.second=0;
}

template <class T>
void delP(T & v,...){
delete v;
v=0;
}

template <class T>
void deletePointer(T &myPointer){
delP<T>(myPointer,0);
}
 
K

Kaz Kylheku

shaun said:
This looks very sexy; I can see that the "take_second" can be reused in
the other algorithms.

Yes, this is functional programming. The take_second class, or its
constructor rather, behaves like a functional combinator. It takes a
function as an argument (for instance deletePointer) and returns a
different function (or rather a function object, an instance of the
take_two class). That object is a transformed version of deletePointer,
one which understands that the argument is a pair, and the second of
that pair is to be deleted.
I dont understand the

typedef void (*func_type)(typename PAIR::second_type &)

This is an ordinary typedef, except for the curious typename part. You
have to remember that the typedef occurs in the body of a template
class, where PAIR is a template parameter.

The typename is a keyword that tells the compiler that the given
identifier names a type. The compiler needs that hint because PAIR is a
template parameter, a dummy meta-variable that doesn't refer to
anything concrete. We are telling the compiler that whatever class PAIR
refers will have a member called "second_type", and that this
"second_class" member is a type name. And having thus qualified it as a
type name, we can now legally use it to declare a parameter.

Later, when our template is instantiated, the compiler will see that
what we told it was the truth: in fact, the the parameter that we
substitute for PAIR, namely std::pair<X, Y> has a second_type member
which is a type, and so the take_second instance will contain a
properly formed typedef for func_type.

Thus func_type is a pointer to a function that returns nothing, and
takes a reference to some object whose type is PAIR::second_type, a
type obtained from the PAIR template. If the PAIR class is
std::pair<int, string *>, then this reduces to:

typedef void (*func_type)(string *&);

which is exactly the type signature of deletePointer.
or

take_second(func_type f) : func(f){}

This is a perfectly ordinary, everyday constructor for the take_second
template class, with an empty inline body, and the member initializer
f(func).
syntax. Do you have reference to point me to?

Just the ANSI/ISO C++ standard, that's all I have on C++.
 
S

shaun roe

Right; I tried using my approach with std::remove_if(start,end,
equal_to...) - to remove map values where the second part is equal to
some value - and ran into problems(possibly because the 'first' in a map
pair is const?). Presumably to use your for_each_second, I would need to
make a function which is just:
template <class T>
bool equal(T lhs, T rhs){ return (lhs==rhs)};

rather than the std::equal_to said:
This is an ordinary typedef, except for the curious typename part. You
have to remember that the typedef occurs in the body of a template
class, where PAIR is a template parameter.

The typename is a keyword that tells the compiler that the given
identifier names a type. The compiler needs that hint because PAIR is a
template parameter, a dummy meta-variable that doesn't refer to
anything concrete. We are telling the compiler that whatever class PAIR
refers will have a member called "second_type", and that this
"second_class" member is a type name. And having thus qualified it as a
type name, we can now legally use it to declare a parameter.

Later, when our template is instantiated, the compiler will see that
what we told it was the truth: in fact, the the parameter that we
substitute for PAIR, namely std::pair<X, Y> has a second_type member
which is a type, and so the take_second instance will contain a
properly formed typedef for func_type.

Thus func_type is a pointer to a function that returns nothing, and
takes a reference to some object whose type is PAIR::second_type, a
type obtained from the PAIR template. If the PAIR class is
std::pair<int, string *>, then this reduces to:

typedef void (*func_type)(string *&);

which is exactly the type signature of deletePointer.


Thanks, I'm not used to dealing with pointers to functions in this way.
This explanation helps
OK, I mis-read this at first, now I can see it.
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

No members online now.

Forum statistics

Threads
473,997
Messages
2,570,239
Members
46,827
Latest member
DMUK_Beginner

Latest Threads

Top