STL removal algorithm question

D

Dilip

Howdy

I have code similar to this in my project:

Note: BSTR is an abomination conjured up some disturbed person in the
COM world. BSTR strings must be allocated/deallocated using the
SysAllocString/SysFreeString Windows APIs.

typedef struct tagMyStruct
{
BSTR somestring;
BSTR someotherstring;
} MyStruct;

vector<MyStruct> my_struct;

over the course of my app, I allocate the BSTRs inside MyStruct and
stuff them into the vector.

When the time comes to get rid of them I was wondering if there is a
way to free the memory pointed to by the BSTR's in every MyStruct
instance inside the vector using a _single_ STL algorithm call?
Currently I use a combination of for_each (with a predicate to delete
the BSTRs) followed by a call to my_struct.erase(mystruct.begin(),
mystruct.end()).

There must be a better way to do this, right?

I looked up remove and remove_if, but they don't seem to be right for
my situation...
 
V

Victor Bazarov

Dilip said:
I have code similar to this in my project:

Note: BSTR is an abomination conjured up some disturbed person in the
COM world. BSTR strings must be allocated/deallocated using the
SysAllocString/SysFreeString Windows APIs.
Noted.

typedef struct tagMyStruct
{
BSTR somestring;
BSTR someotherstring;
} MyStruct;

Please use C++ way of defining types, it's so much easier:

struct MyStruct
{
BSTR somestring;
BSTR someotherstring;
};
vector<MyStruct> my_struct;

over the course of my app, I allocate the BSTRs inside MyStruct and
stuff them into the vector.

Do you allocate those BSTR yourself? Why not give it to MyStruct to
allocate? You know, like, in a constructor, for example...
When the time comes to get rid of them I was wondering if there is a
way to free the memory pointed to by the BSTR's in every MyStruct
instance inside the vector using a _single_ STL algorithm call?

Define the destructor in MyStruct. Make it deallocate those things.
Of course, to follow the Rule of Three, you will need to define the
copy c-tor and the assignment op as well.

After that, a simple destruction of the vector will free up all the
things.
Currently I use a combination of for_each (with a predicate to delete
the BSTRs) followed by a call to my_struct.erase(mystruct.begin(),
mystruct.end()).
OK

There must be a better way to do this, right?

There must be. Dynamic memory management needs to be the responsibility
of the owner of that memory. So, let MyStruct handle its own memory as
it should.
I looked up remove and remove_if, but they don't seem to be right for
my situation...

They are not.

V
 
M

Mike Wahler

Dilip said:
Howdy

I have code similar to this in my project:

Note: BSTR is an abomination conjured up some disturbed person in the
COM world. BSTR strings must be allocated/deallocated using the
SysAllocString/SysFreeString Windows APIs.

typedef struct tagMyStruct
{
BSTR somestring;
BSTR someotherstring;
} MyStruct;

vector<MyStruct> my_struct;

over the course of my app, I allocate the BSTRs inside MyStruct and
stuff them into the vector.

When the time comes to get rid of them I was wondering if there is a
way to free the memory pointed to by the BSTR's in every MyStruct
instance inside the vector using a _single_ STL algorithm call?
Currently I use a combination of for_each (with a predicate to delete
the BSTRs) followed by a call to my_struct.erase(mystruct.begin(),
mystruct.end()).

There must be a better way to do this, right?

I looked up remove and remove_if, but they don't seem to be right for
my situation...

struct MyStruct
{
BSTR somestring;
BSTR someotherstring;
MyStruct(/* params */)
{
// allocate
}

~MyStruct()
{
// de-allocate
}
};

-Mike
 
D

Dilip

Victor said:
Dilip wrote:
Please use C++ way of defining types, it's so much easier:

struct MyStruct
{
BSTR somestring;
BSTR someotherstring;
};

I would except for that damocle's sword called "company policy"
Do you allocate those BSTR yourself? Why not give it to MyStruct to
allocate? You know, like, in a constructor, for example...

See, the problem is I simplified this struct a little too much. This
struct gets used across COM boundaries so I have to put it in a IDL
file and once I do that I don't think I can start adding ctors/dtors
and other stuff.
Define the destructor in MyStruct. Make it deallocate those things.
Of course, to follow the Rule of Three, you will need to define the
copy c-tor and the assignment op as well.

Apologies for the silly question but I presume vector.erase will call
dtor of the contained objects? If not I may not be able to use this
because the vector undergoes erasure/insertion during the lifetime of
my application and during such erasure I want to be able to free up the
BSTRs memory allocated inside every instance of MyStruct. IOW I have
engineered a situation where the capacity of the vector remains intact
-- its just its size() that contracts & expands.
They are not.

Thanks for confirming. I read Items 33/34 of Scott Meyers ESTL just
now -- I won't even go near it!
 
A

Alf P. Steinbach

* Victor Bazarov:
Please use C++ way of defining types, it's so much easier:

struct MyStruct
{
BSTR somestring;
BSTR someotherstring;
};


Do you allocate those BSTR yourself? Why not give it to MyStruct to
allocate? You know, like, in a constructor, for example...


Define the destructor in MyStruct. Make it deallocate those things.
Of course, to follow the Rule of Three, you will need to define the
copy c-tor and the assignment op as well.

That will be hugely inefficient when a MyStruct is copied within the
vector, as happens e.g. when the vector reallocates.

One slightly less inefficient way could be to use boost::shared_ptr to
encapsulate a BSTR (the BSTR type is a pointer).

But, personally I'd encapsulate that vector in a class, because it's
evidently an implementation of something with a very restricted set of
operations, and use an ordinary for loop in the class' destructor.
 
V

Victor Bazarov

Dilip said:
[..]
Apologies for the silly question but I presume vector.erase will call
dtor of the contained objects?

That is correct. How does it help you? Your MyStruct has no d-tor,
and the one that the compiler creates for you won't deallocate your
BSTR members using whatever functions you need.
If not I may not be able to use this
because the vector undergoes erasure/insertion during the lifetime of
my application and during such erasure I want to be able to free up
the BSTRs memory allocated inside every instance of MyStruct. IOW I
have engineered a situation where the capacity of the vector remains
intact -- its just its size() that contracts & expands.

The bottom line: if you have resource acquisition/dismissal during
exectuion of your program, you need to leave it to creation/destruction
of objects. RAII is one of the cornerstones of proper OOP.

As to COM implications, please ask in a Microsoft newsgroup. I am not
certain what you can or cannot do, and this is not the right place to
discsuss it.


V
 
D

Dilip

Victor said:
Dilip said:
[..]
Apologies for the silly question but I presume vector.erase will call
dtor of the contained objects?

That is correct. How does it help you? Your MyStruct has no d-tor,
and the one that the compiler creates for you won't deallocate your
BSTR members using whatever functions you need.

I know what a compiler generated dtor can and cannot do unless I put in
my own code to deallocate/allocate whatever I want to. That was not my
question -- I was simply trying to confirm calling vector.erase does
indeed call the dtor of the contained objects.
The bottom line: if you have resource acquisition/dismissal during
exectuion of your program, you need to leave it to creation/destruction
of objects. RAII is one of the cornerstones of proper OOP.

I know that -- the reason why I posted this question in the first place
was because I couldn't follow proper OOP idiom here and hence was
looking for a clever way to use some STL algorithm I have never ran
into so far to delete memory allocated by the members of MyStruct _and_
erase the individual instances. IOW, I was looking for a better way
than the for_each/vector.erase combination.
As to COM implications, please ask in a Microsoft newsgroup. I am not
certain what you can or cannot do, and this is not the right place to
discsuss it.

I brought up COM only to explain what a BSTR was. My question was and
still remains a C++ question.
 
D

Dilip

Alf said:
That will be hugely inefficient when a MyStruct is copied within the
vector, as happens e.g. when the vector reallocates.

One slightly less inefficient way could be to use boost::shared_ptr to
encapsulate a BSTR (the BSTR type is a pointer).

But, personally I'd encapsulate that vector in a class, because it's
evidently an implementation of something with a very restricted set of
operations, and use an ordinary for loop in the class' destructor.

My only concern in starting this thread was to find out whether a
for_each and a vector.erase might turn out to be inefficient as the
calls completely traverse the vector 2 times.

As a side note, a vector re-allocation cannot happen in my system
because I have locked the capacity at a pre-determined limit and knock
off the elements once that limit is hit.

Unfortunately I can't use Boost :-(
 
O

Old Wolf

Dilip said:
See, the problem is I simplified this struct a little too much. This
struct gets used across COM boundaries so I have to put it in a IDL
file and once I do that I don't think I can start adding ctors/dtors
and other stuff.

You will save a lot of headaches by not using this struct
anywhere, except in COM interface calls. If you need to
manipulate this data yourself, define your own struct, eg:
struct ProperStruct { std::wstring some, someother; }
and include a functions to convert between a ProperStruct
and a MyStruct when it comes time to make an interface call.

I recommend this strategy for deleting strings: maintain a
separate container of all your BSTRs. When you've finished
with your vector, just destroy the vector normally. Then go
through the separate container and SysFreeString all of them.
 
O

Old Wolf

Old said:
I recommend this strategy for deleting strings: maintain a
separate container of all your BSTRs.

Let me add that this is only if you decide to stick with the
idea of using a vector of structs of BSTR. My preferred
solution is to work with structs of CComBSTR, or
some other string type; and then generate a struct of BSTR
only when it's needed for a COM interface call; it makes
all of this management crap unnecessary.
When you've finished
with your vector, just destroy the vector normally. Then go
through the separate container and SysFreeString all of them.

Something like this:

struct BstrManager
{
~BstrManager() { clear(); }
void clear()
{
for_each(all.begin(), all.end(), SysFreeString);
all.clear();
}
BSTR createString(wchar_t const *s, size_t len)
{
BSTR b = SysAllocStringLen(s, len);
all.push_back(b);
return b;
}
BSTR copyString( BSTR s )
{
BSTR b = SysAllocString(s);
all.push_back(b);
return b;
}
void deleteString( BSTR b )
{
std::vector<BSTR>::iterator it = all.find(b);
if ( it != all.end() ) { SysFreeString(b); all.erase(it); }
}

private:
std::vector<BSTR> all;
};

Then in your code you can go:
MyStruct m;
m.somestring = manager.createString(L"Hello", 5);
m.otherstring = manager.copyString(m.somestring);
vec.push_back(m);
// ......
vec.clear();
manager.clear();
 
M

Michiel.Salters

Dilip said:
My only concern in starting this thread was to find out whether a
for_each and a vector.erase might turn out to be inefficient as the
calls completely traverse the vector 2 times.

Alternating between deleting a single BSTR and erasing a single element
from a vector is also inefficient. And if you erase an entire iterator
range, it's likely to be more efficient than looping yourself (cause
the
compiler can see it has to update size() only once, etc. )

HTH,
Michiel Salters
 
A

Alf P. Steinbach

* (e-mail address removed):
Alternating between deleting a single BSTR and erasing a single element
from a vector is also inefficient. And if you erase an entire iterator
range, it's likely to be more efficient than looping yourself (cause
the
compiler can see it has to update size() only once, etc. )

Uh, well. It's potentially an O(n) operation as opposed to an O(n^2)
operation; the size() update is mostly irrelevant, I should think.
However, for this concrete case the most efficient is to never erase.
 

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

Similar Threads

STL algorithm VS Java loop 17
STL question 22

Members online

No members online now.

Forum statistics

Threads
474,431
Messages
2,571,679
Members
48,796
Latest member
Greg L.

Latest Threads

Top