#include <iostream>
#include <thread>
#include <mutex>
#include <vector>
#include <future>
#include <sstream>

using namespace std;

void SleepThenSetData(int sleepTime, promise<string>& returnPromise)
{
	//Sleep for x seconds
	this_thread::sleep_for(chrono::seconds(sleepTime));
	stringstream ss;
	//Create string describing sleep duration
	ss << "I have slept for " << sleepTime << " seconds!" << endl;

	//Write string into promise
	returnPromise.set_value(ss.str());
}

int main(int argc, char** argv)
{
	const int numThreads = 10;
	//Array of promises - one per thread
	promise<string>* returnPromises = new promise<string>[numThreads];
	vector<thread*> threads;
	//Vector of futures - to be set from promises
	vector<future<string>> returnFutures;

	for (int i = 0; i < numThreads; i++)
	{
		//Launch thread, grab future.
		threads.push_back(new thread([i, returnPromises] {SleepThenSetData((i + 1) * 4, returnPromises[i]); }));
		returnFutures.push_back(returnPromises[i].get_future());
	}

	//Check whether there are still valid futures - valid returns true while future remains unread.
	while (find_if(returnFutures.begin(), returnFutures.end(), [](future<string>& f) {return f.valid(); }) != returnFutures.end())
	{
		//Attempt to obtain iterator reference to valid, readable future. Check against valid, i.e. unread and future_status::ready, i.e. data is ready to be read.
		auto it = find_if(returnFutures.begin(), returnFutures.end(), [](future<string>& f) { return f.valid() && f.wait_for(chrono::milliseconds(1)) == future_status::ready; });

		//Check whether iterator points to the end of the vector - if it does, there is no future which matches the above two conditions.
		//i.e. no future is available to be read.
		//If, however, the iterator points to something else, it is a readable future.
		if (it != returnFutures.end())
		{
			//Read the future - output to console, in this case.
			cout << it->get();
		}

		//Sleep thread to avoid a spinlock.
		this_thread::sleep_for(chrono::milliseconds(10));
	}

	//Clean up - join threads, clean up memory.
	for (auto t : threads)
	{
		t->join();
		delete t;
	}

	delete[] returnPromises;

	getchar();
}