分类: C/C++
2010-07-16 11:22:36
No, really. C++ not only lets you declare a ``static extern "C"
'' function, it even requires you to do so in some circumstances. This is another tale of woe from the battles of hapless programmers to write compliant code and stay a part of the C++ programming language freakshow.
C++ is just like C. And in C, ``static
'' is the antonym of ``extern
'', at least as far as function declarations go. Saying
static extern int f(int);
in C pretty much guarantees the compiler will question your obviously lacking intelligence and print some insulting message. So why would a C++ programmer want to say this? I thought C++ is a superset of C! Aren't C++ programmers supposed to be smarter than C programmers, anyway?Actually, the compiler requires it in certain circumstances. Suppose you have some function written in C, which takes a callback argument. For concreteness, I'll talk about qsort. STL has its own sort, so this situation might still appear a bit far-fetched. But not every project can use STL. And when your project has a mature, working, debugged C library, you use it, even from C++.
You tell your C++ compiler about this function:
extern "C" qsort(void *base, size_t nmemb, size_t size,
int (*compar)(const void *, const void *));
qsort
is a C function, taking the described arguments.Now you want to write a comparison function for pointers to members of some POD class A. It's just a static function -- you only need it in the one source file (sorry, translation unit) where you call the C function:
And you want to pass the function pointerstatic int cmp_A(const void* a, int void* b)
{
const A* aa = const_cast(a);
const B* bb = const_cast(b);
return aa > bb ? -1 : bb < aa ? +1 : 0;
}
cmp_A
to qsort
-- after all, it has the right type, doesn't it?Errm, no, it doesn't. Sun Workshop's CC warns of "anachronism", and asks that you declare cmp_A
as extern "C"
. Why?
Well, it's not the name mangling issue, or anything to do with external linkage. After all, cmp_A
is invisible outside this source file (sorry, translation unit) -- it's static
!
But ``recall'' from extern "C" that this declaration affects not just linkage, but also calling convention (ABI). Not that any C++ compiler on the face of this God-forsaken planet actually uses a different calling convention for its functions than the platform's ABI guarantees for C. But it might. In effect, C++ regards the extern "C"
declaration as also affecting any function pointers taken by that function!
Now, the function qsort
is already written. In C. And it expects a function pointer to a function with C calling convention. So to pass it a function from C++, that function must have the C calling convention -- extern "C"
. And since we don't want it visible outside its source file (translation unit, sorry), it must also be static
. So we declare cmp_A
to be ``static extern "C"
''. And another syntacticmonstrosity is born.
premchai21 notes (correctly, of course!) that modern ISO C++ deprecates this use of "static
", preferring instead the use of an unnamed namespace. The true modern style would instead use
to declare and definenamespace {
extern "C" int cmp_A(const void* a, int void* b)
{
const A* aa = const_cast(a);
const B* bb = const_cast(b);
return aa > bb ? -1 : bb < aa ? +1 : 0;
}
}
cmp_A
.Note the pretty "extern
" in there. It's still not an external function -- the nameless namespace
ensures it's invisible outside the filetranslation unit.