Reputation: 39871
I create a new object and set a data and a callback something like this:
class DownloadData
{
std::function<void(int, bool)> m_callback;
int m_data;
public:
void sendHttpRequest()
{
// send request with data
}
private:
void getHttpResponse(int responseCode)
{
if (responseCode == 0)
{
// save data
m_callback(responseCode, true);
delete this;
return;
}
// some processing here
if (responseCode == 1 && some other condition here)
{
m_callback(responseCode, false);
delete this;
return;
}
}
}
Now the usage - I create a new object:
if (isNeededToDownloadTheFile)
{
DownloadData* p = new DownloadData(15, [](){});
p->sendHttpRequest();
}
But as you can see https://isocpp.org/wiki/faq/freestore-mgmt#delete-this it is highly not desirable to make a suicide. Is there a good design pattern or an approach for this?
Upvotes: 3
Views: 1807
Reputation: 39380
If you want to put the delete out of that function, the only way is to store the object somehow. However, this raises the ownership questions: who is the owner of the asynchronous http request that's supposed to call a callback?
In this scenario, doing the GCs job actually makes the code pretty clear. However, if you wanted to make it more adaptable to C++, I'd probably settle on a promise-like interface, similar to std::async
. That way the synchronous code path makes it way easier to store the promise objects.
You asked for a code example, so there goes:
Typical approach would look like this:
{
DownloadData* p = new DownloadData(15, [](auto data){
print(data)
});
p->sendHttpRequest();
}
Once the data is available, it can be printed. However, you can look at the problem "from the other end":
{
Future<MyData> f = DownloadData(15).getFuture();
// now you can either
// a) synchronously wait for the future
// b) return it for further processing
return f;
}
f
will hold the actual value once the request actually processes. That way you can push it as if it was a regular value all the way up to the place where that value is actually needed, and wait for it there. Of course, if you consume it asynchronously, you might as well spawn another asynchronous action for that.
The implementation of the Future
is something that's outside of the scope of this answer, I think, but then again numerous resources are available online. The concept of Promises and Futures isn't something specific to C++.
Upvotes: 1
Reputation: 48625
If the caller keeps a reference to the downloading object then it can erase it when the download signals it has ended:
class DownloadData
{
// true until download stops (atomic to prevent race)
std::atomic_bool m_downloading;
int m_data;
std::function<void(int, bool)> m_callback;
public:
DownloadData(int data, std::function<void(int, bool)> callback)
: m_downloading(true), m_data(data), m_callback(callback) {}
void sendHttpRequest()
{
// send request with data
}
// called asynchronously to detect dead downloads
bool ended() const { return !m_downloading; }
private:
void getHttpResponse(int responseCode)
{
if (responseCode == 0)
{
// save data
m_callback(responseCode, true);
m_downloading = false; // signal end
return;
}
// some processing here
if(responseCode == 1)
{
m_callback(responseCode, false);
m_downloading = false; // signal end
return;
}
}
};
Then from the caller's side:
std::vector<std::unique_ptr<DownloadData>> downloads;
// ... other code ...
if (isNeededToDownloadTheFile)
{
// clean current downloads by deleting all those
// whose download is ended
downloads.erase(std::remove_if(downloads.begin(), downloads.end(),
[](std::unique_ptr<DownloadData> const& d)
{
return d->ended();
}), downloads.end());
// store this away to keep it alive until its download ends
downloads.push_back(std::make_unique<DownloadData>(15, [](int, bool){}));
downloads.back()->sendHttpRequest();
}
// ... etc ...
Upvotes: 0
Reputation: 8926
You could put them in a vector
or list
, have getHttpResponse()
set a flag instead of delete this
when it's completed, and then have another part of the code occasionally traverse the list looking for completed requests.
That would also allow you to implement a timeout. If the request hasn't returned in a day, it's probably not going to and you should delete that object.
Upvotes: 2