Oh, a rappa!
That title should probably read “OO API Wrapper.” Sorry, wrong regional dialect. My mistake.
I’m pretty sure that anyone who has (successfully) used an API at some point will tell you that they are wonderful. What I’m also guessing, from my admittedly limited experience, however, is that a lot of them will also tell you, if they’re honest, that APIs can be clumsy. Why is that? From the C/C++ perspective, the answer is dreadfully clear to me. Many of the most basic and useful APIs are written in C. Which means the large portion of them were written using a procedural paradigm.
You’re required to create a resource handle object (be it a pointer to some struct or class as in the MySQL and cURL C APIs, or maybe just a plain old int, as is the case for using POSIX threads), and then call a barrage of functions (passing the handle as a parameter each time) to manipulate it. Straightforward enough for any reasonable person, I’d estimate. Arbitrarily using cURL as the example, we typically see something like the following…
CURL* handle;
curl_easy_setopt( handle, CURLOPT_ERRORBUFFER, _error );
curl_easy_setopt( handle, CURLOPT_FAILONERROR, true );
curl_easy_setopt( handle, CURLOPT_FOLLOWLOCATION, true );
curl_easy_setopt( handle, CURLOPT_HEADER, false );
curl_easy_setopt( handle, CURLOPT_NOSIGNAL, 1 );
curl_easy_setopt( handle, CURLOPT_AUTOREFERER, 1 );
curl_easy_setopt( handle, CURLOPT_SSL_VERIFYPEER, 0 );
curl_easy_setopt( handle, CURLOPT_NOPROGRESS, true );
curl_easy_setopt( handle, CURLOPT_CONNECTTIMEOUT, timeout );
curl_easy_setopt( handle, CURLOPT_MAXREDIRS, 5 );
curl_easy_setopt( handle, CURLOPT_TIMEOUT, wait );
curl_easy_setopt( handle, CURLOPT_USERAGENT, agent.c_str() );
curl_easy_setopt( handle, CURLOPT_VERBOSE, false );
curl_easy_perform( handle );
Not too bad, a few lines for the ability to browse the web. Decent enough, says me. Especially when the alternative (without the API that is) would be to use the system() function to pass a string like the following to the OS.
curl -f -v –connect-timeout 30 –data-ascii –url http://www.somesite.org/index.html
Portability? What’s that? Worse yet, you could use libcurl nude. Yikes. And by the way, that string isn’t NEAR complete, if we’re dedicated to all of the customizations represented in the sample code. I don’t even want to think about that. So… looking at it this way, an API couldn’t get better. But seriously. What kind of self-respecting programmer doesn’t try, at least once in a while, to fix what isn’t broken? Come on… admit it. You’ve done it. You did it once a long time ago and learned your lesson. And then a situation, much like the above, arises and you said to yourself “Robert – ” since that may in fact be how you address yourself – “Robert, this works… but I can do better.” It’s natural. Some of these APIs – as much functionality as they offer, and as wonderful as they are, just don’t cut it sometimes. The libcurl C API, for instance, requires you to implement two callbacks, which are responsible for some very low level memory allocations and pointer dereferences. Stuff that would make one of this new generation of upcoming greenhorn developers (who know naught but their fancy, highly abstracted, memory managed interpreters) run home and cry in the fetal position. Jeez – I didn’t even want to do it, and I’m a fan of the low level stuff. So who wants to do that every time you write an application that needs to rip a web page? Who wants to do it for a SINGLE app that needs to rip just one page?
Not me.
Probably not you.
And, if you’re anything like me, you despise re-writing code. One of the biggest problems with this type of code is the fact that it flies in the face of all that fancy object oriented paradigm you’re expected to exalt. This is my idea. For about a year now I’ve been doing this, and it’s worked wonderfully. Before you use an API, think about it. Then, take the hour to learn the API. Test it a few times. Be a little crazy. Get comfortable with it. And then take a second hour… maybe two… and write a well formed, object oriented wrapper class for the API. Commit this class to your favorite version control system (I prefer Subversion). Then take the time to debug it. Let’s face it. Sometimes, you just have to sidetrack from your project, and I think this is a perfectly legitimate cause.
Let’s examine the situation. You’ve now got a properly written class wrapping an API at your disposal. Granted, some APIs are quite large, so this may be no trivial undertaking. But, get the bare bones working first, and then add on as you need/want to – as with any development project. Instead of all that code *up there* (that doesn’t include stuff like those nasty callbacks, which, using my method, you’d only have to write once) all you need is something like this:
Curl curl;
curl.setUrl( "http://some.place.dot.com/index/page.html" );
curl.exec();
Oh, and the html you grabbed can be accessed via curl.getResult(). Isn’t that a lot better? Granted, you’re now probably half a day “off course.” But guess what? You were going to have to learn and get used to the API anyway. You were going to have to hack your way around it’s quirks anyway. You were going to have to write 90% of that code anyway in your actual application. And, the real reward, the next time you have to use cURL in an application, it’s as easy as copying the wrapper class files into your project directory and using them.
Personally, I’ve got wrappers written for cURL, MySQL, PCRE (Didn’t do much for this one, just re-packaged libpcrecpp from teh kind folks over at Google), Pthread, and a C++ implementation of the MD5 algorithm I found online. I’ve also got a wrapper on some common time functions in a class called “Timer.” And they’re all committed to a single version control project. When I need to add functionality, I go back, update this project with the proper code, and then, once all is copesetic, copy the updated files to whatever project needed the wrapper.
Now, of course, the wrapper can’t do EVERYTHING for you. Some APIs, like cURL and Pthread, require callbacks and whatnot. Of course, you deal with these case-by-case as appropriate for your situation. But I’ve found that these wrappers can save a LOT of time in both the writing and debugging phases. Also, they pay for themselves the first time you use them, if for no other reason than the fact that they keep your project code looking neat and object oriented, as it should be. Develop them generically, and you can use the same wrappers for multiple projects (of course adapting them as needed, when needed).