curl-library
rfc: curl_easy_setopt() typechecker
Date: Mon, 25 Feb 2008 15:11:01 +0100
Hi,
I played a bit with various extensions available in gcc and hacked
together a set of macros that allow for compile-time checking of
parameters passed to curl_easy_setopt(). The result is attached to this
mail as a patch, but needs probably a bit of explanation :) Also, the
patch isn't meant to be checked in as it is now.
How it works:
- curl_easy_setopt() is wrapped into a macro with a fixed number of
three arguments. Passing less or more results in an error (see below).
- First, __builtin_constant_p() [1] determines, whether the number of
the option is known at compile time. If it isn't, no checks are done
and curl_easy_setopt() is called.
- If we know what option the user wants to set, the type of the argument
is checked with __builtin_types_compatible_p() [1]. If the type
matches, everything is OK.
- if the type doesn't match, a custom warning is issued using gcc 4.3's
__attribute__((warning (...))) feature [3]. curl_easy_setopt() is
called nevertheless
Example (error-checking omitted for sake of brevity):
$ nl -b a test.c
1 #include <curl/curl.h>
2
3 int main()
4 {
5 CURL *h = curl_easy_init();
6 curl_easy_setopt(h, CURLOPT_HTTPPROXYTUNNEL,
"http://proxy");
7 curl_easy_setopt(h, CURLOPT_URL, "http://example.com/");
8 curl_easy_perform(h);
9 return 0;
10 }
$ gcc -Wall -O2 -I ./include/ -l curl test.c
test.c: In function ‘main’:
test.c:6: warning: call to ‘_curl_easy_setopt_err_long’ declared with
attribute warning: curl_easy_setopt expects a long argument for this option
Above sounds pretty simple, but of course there are a few gotchas:
- The CURLOPT_SSLENGINE_DEFAULT option doesn't take any argument. This
means that completely valid code using CURLOPT_SSLENGINE_DEFAULT can
break due to the three-arg macro. OTOH it shouldn't be too hard to
pass a dummy option argument. Also, googling for code possibly
affected by this [3], I only found one project (among open source
code, that is).
- __builtin_types_compatible_p() only works in C, not C++, so for C++
there's no typechecking. Although it might be doable using rtti.
- It only works with the not-yet-released gcc 4.3.
- The macros are a bit messy, yeah :). But it's not fatal if the
typecheck-gcc.h file gets out of sync with curl.h, it just won't check
newly added options.
- It might not work (read: it might not issue warnings) with -O0
- While it's easy to recognize a char* pointer, checking the various
function pointers prove rather hard. E.g. it's OK to write
size_t my_write(void *, size_t, size_t, struct my_buf*);
^^^^^^^^^^^^^^
curl_easy_setopt(handle, CURLOPT_WRITEFUNCTION, my_write);
but the typechecker will treat it as a different type than the
required one :-/
Anyway, except for the issue with CURLOPT_SSLENGINE_DEFAULT, it
shouldn't change API or ABI. I tried a build-test of a bunch of packages
that use libcurl in the openSUSE build service [4] and the only failures
were xmlrpc-c due to CURLOPT_SSLENGINE_DEFAULT and
libopensync-plugin-opie which uses -Werror (it gets what it asks for
;)). I didn't analyze the resulting warnings for false positives yet.
The most common warning is "curl_easy_setopt expects a long argument for
this option" (447 occurences), probably because nobody cares about int
vs. long.
Now the question: are you interested in this? I can offer maintaining
the typecheck-gcc.h file and/or documenting exactly how to maintain it.
Also, it's easy to extend this to cover other libcurl functions
(curl_easy_getinfo() is probably even more interesting than
curl_easy_setopt() due to the pointers involved) if there's interest.
Michal
[1] http://gcc.gnu.org/onlinedocs/gcc/Other-Builtins.html
[2] http://gcc.gnu.org/onlinedocs/gcc/Function-Attributes.html
[3]
http://www.google.com/codesearch?q=curl_easy_setopt.*CURLOPT_SSLENGINE_DEFAULT%5C%29
[4]
https://build.opensuse.org/project/show?project=home%3Amichal-m%3Acurl-typecheck
(requires a registration, but access is otherwise unrestricted)
- text/x-patch attachment: curl-typecheck.patch