C++ specific types in 'extern "C"'

R

Robert Fendt

Hi,

I just came across something very odd. In an old project, there were
declarations of the scheme

extern "C" void foo(int&);

thus using C++ reference types in a C-linkage declaration. In the C++
implementation used (VC++), the binary code produced is apparently
identical to foo(int*), a fact that is actually used in the project I
mentioned, i.e. a second header file(!) exists declaring the pointer
version:

#ifdef __cplusplus
extern "C" {
#endif
void foo(int*);
#ifdef __cplusplus
}
#endif

I am very surprised that the compiler even allows a reference type in
an 'extern "C"' block. Apparently the standard does not disallow C++-
specific types in C-linkage declarations (at least I did not find
anything), but: how much sense does this make? Is a reference type in
the argument list guaranteed to 'collapse' to a pointer, or is this
just a happy coincidence. I cannot help but feel that this is quite an
ugly example of perverting knowledge of implementation details...

Regards,
Robert
 
G

gwowen

Is a reference type in the argument list guaranteed to 'collapse' to a pointer, or is this
just a happy coincidence.

No guarantee at all, though I'd imagine its quite common. It reminds
me of assuming the "called with hidden argument" convention for member
functions.
I cannot help but feel that this is quite an
ugly example of perverting knowledge of implementation details...

I agree. I can see one legitimate use -- extern "C" is helpful to
prevent name mangling. You could have a C++-only dynamic library with
functions-with-reference-parameters, and use extern "C" to make it
easier to find and load these functions by name, using dlsym()/
GetProcAddress() or the like.
 
V

Victor Bazarov

I just came across something very odd. In an old project, there were
declarations of the scheme

extern "C" void foo(int&);

thus using C++ reference types in a C-linkage declaration. In the C++
implementation used (VC++), the binary code produced is apparently
identical to foo(int*), a fact that is actually used in the project I
mentioned, i.e. a second header file(!) exists declaring the pointer
version:

#ifdef __cplusplus
extern "C" {
#endif
void foo(int*);
#ifdef __cplusplus
}
#endif

I am very surprised that the compiler even allows a reference type in
an 'extern "C"' block. Apparently the standard does not disallow C++-
specific types in C-linkage declarations (at least I did not find
anything), but: how much sense does this make? Is a reference type in
the argument list guaranteed to 'collapse' to a pointer, or is this
just a happy coincidence. I cannot help but feel that this is quite an
ugly example of perverting knowledge of implementation details...

A couple of days ago we discussed a similar issue (was it your post?)
but in that case g++ (GNU C++ compiler) actually provided the
implementation where passing by a reference was the same as passing by
value (or something like that). Tring to marry those two cases, the
only conclusion I can come to is that allowing references with 'extern
"C"' _linkage_ specification is by no means "perverting knowledge of
implementation details". Especially considering that using any other
type on the receiving end (pointer in your case, value in the other
case) has undefined behavior.

I think that for simplicity's sake the restrictions weren't imposed
simply because the designers of the language decided to give it to the
user/programmer to decide whether they want to risk UB to gain some kind
of functionality. By the same token, it's not reasonable to require the
behavior to be implementation-defined because then all implementations
would be required to provide some mechanism. It's simpler to allow the
compiler to warn the user, and there might be some that do (I just don't
know of any).

V
 
M

Michael Tsang

-----BEGIN PGP SIGNED MESSAGE-----
Hash: SHA1

Robert said:
I am very surprised that the compiler even allows a reference type in
an 'extern "C"' block. Apparently the standard does not disallow C++-
specific types in C-linkage declarations (at least I did not find
anything), but: how much sense does this make? Is a reference type in
the argument list guaranteed to 'collapse' to a pointer, or is this
just a happy coincidence. I cannot help but feel that this is quite an
ugly example of perverting knowledge of implementation details...

Normally, passing a reference is implemented by passing a pointer (it is
implementation-defined). Also, the behaviour of C-linkage is implementation-
defined.
-----BEGIN PGP SIGNATURE-----
Version: GnuPG v1.4.10 (GNU/Linux)

iEYEARECAAYFAkz48bsACgkQZ1Turg5KUCn5gQCZAZlW9A07iQ2E0x1PDr3b01Ue
6AcAoIYH/XmzO86mqURBUNytEqq5IdDl
=yJ0G
-----END PGP SIGNATURE-----
 
R

Robert Fendt

A couple of days ago we discussed a similar issue (was it your post?)
but in that case g++ (GNU C++ compiler) actually provided the
implementation where passing by a reference was the same as passing by
value (or something like that).  Tring to marry those two cases, the
only conclusion I can come to is that allowing references with 'extern
"C"' _linkage_ specification is by no means "perverting knowledge of
implementation details".

I did not mean that. I had difficulty coming up with an example where
this might actually be useful. gwowen did, however: dynamic loading of
functions via dlsym and the like.
 Especially considering that using any other
type on the receiving end (pointer in your case, value in the other
case) has undefined behavior.

This is what I meant by 'perverting' knowledge of the implementation's
behaviour. What happens when linking a void foo(int&) and a void
foo(int*) together is at least implementation-defined (if the
manufacturer even bothers defining it), or as you say, it should even
be considered UB. But as I said, I see it being used in production
code.

Regards,
Robert
 
V

Victor Bazarov

I did not mean that. I had difficulty coming up with an example where
this might actually be useful. gwowen did, however: dynamic loading of
functions via dlsym and the like.


This is what I meant by 'perverting' knowledge of the implementation's
behaviour. What happens when linking a void foo(int&) and a void
foo(int*) together is at least implementation-defined (if the
manufacturer even bothers defining it), or as you say, it should even
be considered UB. But as I said, I see it being used in production
code.

Well, if you reread your last paragraph, you will see why I objected.
You start by expressing the surprise that "compiler even allows" and
finish with "perverting knowledge of...". It sounded like you blamed
the language (the Standard) for that act of "perverting". I simply
don't agree.

Also, in my book "being used in production code" never means "correct"
or "as intended by the designers of the language". The design of the
language, while practical, in some cases assumes *ideal world*
situation. People don't live/work in an ideal world, not to mention
that probably not many people are "without sin" AFA language use and
cutting corners by exploiting implementation details are concerned. So,
don't make any conclusions about the language when examining "production
code". Deal? ;-)

V
 
V

Victor Bazarov

Normally, passing a reference is implemented by passing a pointer (it is
implementation-defined). Also, the behaviour of C-linkage is implementation-
defined.

Please be careful when using terms from the Standrad.
"Implementation-defined" requires documentation (see
[defns.impl.defined]). There is no requirement for the implementation
to document how they implement C-linkage or references. Use the
expression "up to the implementation" instead, or "unspecified
implementation details".

V
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

No members online now.

Forum statistics

Threads
473,769
Messages
2,569,581
Members
45,055
Latest member
SlimSparkKetoACVReview

Latest Threads

Top