Discussion:
Warning for incompatible functions declared 'extern "C"'?
Nick
2014-07-23 22:53:09 UTC
Permalink
I have an oversight in my code where I'm declaring & defining a function
with C-linkage, though it's not possible.

Example snippet:

<snip>
//------------------------------------------------------------

#ifdef __cplusplus
extern "C"
{
#endif

// ... some functions which are compatible with C linkage

// Intended to be a helper function not exposed from library
std::string GetEngineVersion()
{
// ...
}

#ifdef __cplusplus
}
#endif

//------------------------------------------------------------
</snip>


Obviously the GetEngineVersion function cannot have C linkage because it
returns a C++ class.

My question is: does GCC have a warning for this scenario?
Specifically, can it warn when something is declared extern "C" that's
incompatible with C linkage?

I compile with the following warning flags and I didn't get a warning:
-Wall -Wunused-parameter -Wextra -Weffc++ -Wctor-dtor-privacy
-Wnon-virtual-dtor -Wreorder -Wold-style-cast -Woverloaded-virtual
-Werror


Incidentally, when I compile the same code in VS2013 I get this warning:

warning C4190: 'GetEngineVersion' has C-linkage specified, but returns
UDT
'std::basic_string<char,std::char_traits<char>,std::allocator<char>>'
which is incompatible with C


Best regards,
Nick
Jonathan Wakely
2014-07-24 08:19:59 UTC
Permalink
Post by Nick
I have an oversight in my code where I'm declaring & defining a function
with C-linkage, though it's not possible.
<snip>
//------------------------------------------------------------
#ifdef __cplusplus
extern "C"
{
#endif
// ... some functions which are compatible with C linkage
// Intended to be a helper function not exposed from library
std::string GetEngineVersion()
{
// ...
}
#ifdef __cplusplus
}
#endif
//------------------------------------------------------------
</snip>
Obviously the GetEngineVersion function cannot have C linkage because it
returns a C++ class.
That is valid C++ code. C linkage doesn't mean the function has to
actually be usable from C.

You can also declare functions with C linkage that take reference
parameters or throw exceptions.
Post by Nick
My question is: does GCC have a warning for this scenario?
Specifically, can it warn when something is declared extern "C" that's
incompatible with C linkage?
No, there is no such warning as far as I know.

Such a warning doesn't seem unreasonable, since *usually* (not not
always) you want functions with C language linkage to be callable from
C.
Nick
2014-07-25 04:47:42 UTC
Permalink
Post by Jonathan Wakely
That is valid C++ code. C linkage doesn't mean the function has to
actually be usable from C.
You can also declare functions with C linkage that take reference
parameters or throw exceptions.
I didn't know that. Out of curiosity...I assumed that one of the
ramifications of declaring some code w/ C linkage was that it emitted
symbols that have C mangling. Is that true? If so, how are C++ antics
handled in this scenario?
Post by Jonathan Wakely
Post by Nick
My question is: does GCC have a warning for this scenario?
Specifically, can it warn when something is declared extern "C" that's
incompatible with C linkage?
No, there is no such warning as far as I know.
Such a warning doesn't seem unreasonable, since *usually* (not not
always) you want functions with C language linkage to be callable from
C.
Ok well let me know if I should open an enhancement request ticket for
this.
Jędrzej Dudkiewicz
2014-07-25 05:26:53 UTC
Permalink
Post by Nick
Post by Jonathan Wakely
That is valid C++ code. C linkage doesn't mean the function has to
actually be usable from C.
You can also declare functions with C linkage that take reference
parameters or throw exceptions.
I didn't know that. Out of curiosity...I assumed that one of the
ramifications of declaring some code w/ C linkage was that it emitted
symbols that have C mangling. Is that true? If so, how are C++ antics
handled in this scenario?
Look at this:

[***@facs-bugtracker ~]$ cat y.cc
#include <string>

extern "C" {
std::string GetVersion() { return "1"; }
}


[***@facs-bugtracker ~]$ objdump -t y.o

y.o: file format elf64-x86-64

SYMBOL TABLE:
[....]
0000000000000000 g F .text 0000000000000061 GetVersion
[....]

Name GetVersion is not mangled, so it's a bare name. Note, that if you
want to use it elsewhere, you'd have to declare it somehow. So if
GetVersion returns std::string, your header would wound up with
"#include <string>" and it won't be understood by C compiler, or you
could declare it as something else, but then you'd be lying to the
compiler, and then you're on your own. But if it's included in C++
sources, it will be ok, since function would be declared with proper
return value and it will be annotated as having C linkage, so C++
expects something that is called 'GetVersion' and returns std::string
- so everything is ok.

Of course it won't work if GetVersion is overloaded:

^
[***@facs-bugtracker ~]$ cat y.cc
#include <string>

extern "C" {
std::string GetVersion() { return "1"; }
int GetVersion(bool as_int) { return 1; }
}


[***@facs-bugtracker ~]$ g++ y.cc -c
y.cc: In function ‘int GetVersion(bool)’:
y.cc:5:28: error: declaration of C function ‘int GetVersion(bool)’
conflicts with
int GetVersion(bool as_int) { return 1; }
^
y.cc:4:14: error: previous declaration ‘std::string GetVersion()’ here
std::string GetVersion() { return "1"; }

Please forgive me the most idiotic overload ever.
--
Jędrzej Dudkiewicz

I really hate this damn machine, I wish that they would sell it.
It never does just what I want, but only what I tell it.
Nick
2014-07-26 01:15:50 UTC
Permalink
Post by Jędrzej Dudkiewicz
#include <string>
extern "C" {
std::string GetVersion() { return "1"; }
}
y.o: file format elf64-x86-64
[....]
0000000000000000 g F .text 0000000000000061 GetVersion
[....]
Name GetVersion is not mangled, so it's a bare name. Note, that if you
want to use it elsewhere, you'd have to declare it somehow. So if
GetVersion returns std::string, your header would wound up with
"#include <string>" and it won't be understood by C compiler, or you
could declare it as something else, but then you'd be lying to the
compiler, and then you're on your own. But if it's included in C++
sources, it will be ok, since function would be declared with proper
return value and it will be annotated as having C linkage, so C++
expects something that is called 'GetVersion' and returns std::string
- so everything is ok.
^
#include <string>
extern "C" {
std::string GetVersion() { return "1"; }
int GetVersion(bool as_int) { return 1; }
}
y.cc:5:28: error: declaration of C function ‘int GetVersion(bool)’
conflicts with
int GetVersion(bool as_int) { return 1; }
^
y.cc:4:14: error: previous declaration ‘std::string GetVersion()’ here
std::string GetVersion() { return "1"; }
Makes sense. Thanks for laying it out.

Loading...