jmhobbs

Better libcurl from C++

I've been a longtime fan of libcurl. But I'm a C++ author and so the c level of that is not where I want to be (and cURLpp looks ungainly for my minor usage).

Firing up google on "libcurl C++" yields "Using libcurl from C++ | Luckyspin.org" as the #1 entry. This article show a good starting example, but it's not quite there. Here's a cleaned up snippet:

// Write all expected data in here
static string buffer;
  
// This is the writer call back function used by curl  
static int writer(char *data, size_t size, size_t nmemb, std::string *buffer) {
  int result = 0;  
  if (buffer != NULL) {
    buffer->append(data, size * nmemb);  
    result = size * nmemb;
  }
  return result;
}
.
.
.
int main(int argc, char* argv[]) {
.
.
.
  curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, writer);
  curl_easy_setopt(curl, CURLOPT_WRITEDATA, &buffer);
.
.
.

Now that is a totally legitimate use and it works fine. But do you see the problem? Yep, the data (buffer) is global, as is the writer but that isn't as big a deal. This is not good for many applications and is not good C++.

So let's encapsulate that bad boy shall we? It's really not all that hard, so here's some example code.

#include 
#include 
#include 

class MyCurlObject {
  public:
    MyCurlObject (std::string url) {
      curl = curl_easy_init();
      if(!curl)
        throw std::string ("Curl did not initialize!");

      curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, &MyCurlObject::curlWriter);
      curl_easy_setopt(curl, CURLOPT_WRITEDATA, &curlBuffer);
      curl_easy_setopt(curl, CURLOPT_URL, url.c_str());
      curl_easy_perform(curl);
    };

    static int curlWriter(char *data, size_t size, size_t nmemb, std::string *buffer) {
      int result = 0;
      if (buffer != NULL) {
        buffer->append(data, size * nmemb);
        result = size * nmemb;
      }
      return result;
    }

    std::string getData () { return curlBuffer; }

  protected:
    CURL * curl;
    std::string curlBuffer;
};

int main (int argc, char ** argv) {
  try {
    MyCurlObject mco ("http://www.google.com/");
    MyCurlObject moco ("http://www.yahoo.com/");
    std::cout << moco.getData() << std::endl;
    std::cout << "--------------------------------------------" << std::endl;
    std::cout << mco.getData() << std::endl;
  }
  catch (std::string & s) {
    std::cerr << "Oops! " << s << std::endl;
  }
}

Now that example has all kinds of missing things and the object is useless beyond one request but that is not the point, it's a contrived example. The point is that your buffer is no longer global and it's protected by your class. You can make multiple requests without having to grab the data off by yourself. That's the big deal.

I don't want this to seem like I am putting down LuckySpin. It's a good example, I feel this is just a step better.