cURL / Mailing Lists / curl-library / Single Mail

curl-library

Re: How to retrieve "context" information in write/read callbacks?

From: Stephen Collyer <scollyer_at_netspinner.co.uk>
Date: Thu, 07 Feb 2008 09:55:33 +0000

Andrea Funtò wrote:

> A callback registering function looks like:
> void set_my_callback(callback_function_ptr_t fx_ptr, void *
> callback_data);
> and when you actually use it in a C++ class, you can do something like:
> set_my_callback(<static method of my class>, (void *)this);
> so the "stub" callback you provide in the <static member of the class> can
> go back to object orientation by grabbing and casting as appropriate the
> callback_data.
> Unfortunately I cannot see this feature in libcurl: if I register a write
> callback with curl_easy_setopt(CURLOPT_WRITEFUNCTION), I can only provide a
> pointer to the memory area to be filled by libcurl before invoking my
> callback (through CURLOPT_WRITEDATA). There is no CURL handle available to
> the callback either... Am I missing some important point, or misintepreting
> the docs?

I don't fully understand your difficulty, but I have recently
implemented a C++ wrapper around the curl callbacks (using
Qt, but not using Qt signals however - the callback code is
not Qt specific).

1. I have an interface class that starts like this:

> typedef struct
> {
> double bytes_to_transfer;
> double bytes_transferred;
> }
> ProgressData;
>
> typedef boost::function<bool (ProgressData)> ProgressCallback;
>
> class TransferrerInterface
> {
> public:
>
> // Constructors and destructors
>
> virtual ~TransferrerInterface() {};
>
> // Public interface
>
> virtual void set_progress_callback(const ProgressCallback cb) = 0;
>
> virtual bool pull(const QString protocol_name,
> const QUrl &url,
> QFile &file,
> const quint64 startbyte = 0) = 0;
.
.
> private:
> ProgressCallback progress_cb_;
>

with the following trivial implementation of set_progress_callback:

> void curlPlugin::set_progress_callback(const ProgressCallback cb)
> {
> progress_cb_ = cb;
> }

with the following non-class progress callback function:

> int download_progress_callback(ProgressCallback *mdp_progress_cb,
> double bytes_to_download,
> double bytes_downloaded,
> double bytes_to_upload,
> double bytes_uploaded)
> {
> // return 0 to allow transfer to continue
>
> ProgressData current_data;
> current_data.bytes_to_transfer = bytes_to_download;
> current_data.bytes_transferred = bytes_downloaded;
>
> // call the callback if we have one
>
> if (mdp_progress_cb && ! (*mdp_progress_cb)(current_data))
> {
> return 1;
> }
>
> return 0;
> }

which is set in a class method immediately before a transfer
in the usual way:

> curl_easy_setopt(curl_, CURLOPT_PROGRESSFUNCTION, download_progress_callback);

This can then be used like so:

> class ReportProgress
> {
> public:
> bool report(ProgressData pd) { return pd.bytes_transferred < 15000; };
> };
.
.

> ProgressCallback cb;
> ReportProgress rp;
>
> cb = std::bind1st(std::mem_fun(&ReportProgress::report), &rp);
> TransferrerInterface *interface = <some code to make an object>
>
> interface->set_progress_callback(cb);
> bool result = interface->pull("HTTP", url, file);

where pull() is another method in class TransferrerInterface and
is the method which sets the progress callback.

The line where cb is set up is the one which binds the callback
to an appropriate method in an instantiated object. In this case,
the method merely aborts the transfer when more than 15000 bytes
have been retrieved.

-- 
Regards
Steve Collyer
Netspinner Ltd
Received on 2008-02-07