Auto cast an iterator into its base pointer type ?

The Partridge Family were neither partridges nor a family. Discuss.
binbinhfr
Posts: 78
Joined: May 9th, 2019, 10:57 pm

Re: Auto cast an iterator into its base pointer type ?

Post by binbinhfr » March 23rd, 2020, 7:53 pm

Not sure exactly what you mean there. What checking is there in C++ that you wouldn't have in a C program?
Hard to explain in english : when CPU was an issue, we often tried to avoid explicit checkings, trying to rely on the inner safety of the code, "the way it should work", which was of course hard to obtain in complex codes, where everything was not always working as it was supposed to...

I like the Prog::WakeUp(){ cv.notify_one(); } thing.
Very convenient and more readable, thx, I take it !

At the inverse, I think that the "ThreadSafeCall" trick kills the lisibility when it comes to call the Print function...
More than that, in my real project, I don't want to block the whole function with the mutex, because I will run many instances of this function in parallel.
So if they all blocked the shared data, it won't be parallel anymore ;-)

About notify_all(), I agree with you that it sounds like it should satisfy my needs, but I tried to use it many times without any success.
Originally I thought each thread was suppose to share a mutex so each thread knew when things were locked
Yes that's what I thought too. I tried with 1 single mutex, trying to block every threads at once, and releasing them with a notify_all, but without success. I do not see why.

Maybe Chili will have an idea if he comes around ? Or someone else on this forum ?

I found something great : the VS debugger is very good at MT : you can swap from one thread to another quite freely. Very convenient.

Anyway, thx for your help, man !

albinopapa
Posts: 4373
Joined: February 28th, 2013, 3:23 am
Location: Oklahoma, United States

Re: Auto cast an iterator into its base pointer type ?

Post by albinopapa » March 24th, 2020, 2:59 am

Glad you like the WakeUp() idea, this is what I mean by finding ways to handle things in class instead of gaining access through getters or using setters to set some state.

The ThreadSafeCall() would take some forethought in code design or redesigning certain functions, so I can definitely see drawbacks to implementing something like this. It also requires that only one thread be allowed to call that function, like your Print function, the only reason I even threw it out there was to avoid having to write the lock/unlock or lock_guard lines of code throughout different functions. This has the added bonus of being able to keep the called/passed-in functions single threaded for use in single threaded situations, maybe to reuse in a different project where multi-threading isn't needed. Also, if functions from other APIs aren't thread safe, then you could use this instead of littering your code with lock_guards or calls to lock/unlock. Just thought I'd throw the idea out there, it would need to be modified a bit to be able to use it for member functions and the like, but anything callable without the need for an owning instance should work such as global function pointers and lambdas.

I suppose I will need to start my journey of multi-threading. Mutli-threading and networking are the last two things I want to learn on my programming journey. I'm not expecting too much resistance learning networking as I have come across a few good tutorials, one made by a member on this forum.
If you think paging some data from disk into RAM is slow, try paging it into a simian cerebrum over a pair of optical nerves. - gameprogrammingpatterns.com

binbinhfr
Posts: 78
Joined: May 9th, 2019, 10:57 pm

Re: Auto cast an iterator into its base pointer type ?

Post by binbinhfr » March 24th, 2020, 5:53 am

Thanks for your answer albinopapa. I'm looking forward to see what you will earn on the MT subject. I just wonder if you have the permission to move the MT answers from this thread to my MT thread on the forum. It would be clearer for everyone, and future MT users... ;-)

albinopapa
Posts: 4373
Joined: February 28th, 2013, 3:23 am
Location: Oklahoma, United States

Re: Auto cast an iterator into its base pointer type ?

Post by albinopapa » March 24th, 2020, 6:00 pm

I just wonder if you have the permission to move the MT answers from this thread to my MT thread on the forum.
I do have permissions to do so, but I don't think I've offered anything specific to mutli-threading so I don't believe there is anything to move and is why I have kind of stuck to this thread. If I do have anything new to offer in the way of MT, I'll post in the other thread.
If you think paging some data from disk into RAM is slow, try paging it into a simian cerebrum over a pair of optical nerves. - gameprogrammingpatterns.com

binbinhfr
Posts: 78
Joined: May 9th, 2019, 10:57 pm

Re: Auto cast an iterator into its base pointer type ?

Post by binbinhfr » March 25th, 2020, 6:50 am

coming back to a thing you said before :
for parsing a list, you suggested parsing the items, hiding the iterator stuff
from
for (auto i = list.begin(), e = list.end(); i != e; ++i)
to
for ( auto& i : list)

which is a nice idea that I used.

but if I want to erase one element of the list, do I have to return to the iterator parsing ?
and then is there a nicer way of parsing that the first for loop ?

albinopapa
Posts: 4373
Joined: February 28th, 2013, 3:23 am
Location: Oklahoma, United States

Re: Auto cast an iterator into its base pointer type ?

Post by albinopapa » March 25th, 2020, 5:04 pm

For removing one item from a vector, I would suggest a "swap and pop" approach if you don't care about the order of elements in the vector. This means you swap the one you want to delete with the last element ( not the end iterator ) in the vector then pop it off. This puts the last element in the place where the deleted one was. Doing this, you have few moves/copies being done so is more efficient.

However, as I said this only works if the order doesn't matter, otherwise simply erasing the element is the only thing you can do.

That being said, if you have multiple elements to erase, the best option is to use std::remove_if or similar. This pushes all the elements you want to erase based on some predicate to the end of the vector. Std::remove_if returns an iterator to the first element to be removed, you then use this iterator in your call to std::vector<T>::erase;

auto first_removed_item = std::remove_if( vec.begin(), vec.end(), []( Object const& obj ) { return !obj.IsNeeded(); } );
vec.erase( first_removed_item, vec.end() );

This preserves the order of the vector and takes erasing elements out of a loop. Erasing while looping isn't recommended because when you erase an element, the iterator you use and any iterators after it are invalidated. There are ways of handling erasing in a loop, but the cleanest ways in my opinion are the two that I listed here.

The std::remove_if algorithm won't work on std::list or associative containers like std::map, so that too may not be a good option. If you are using std::list, you're probably going to have to revert back to the iterator loop version. Something you can do though is separate out the tasks and move the deletion of unneeded elements to their own utility function. This way the main code gets to use the ranged for loop, then after the updates are done send the list to the custom utility function to have it pruned as needed. This should keep the code looking clean and maintainable.

Code: Select all

template<typename Container, typename Predicate>
void EraseIf( Container& cont, Predicate pred )
{
	for( auto it = cont.begin(); it != cont.end(); )
	{
		it = pred( *it ) ? cont.erase( it ) : it + 1;
	}
}
This utility function should be designed to handle std::list and std::vector at the very least. I didn't add any restrictions to the template parameters, I'll leave it to you to make sure you pass the correct objects if you intend on using it. If the predicate is going to be the same and if the container is going to be of the same type, then you can just change the signature and remove the template stuff.
If you think paging some data from disk into RAM is slow, try paging it into a simian cerebrum over a pair of optical nerves. - gameprogrammingpatterns.com

Post Reply