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.
-
\$\begingroup\$ Good job guys! I put this code on github.com/asashnov/tinycurl So everyone can use the latest version and improve it. \$\endgroup\$asashnov– asashnov2015年03月04日 11:27:27 +00:00Commented 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\$rolfl– rolfl2015年03月04日 11:50:52 +00:00Commented 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\$asit_dhal– asit_dhal2015年03月05日 22:35:48 +00:00Commented Mar 5, 2015 at 22:35
1 Answer 1
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);