0

I'm using C++11 to do some thread program.
Now I get such a situation:

I have a std::set<std::future<std::string>> results to store some results of threads, of course all of these threads will return a string.

However, when I try to get the strings, I get an error as below:

passing xxx as 'this' argument of xxx discards qualifiers

According to this link, I think it's because I'm trying to call a non-const function which belongs to the element of the set. In other words, the element of the set is std::future<std::string> and std::future<std::string>::get() is non-const. This is why I get such an error.

If I'm right, does it mean that I can never declare a std::set<std::future> because its get is always unusable?

Here is my code:

set<future<string>> results;
results.insert(...); // insert some future
for(auto it = results.begin(); it != results.end();)
{
    if (it->wait_for(std::chrono::seconds(0)) == std::future_status::ready)
    {
        string tmp = it->get();  // ERROR!!!
    }
}
Yakk - Adam Nevraumont
  • 262,606
  • 27
  • 330
  • 524
Yves
  • 11,597
  • 17
  • 83
  • 180
  • yeah you'd need to move it out of the set first. seems like what you want isn't possible until c++17 via `set::extract`. cppreference notes "extract is the only way to take a move-only object out of a set" – kmdreko Aug 31 '17 at 04:41
  • 4
    Why on earth would you use a `set` for this? – T.C. Aug 31 '17 at 05:26
  • @T.C. Because in my case, I need a container, whose algorithm is good for insertion, removing and for iterating. Well, I think I should use `set`. – Yves Aug 31 '17 at 06:03
  • @T.C. I have many threads. I need a container, which can store the `future`s of the threads and when a thread finishes, remove its `future` from the container. At the same time, threads keep being generated so I need to store them into the container. So I think `set` is the best choice. – Yves Aug 31 '17 at 06:06
  • 2
    `std::future` doesn't support `<`, so that shouldn't compile long before you call `.get()`? Please post [MCVE]s, not pseudo code. – Yakk - Adam Nevraumont Aug 31 '17 at 13:37

1 Answers1

5

When thinking of what container to use, you should think "can I do this with a std::vector?". If your answer is no, you should ask "no, really, can I do this with a std::vector?".

using namespace std::literals;
std::vector<std::future<std::string>> results;
constexpr auto threads = 10;
for (int i = 0; i < threads; ++i) {
  results.push_back( std::async( std::launch::async,
    [i]{
      std::this_thread::sleep_for( std::chrono::milliseconds((threads-i)%(threads/3)) );
      std::stringstream ss;
      ss << "hello world " << i;
      return ss.str();
    }
  ) );
}
for (auto it = results.begin(); it != results.end();) {
  if (it->wait_for( 0s ) == std::future_status::ready) {
    std::string tmp = it->get();
    std::cout << tmp << "\n";
    std::swap( *it, results.back() );  // noop if already at back
    if (std::next(it)==results.end()) it = results.begin(); // do this before pop back
    results.pop_back(); // Or this could invalidate it
    if (results.begin()==results.end()) break; // empty vector has to be caught early, as it is invalidated if vector is now empty
    continue;
  } else {
    ++it;
    if (it == results.end()) it = results.begin();
    continue;
  }     
}

Live example

Doing this with a std::set is both a bad and impossible idea.

A bad idea, because std::set is a node based container that does effort to maintain sort order. We don't need nodes. We don't need a sort order. So we are using a more powerful container than we need.

Impossible, because the contents of std::set are by design immutable.

Impossible, because std::future provides no operator<, nor is there a reasonable place to hook a user-provided comparison function onto them.

Note that shared_futures have a const get() method, so they can be stored in an immutable manner. But they still provide no "hooks" for a user-provided ordering operation.

Yakk - Adam Nevraumont
  • 262,606
  • 27
  • 330
  • 524
  • 3
    Those `continue`s seem superflous – Caleth Aug 31 '17 at 13:56
  • @Yakk How come it is not possible to achieve the same thing with `std::set`? – knoxgon Aug 31 '17 at 13:57
  • 1
    @Caleth They are; except they mean you don't have to keep reading to determine if there is anything hiding below. And I am lazy when reading. – Yakk - Adam Nevraumont Aug 31 '17 at 13:57
  • 1
    @VG For many reasons, starting with the lack of `operator<` on `std::future`. Added to answer. – Yakk - Adam Nevraumont Aug 31 '17 at 13:58
  • std::vector is a very very useful container, but your first sentence raises all kinds of warning flags with me. It's an example of "when your favorite tool is a hammer, everything looks like a nail." thinking. For one simple example, it it quite possible to use a vector as a queue (FIFO) but that is most certainly not the right tool for the job. – Dale Wilson Aug 31 '17 at 14:03
  • @DaleWilson Have you considered writing a circular buffer adapter on a vector? That will do away with constantly allocating and deallocating memory when the queue is staying the same size. (And yes, if the queue grows large enough, you probably want to switch to a block-based system to avoid having to have a many-gigabyte contiguous vector) – Yakk - Adam Nevraumont Aug 31 '17 at 14:04
  • Of course I have, Have you ever considered what would happen to your vector based circular buffer queue if you needed to grow it? [note we aren't supposed to have lengthy conversations here, so I'm going to bow out. You may get in the last word if you want to. – Dale Wilson Aug 31 '17 at 14:06
  • Another reason `set` is a bad idea: How many elements should `std::set, MythicalFutureLess> someSet { std::async( std::launch_async, [](){ std::this_thread::sleep_for(10000); return "Same Value"; } ), std::async( std::launch_async, [](){ std::this_thread::sleep_for(10000); return "Same Value"; } ) }` have? – Caleth Aug 31 '17 at 14:10
  • @DaleWilson [Yes, I have](http://coliru.stacked-crooked.com/a/b877d6ea38387790) (note that this implementation assumes no-throw move construction; a slight modification is required with throwing move ctors) – Yakk - Adam Nevraumont Aug 31 '17 at 14:33