Is it possible to overload functions with extern linkage? - c++

I saw a strange code, in declaration of std::atexit:
extern "C" int atexit( void (*func)() );
extern "C++" int atexit( void (*func)() ); // ... why are there two functions?
I guess it's some kind of function overloading, but it seems obviously wrong.
What's it? and why is it necessary?

The problem with your source
This is cppreference being a little misleading.
Declaring the same function twice with different storage-class-specifiers is illegal and causes a build failure. If you look at the source for libstdc++ (GCC's standard library implementation), you'll see that only the extern "C" version is actually provided.
The standard's take on this
Although [C++11: 18.5] lists both declarations, just as does cppreference, this does not mean that both may be used in a single implementation; it means that an implementation may choose to declare either of them: that is, it accounts for [C++11: 17.6.4.3.3/4] which says:
Each function signature from the Standard C library declared with external linkage is reserved to the implementation for use as a function signature with both extern "C" and extern "C++" linkage [..]
Also:
[C++11: 17.6.2.3/2]: Whether a name from the C standard library declared with external linkage has extern "C" or extern "C++" linkage is implementation-defined. It is recommended that an implementation use extern "C++" linkage for this purpose
The rule is made explicitly clear here:
[C++11: 7.5/5]: If two declarations declare functions with the same name and parameter-type-list (8.3.5) to be members of the same namespace or declare objects with the same name to be members of the same namespace and the declarations give the names different language linkages, the program is ill-formed; no diagnostic is required if the declarations appear in different translation units. [..]
Why this can be confusing
To my mind, this does cause some problems in other places within the standard; for example:
[C++11: 25.5/2]: The contents are the same as the Standard C library header <stdlib.h> with the following exceptions:
[C++11: 25.5/3]: The function signature:
bsearch(const void *, const void *, size_t, size_t,
int (*)(const void *, const void *));
is replaced by the two declarations:
extern "C" void *bsearch(const void *key, const void *base,
size_t nmemb, size_t size,
int (*compar)(const void *, const void *));
extern "C++" void *bsearch(const void *key, const void *base,
size_t nmemb, size_t size,
int (*compar)(const void *, const void *));
both of which have the same behavior as the original declaration.
I consider that to be a language defect.

I think you can not see such a code. At least there might be preprocessor directives similar to #if or #ifdef that separate these two declarations because according to the C++ Standard
5 If two declarations declare functions with the same name and parameter-type-list (8.3.5) to be members of the same namespace or declare objects with the same name to be members of the same namespace and the declarations give the names different language linkages, the program is ill-formed; no diagnostic is required if the declarations appear in different translation units. Except for functions with C++ linkage, a function declaration without a linkage specification shall not precede the first linkage specification for that function. A function can be declared without a linkage specification after an explicit linkage specification has been seen; the linkage explicitly specified in the earlier declaration is not affected by such a function declaration.

Related

Conflicting declaration of function with ‘C’ linkage

Following compiles without error:
extern "C" void func( void );
extern void func( void );
If I change order of declarations,
extern void func( void );
extern "C" void func( void );
I get compiler error: conflicting declaration of ‘void func()’ with ‘C’ linkage.
C++ standard says:
At most one function with a particular name can have "C" linkage (regardless of namespace)
In both cases it is the same function. My assumption is that C++ compiler stores internally functions signature it will compile. In first example, compiler consideres second declaration as valid and uses first declaration to compile. In second example it is not the case. It regrads both declarations as diferent declaration.
What are technical details behind it?
gcc 9.4.0 and msvc 19.29.30147
Re-declarations of a function are not required to repeat their linkage specification. Per Language linkage from cppreference:
A function can be re-declared without a linkage specification after it was declared with a language specification, the second declaration will reuse the first language linkage. The opposite is not true: if the first declaration has no language linkage, it is assumed "C++", and redeclare with another language is an error.
In the first snippet, you declare a function with "C" linkage and then later re-declare it with no explicit linkage. This is okay; the original linkage is assumed. However, in the second snippet, you declare a function with (implicit) "C++" linkage, and then later attempt to re-declare it with "C" linkage---that's a conflict, hence the error.

Is the specification of `bsearch` in C++11 & C++14 defective?

Following on from my answer to this question, in both C++11 and C++14:
[C++11, C++14: 25.5/2]: The contents are the same as the Standard C library header <stdlib.h> with the following exceptions:
[C++11, C++14: 25.5/3]: The function signature:
bsearch(const void *, const void *, size_t, size_t,
int (*)(const void *, const void *));
is replaced by the two declarations:
extern "C" void *bsearch(const void *key, const void *base,
size_t nmemb, size_t size,
int (*compar)(const void *, const void *));
extern "C++" void *bsearch(const void *key, const void *base,
size_t nmemb, size_t size,
int (*compar)(const void *, const void *));
both of which have the same behavior as the original declaration.
However,
[C++11, C++14: 7.5/5]: If two declarations declare functions with the same name and parameter-type-list (8.3.5) to be members of the same namespace or declare objects with the same name to be members of the same namespace and the declarations give the names different language linkages, the program is ill-formed; no diagnostic is required if the declarations appear in different translation units. [..]
Is this a defect?
But the parameter types list are not the same. In one, compar is a pointer to a function with "C" language linkage, in the other one, it's a pointer to a function with "C++" language linkage.
C++11, 7.5 specifies:
1 ... Two function types with different language
linkages are distinct types even if they are otherwise identical.
4 In a linkage-specification, the specified language linkage applies to the function
types of all function declarators, function names with external linkage, and variable names with external
linkage declared within the linkage-specification. [ Example:
extern "C" void f1(void(*pf)(int));
// the name f1 and its function type have C language
// linkage; pf is a pointer to a C function
The seeming inconsistency between 7.5/1 and 7.5/5 is solved when realising that 1 talks about function types, while 5 addresses function names.

Typedef for a pointer to a C function inside C++

I'm trying to declare a function pointer type (not a variable) that would specify C calling convention. Both
extern "C" typedef void (*PFunc)();
and
typedef extern "C" void (*PFunc)();
produce a syntax error, when used on function level.
extern "C" { typedef void (*PFunc)(); }
extern "C" typedef void (*PFunc)();
both work when used on the global scope; I'd rather keep it local.
What's the proper way, please? I don't want to use compiler specific extentions.
According to this, matching calling conventions between the pointer and the target is the safe thing to do when calling inderectly functions that are declared as extern "C", because C and C++ calling conventions might mismatch. In real life they mostly don't, but still, correctness.
Contrary to what some commenters have said here, calling convention/linkage is part of a function type. It has to be, otherwise this information would not be known when calling the function through the function pointer:
[C++11: 7.5/1]: All function types, function names with external linkage, and variable names with external linkage have a language linkage. [..]
The correct declaration is:
extern "C" typedef void (*PFunc)();
However, at block-scope, you cannot declare a function to have any linkage:
[C++11: 7.5/4]: Linkage specifications nest. When linkage specifications nest, the innermost one determines the language linkage. A linkage specification does not establish a scope. A linkage-specification shall occur only in namespace scope (3.3). In a linkage-specification, the specified language linkage applies to the function types of all function declarators, function names with external linkage, and variable names with external linkage declared within the linkage-specification. [..]
So, you will have to stick with a namespace-scope declaration. If you still want to restrict the visibility of the declaration, you could "shelter" it from other code in the same translation unit, using an unnamed namespace.
Yes, linkage specification (extern "C") should be in namespace scope (or global) by Standard.
extern "C" typedef void (*pf)();
is right. If your compiler doesn't accept it, it is a bug of the compiler.
Guessing, but it might be because you are declaring the function as (), in C that means it can take both an INT and VOID... Try explicitly saying (void)

Overload resolution on extern "C" and "C++" version of qsort()/bsearch()

In C++, there are two versions of qsort() provided by the standard library:
extern "C" void qsort(void* base, size_t nmemb, size_t size, int (*compar)(const void*, const void*));
extern "C++" void qsort(void* base, size_t nmemb, size_t size, int (*compar)(const void*, const void*));
bsearch() is about the same.
My question is, how does overload resolution work when calling qsort()? Does it automatically link to the appropriate function based on the linkage type ("C" or "C++") of the function pointer passed as the last argument? Or the caller needs to specify explicitly with some sort of extra syntax?
(Let's just put away the temptation to call std::sort for a second...)
The int (*compar)(const void*, const void*) parameter has different types for the two different overloads. For the first overload, it's an extern "C" function pointer parameter. For the second overload, it's an extern "C++" function pointer parameter. Any function pointer you pass to qsort will already have some sort of linkage, and that is what gets used to determine which overload to call.
To quote the standard:
7.5 Linkage specifications [dcl.link]
All function types, function names with external linkage, and variable names with external linkage have a language linkage. [...] The default language linkage of all function types, function names, and variable names is C++ language linkage. Two function types with different language linkages are distinct types even if they are otherwise identical.
In fact, I don't think the standard actually means to require that the two qsort overloads really do have different linkage. Unlike in C, user-provided declarations of standard library functions are not allowed; the relevant difference between them is the type of compar. They could have been declared as
extern "C" typedef int (*__compar_fnp_c)(const void *, const void *);
extern "C++" typedef int (*__compar_fnp_cxx)(const void *, const void *);
void qsort(void* base, size_t nmemb, size_t size, __compar_fnp_c compar);
void qsort(void* base, size_t nmemb, size_t size, __compar_fnp_cxx compar);
where it should be more obvious that __compar_fnp_c and __compar_fnp_cxx are different types. That said, the as-if rule doesn't allow this implementation, since it would break code that takes a pointer or reference to qsort.
Note that GCC, as well as some other compilers, don't implement this correctly, and don't treat linkage as part of the function pointer's type. On such implementations, only one version of qsort will have been made available, to prevent a conflict during overload resolution.

Overload resolution with extern "C" linkage

In a mixed C/C++ project, we need to make a call from C to a C++ function. The function to be called is overloaded as three separate functions, but we can ignore that from the C-side, we just pick the one most suitable and stick to that one.
There's two ways to do this: (1) write a small C++ wrapper with a extern "C" function that forwards the call to the chosen overloaded function, or (2) the hackish way to just declare the one function we want to call from C as extern "C".
The question is, is there any disadvantages (apart from nightmares and bad karma) to go for the second variant? In other words, given three overloaded function, where one is declared as exern "C", should we expect trouble with the C++ side, or is this well defined according to the standard?
I believe the language in the standard is specifically written to allow exactly one function with "C" linkage, and an arbitrary number of other functions with "C++" linkage that overload the same name (§[dcl.link]/6):
At most one function with a particular name can have C language linkage. Two declarations for a function with C language linkage with the same function name (ignoring the namespace names that qualify it) that appear in different namespace scopes refer to the same function. Two declarations for an object with C language linkage with the same name (ignoring the namespace names that qualify it) that appear in different namespace scopes refer to the same object.
The standard shows the following example:
complex sqrt(complex); // C + + linkage by default
extern "C" {
double sqrt(double); // C linkage
}
Even if it was allowed by the standard, future maintainers of the code will probably be extremely confused and might even remove the extern "C", breaking the C code (possibly far enough later that the events aren't linkable).
Just write the wrapper.
EDIT:
From C++03 7.5/5:
If two declarations of the same
function or object specify different
linkage specifications (that is, the
linkage specifications of these
declarations specify different
string literals), the program is
ill-formed if the declarations appear
in the same translation unit, and the
one definition rule (3.2) applies if
the declarations appear in different
translation units...
I interpret this to not apply since C and C++ functions with the same name aren't actually the same function but this interpretation may be wrong.
Then from C++03 7.5/6:
At most one function with a particular
name can have C language linkage...
This then implies that you could have other, non-C-linkage, functions with the same name. In this case, C++ overloads.
As long as you follow the other rules for extern-C functions (such as their special name requirements), specifying one of the overloads as extern-C is fine according to the standard. If you happen to use function pointers to these functions, be aware that language linkage is part of the function type, and needing a function pointer to this function may decide the issue for you.
Otherwise, I don't see any significant disadvantages. Even the potential disadvantage of copying parameters and return value can be mitigated by compiler- and implementation-specifics that allow you to inline the function – if that is determined to be a problem.
namespace your_project { // You do use one, right? :)
void f(int x);
void f(char x);
void f(other_overloads x);
}
extern "C"
void f(int x) {
your_project::f(x);
}
(This answer applies to C++14; other answers so far are C++03).
It is permitted to use overloading. If there is an extern "C" function definition of some particular name then the following conditions apply (references to C++14 in brackets):
The declaration of the extern "C" function must be visible at the point of any declaration or definition of overloads of that function name (7.5/5)
There must be no other extern "C" definition of a function or variable with the same name, anywhere. (7.5/6)
An overloaded function with the same name must not be declared at global scope. (7.5/6)
Within the same namespace as the extern "C" function, there must not be another function declaration with the same name and parameter list. (7.5/5)
If any violation of the above rules occurs in the same translation unit the compiler must diagnose it; otherwise it is undefined behaviour with no diagnostic required.
So your header file might look something like:
namespace foo
{
extern "C" void bar();
void bar(int);
void bar(std::string);
}
The last bullet point says that you cannot overload solely on linkage; this is ill-formed:
namespace foo
{
extern "C" void bar();
void bar(); // error
}
However you can do this at different namespaces:
extern "C" void bar();
namespace foo
{
void bar();
}
in which case , normal rules of unqualified lookup determine whether a call bar() in some code finds ::bar, foo::bar, or ambiguous.