3
\$\begingroup\$

I have written this sample code to fetch a web page in C++ using libcurl. Please review it.

#include <iostream>
#include <iostream>
#include <string>
#include <exception>
extern "C"
{
#include <curl/curl.h>
#include <stdlib.h>
}
//Exception class for curl exception
class CurlException : std::exception
{
public:
 CurlException(std::string message):m_message(message) { }
 CurlException(CURLcode error)
 {
 m_message = curl_easy_strerror(error);
 }
 const char* what() throw()
 {
 return m_message.c_str();
 }
 ~CurlException() throw() { }
private:
 std::string m_message;
};
//A tiny wrapper around Curl C Library
class CppCurl
{
public:
 CppCurl(std::string url) throw (CurlException)
 {
 m_handle = curl_easy_init();
 if ( m_handle == NULL )
 throw CurlException("Unable to initialize curl handler");
 if ( url.length() == 0 )
 throw CurlException("URL can't be of zero length");
 m_url = url;
 }
 std::string Fetch() throw (CurlException)
 {
 SetOptions();
 SendGetRequest();
 return m_data;
 }
 ~CppCurl() throw()
 {
 curl_easy_cleanup(m_handle);
 }
private:
 void SetOptions() throw (CurlException)
 {
 CURLcode res;
 //set the url
 res = curl_easy_setopt(m_handle, CURLOPT_URL, m_url.c_str());
 if ( res != CURLE_OK)
 throw CurlException(res);
 //progress bar is not require
 res = curl_easy_setopt(m_handle, CURLOPT_NOPROGRESS, 1L);
 if ( res != CURLE_OK )
 throw CurlException(res);
 //set the callback function
 res = curl_easy_setopt(m_handle, CURLOPT_WRITEFUNCTION,
 CppCurl::WriteDataCallback);
 if ( res != CURLE_OK )
 throw CurlException(res);
 //set pointer in call back function
 res = curl_easy_setopt(m_handle, CURLOPT_WRITEDATA, this);
 if ( res != CURLE_OK )
 throw CurlException(res);
 }
 void SendGetRequest()
 {
 CURLcode res;
 res = curl_easy_perform(m_handle);
 if ( res != CURLE_OK )
 throw CurlException(res);
 }
 static size_t WriteDataCallback(void *ptr, size_t size,
 size_t nmemb, void* pInstance)
 {
 return (static_cast<CppCurl*>(pInstance))->write_data(ptr, size, nmemb);
 }
 size_t write_data(void* ptr, size_t size, size_t nmemb)
 {
 size_t numOfBytes = size * nmemb;
 char *iter = (char*)ptr;
 char *iterEnd = iter + numOfBytes;
 //while ( iter != iterEnd )
 //{
 // cout<<*iter;
 // iter ++;
 //}
 m_data += std::string(iter, iterEnd);
 return numOfBytes;
 }
 CURL *m_handle;
 std::string m_url;
 std::string m_data;
};
int main()
{
 try
 {
 CppCurl ob("http://kodeyard.blogspot.in/");
 std::cout<<ob.Fetch();
 std::cout<<std::endl;
 }
 catch ( CurlException e)
 {
 std::cerr<<"Exception thrown..."<<std::endl;
 std::cerr<<e.what()<<std::endl;
 }
 return 0;
}

This is also here in the blog.

Jamal
35.2k13 gold badges134 silver badges238 bronze badges
asked Aug 6, 2012 at 21:53
\$\endgroup\$
3
  • \$\begingroup\$ Good job guys! I put this code on github.com/asashnov/tinycurl So everyone can use the latest version and improve it. \$\endgroup\$ Commented Mar 4, 2015 at 11:27
  • 1
    \$\begingroup\$ @asashnov - you cannot take code licensed using the CC-by-SA license, and then put it in the public domain. You need to read up on how copyright works. \$\endgroup\$ Commented Mar 4, 2015 at 11:50
  • \$\begingroup\$ @rolfl I think it's ok. I wrote that own(means I own it). So, I am ok with that code being in public domain. \$\endgroup\$ Commented Mar 5, 2015 at 22:35

1 Answer 1

7
\$\begingroup\$

You should probably derive from std::runtime_error.
It takes a string in the constructor as the message and stored it for use with what() in a way that is safe even in low memory situations:

Your exception class is then simplified too:

class CurlException : public std::runtime_error
{
public:
 CurlException(std::string const& message): std::runtime_error(message) {}
 // ^^^^^^^ pass message by const reference.
 CurlException(CURLcode error): std::runtime_error(curl_easy_strerror(error)) {}
};

The use of exception specifications in C++ was an experiment that ultimitely showed it was a bad idea. The only useful specification was the no throw specification (and then only when you made sure it really was no-throw).

void SetOptions() throw (CurlException)
 // ^^^^^^^^^^^^^^^^^^^^^ Get rid of this bit.

In C++11 exception specifications have been deprecated.

The curl library is a C library. It only knows about C calling conventions. Thus you can NOT pass it C++ functions. If it works it is pure luck and definately non portable.

 res = curl_easy_setopt(m_handle, CURLOPT_WRITEFUNCTION,
 CppCurl::WriteDataCallback);

Even though CppCurl::WriteDataCallback is a static function there is no guarantee that static methods have the same calling convention as a C function. Future versions of the compiler may break your code.

You should do something like this:

 extern "C" size_t WriteDataCallback(void *ptr, size_t size, size_t nmemb, void* pInstance)
 {
 CppCurl* obj = reinterpret_cast<CppCurl*>(pInstance);
 return obj->write_data(ptr, size, nmemb);
 }

Not a big deal but I would have used insert rather than +=.

 m_data += std::string(iter, iterEnd);
 // -- or.
 m_data.insert(m_data.begin(), iter, iterEnd);
answered Aug 6, 2012 at 23:01
\$\endgroup\$

Your Answer

Draft saved
Draft discarded

Sign up or log in

Sign up using Google
Sign up using Email and Password

Post as a guest

Required, but never shown

Post as a guest

Required, but never shown

By clicking "Post Your Answer", you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.