Can extern "C" functions throw exceptions

70 views
Skip to first unread message

Bonita Montero

unread,
Sep 11, 2021, 6:35:56 AMSep 11
to
Is it specified that extern "C" functions never throw exceptions.
If it is I see a problem with this: if I pass a function-pointer
to a C++-function an extern "C" function and this function is
called through its pointer by the extern "C" function it might
throw an exception.

Bo Persson

unread,
Sep 11, 2021, 7:49:52 AMSep 11
to
On 2021-09-11 at 12:35, Bonita Montero wrote:
> Is it specified that extern "C" functions never throw exceptions.

No.

> If it is I see a problem with this: if I pass a function-pointer
> to a C++-function an extern "C" function and this function is
> called through its pointer by the extern "C" function it might
> throw an exception.

Can you really pass an extern "C++" function pointer to an extern "C"
function?

Also, if the C function is compiled with a C compiler, it probably
doesn't know how to handle an exception.

Alf P. Steinbach

unread,
Sep 11, 2021, 7:55:59 AMSep 11
to
On 11 Sep 2021 12:35, Bonita Montero wrote:
> Is it specified that extern "C" functions never throw exceptions.

No, I don't think so, but that's a common and reasonable assumption.


> If it is I see a problem with this: if I pass a function-pointer
> to a C++-function an extern "C" function and this function is
> called through its pointer by the extern "C" function it might
> throw an exception.

Yes, that can for example happen in Windows desktop programming in a
WM_PAINT handler.

If one lets the exception just propagate normally up to the C++ code
above, then every C function it logically passes through (in reality it
just jumps all the way back up directly) may leak some resources.

One way to deal with it is to use `std::current_exception` to obtain an
exception pointer, store that in static storage somewhere, return
normally up through the C call chain, and re-throw in the higher level
C++ code that passed the C callback to be called.

I must have written such exception propagation support at least five
times. One gets wary of it. Why on Earth does the standard not supply
the mechanism, only the building blocks to make it?

And ditto for retrieving the exception messages of nested exceptions:
why on Earth should every individual programmer have to re-invent that
wheel in every app?


- Alf

Alf P. Steinbach

unread,
Sep 11, 2021, 7:57:33 AMSep 11
to
On 11 Sep 2021 13:49, Bo Persson wrote:
> On 2021-09-11 at 12:35, Bonita Montero wrote:
>> Is it specified that extern "C" functions never throw exceptions.
>
> No.
>
>> If it is I see a problem with this: if I pass a function-pointer
>> to a C++-function an extern "C" function and this function is
>> called through its pointer by the extern "C" function it might
>> throw an exception.
>
> Can you really pass an extern "C++" function pointer to an extern "C"
> function?

Yes, in practice, but not formally.

As I recall from earlier discussions only the/a Solaris compiler
warns/warned by default.

Happily that warning could be turned off.


> Also, if the C function is compiled with a C compiler, it probably
> doesn't know how to handle an exception.

Yes.


- Alf

Paavo Helde

unread,
Sep 11, 2021, 8:12:29 AMSep 11
to
11.09.2021 13:35 Bonita Montero kirjutas:
> Is it specified that extern "C" functions never throw exceptions.

If the extern "C" function is itself compiled as C++, then you can
freely throw exceptions inside it and in any C++ functions called from
it, provided the exceptions are caught before propagating out of the
extern "C" function. The latter is undefined behavior by the C++
standard. Some implementations define the behavior though, see the /EHa
and /EHs options of MSVC or -fexceptions option for gcc.

So, for portability it is best to put a try-catch block around any
extern "C" function contents, and to convert all exceptions into an
error code return.

> If it is I see a problem with this: if I pass a function-pointer
> to a C++-function an extern "C" function and this function is
> called through its pointer by the extern "C" function it might
> throw an exception.

If you pass a pointer to a C++ function A to an extern "C" function B
which is itself compiled as C++, then throwing an exception from A is
not a problem if it is properly caught in B. Propagating the exception
out of B is the problematic point.

Paavo Helde

unread,
Sep 11, 2021, 8:20:10 AMSep 11
to
11.09.2021 14:49 Bo Persson kirjutas:
> On 2021-09-11 at 12:35, Bonita Montero wrote:
>> Is it specified that extern "C" functions never throw exceptions.
>
> No.
>
>> If it is I see a problem with this: if I pass a function-pointer
>> to a C++-function an extern "C" function and this function is
>> called through its pointer by the extern "C" function it might
>> throw an exception.
>
> Can you really pass an extern "C++" function pointer to an extern "C"
> function?

Sure, an extern "C" function in C++ can have any C++ types as
parameters. It would be just become impossible to call it from C, but
you can still can call it from C++ code.

One might want to do this in order to have a non-mangled function name
to be looked up by dladdr()/GetProcAddress().

Bonita Montero

unread,
Sep 11, 2021, 9:32:20 AMSep 11
to
Am 11.09.2021 um 13:49 schrieb Bo Persson:
> On 2021-09-11 at 12:35, Bonita Montero wrote:
>> Is it specified that extern "C" functions never throw exceptions.
>
> No.
>
>> If it is I see a problem with this: if I pass a function-pointer
>> to a C++-function an extern "C" function and this function is
>> called through its pointer by the extern "C" function it might
>> throw an exception.
>
> Can you really pass an extern "C++" function pointer to an extern "C"
> function?

Of course you can:

#include <iostream>

using namespace std;


#if defined(_MSC_VER)
#define NOINLINE __declspec(noinline)
#elif defined(__GNUC__) || defined(__clang__)
#define NOINLINE __attribute((noinline))
#endif

extern "C"
NOINLINE
void f( void (*fn)() )
{
fn();
}

NOINLINE
void thrower()
{
throw "hello world";
}

int main()
{
try
{
f( thrower );
}
catch( char const *str )
{
cout << str << endl;
}
}

And MSVC, clang and gcc generate unwind-handlers for "void f()",
i.e. f() isn't implicitly noexcept, but I don't know whether this
is mandated by the standard.

Bonita Montero

unread,
Sep 11, 2021, 9:34:45 AMSep 11
to
Am 11.09.2021 um 13:55 schrieb Alf P. Steinbach:
> On 11 Sep 2021 12:35, Bonita Montero wrote:
>> Is it specified that extern "C" functions never throw exceptions.
>
> No, I don't think so, but that's a common and reasonable assumption.
>
>
>> If it is I see a problem with this: if I pass a function-pointer
>> to a C++-function an extern "C" function and this function is
>> called through its pointer by the extern "C" function it might
>> throw an exception.
>
> Yes, that can for example happen in Windows desktop programming in a
> WM_PAINT handler.

I'm pretty sure the WndProc handling WM_PAINT can't throw an exception
because the Windows-APIs calling it don't have any unwind-handlers, i.e.
linked unrolling with 32 bit code or table-driven unrolled handlers for
32 bit code.

Bonita Montero

unread,
Sep 11, 2021, 9:58:31 AMSep 11
to
^^ 64

Alf P. Steinbach

unread,
Sep 11, 2021, 11:00:42 AMSep 11
to
You're right that the Windows code calling it lacks unwind-handlers for
C++ exceptions.

The part you snipped discussed one way to deal with that in C++11 and
later, because it's the general problem with exceptions in C callbacks
provided by C++ code.

Note that the WM_PAINT handler is generally C++ app code, and it can
throw exceptions by design and/or inadvertently. I prefer to deal with
that. I do know from discussions with some Microsoft programmers (both
when the Microsoft Usenet servers were still going, via mailing list
when I was an MSVP in Visual C++ in 2012/13, and later at Reddit and
elsewhere) that they don't quite understand the need to deal with
exceptions or how, and that they generally don't understand the utility
of exceptions in such code, in particular for DirectX where the most
vocal of them erroneously believe that exceptions can't be used, and
that they absolutely have to be right about that because everyone they
know believe the same... But then, these are they guys writing `void
main` and so on. And these are the guys creating a Windows calculator
that by default evaluates 2+3*4 as (2+3)*4. And so on. And on. :-o

- Alf

Horsey...@the_stables.com

unread,
Sep 11, 2021, 11:54:36 AMSep 11
to
Would it need to? I guess it depends ultimately on how exceptions are actually
converted into assembler by the given compiler and whether during compile time
its smart enough to set the exception jumps to skip C functions on the stack
entirely.

Horsey...@the_stables.com

unread,
Sep 11, 2021, 11:57:10 AMSep 11
to
On Sat, 11 Sep 2021 13:55:43 +0200
"Alf P. Steinbach" <alf.p.s...@gmail.com> wrote:
>And ditto for retrieving the exception messages of nested exceptions:
>why on Earth should every individual programmer have to re-invent that
>wheel in every app?

If so many programmers didn't use exceptions as fancy gotos they probably
wouldn't have to much if ever. An exception should be for an exceptional
situation, the clue is in the name, not simply a convenient way to avoid doing
manual returns up the call stack.


David Brown

unread,
Sep 11, 2021, 2:10:39 PMSep 11
to
On 11/09/2021 13:55, Alf P. Steinbach wrote:
> On 11 Sep 2021 12:35, Bonita Montero wrote:
>> Is it specified that extern "C" functions never throw exceptions.
>
> No, I don't think so, but that's a common and reasonable assumption.
>

extern "C" marks the function as having C naming conventions and C
calling conventions (which may, in theory, differ from C++ conventions -
though I have never heard of that in practice). But there is nothing to
stop a C++ function being marked extern "C" - I have done that in my own
code, in connection with overriding weak alias functions defined in C or
when I needed a non-mangled name for the function.

If we are talking about external C functions - functions compiled in a C
unit by a C compiler - then it is reasonable to assume that the function
itself will not throw any exceptions. You'd need C compiler extensions
to support that. However, the C function could call C++ functions that
in turn throw exceptions - the external C function would then be passing
on exceptions, even though it did not throw any itself.


Juha Nieminen

unread,
Sep 12, 2021, 5:20:39 AMSep 12
to
David Brown <david...@hesbynett.no> wrote:
> extern "C" marks the function as having C naming conventions and C
> calling conventions

Indeed, extern "C" doesn't mean that the code being called using
those functions has actually been compiled as C. It merely changes
the naming of the functions in the object files to be compatible
with how functions are named in C.

But that makes me wonder: If an extern "C" declared function has
C++ types as parameters (or return value type), how are those
encoded in the name? Or are they at all? Can you overload extern "C"
functions?

Paavo Helde

unread,
Sep 12, 2021, 5:34:59 AMSep 12
to
They aren't.

> Can you overload extern "C"
> functions?

No.

David Brown

unread,
Sep 12, 2021, 6:24:51 AMSep 12
to
On 12/09/2021 11:20, Juha Nieminen wrote:
> David Brown <david...@hesbynett.no> wrote:
>> extern "C" marks the function as having C naming conventions and C
>> calling conventions
>
> Indeed, extern "C" doesn't mean that the code being called using
> those functions has actually been compiled as C. It merely changes
> the naming of the functions in the object files to be compatible
> with how functions are named in C.
>
> But that makes me wonder: If an extern "C" declared function has
> C++ types as parameters (or return value type), how are those
> encoded in the name?

They are not.

> Or are they at all? Can you overload extern "C"
> functions?
>

No.

Although in theory a compiler can support different ABI's or calling
conventions for C and C++ functions, in practice making a function
extern "C" simply disables all name mangling for the function. That in
turn means you can't overload it, or have it in a namespace.

Bonita Montero

unread,
Sep 12, 2021, 6:30:08 AMSep 12
to
Am 12.09.2021 um 12:24 schrieb David Brown:

> Although in theory a compiler can support different ABI's or calling
> conventions for C and C++ functions, in practice making a function
> extern "C" simply disables all name mangling for the function. That
> in turn means you can't overload it, or have it in a namespace.

You can actually define it in a namespace, but it won't belong to it.

Chris Vine

unread,
Sep 12, 2021, 9:44:05 AMSep 12
to
On Sun, 12 Sep 2021 09:20:23 -0000 (UTC)
Juha Nieminen <nos...@thanks.invalid> wrote:
> David Brown <david...@hesbynett.no> wrote:
> > extern "C" marks the function as having C naming conventions and C
> > calling conventions
>
> Indeed, extern "C" doesn't mean that the code being called using
> those functions has actually been compiled as C. It merely changes
> the naming of the functions in the object files to be compatible
> with how functions are named in C.

For functions, it does more than affecting naming. The language linkage
of a function determines two things: the function's name (name mangling)
and the function's type (calling convention). For example a function
pointer has language linkage for its function type even though name
mangling is irrelevant to it (save as mentioned further below with
respect to variable names). gcc allows you to pass a function pointer
with C++ language linkage to a C function expecting a C function
pointer, but strictly speaking it is undefined behaviour. I believe
some of the intel compilers did indeed have a different calling
convention as respects use of registers as between C and C++ functions.

> But that makes me wonder: If an extern "C" declared function has
> C++ types as parameters (or return value type), how are those
> encoded in the name? Or are they at all? Can you overload extern "C"
> functions?

In C++ variables also have a language linkage ("All function types,
function names with external linkage, and variable names with external
linkage have a language linkage"). But I know of no compiler which
actually does distinguish between the language linkage of C and C++
variable names. Function pointers which are lvalues have a language
linkage with respect to the variable name (if any) of the pointer and as
regards the type (calling convention) of the pointed-to function. As
mentioned, the language linkage of the variable name is in practice
immaterial.

Functions with C language linkage cannot be overridden because name
mangling is suppressed by C name mangling (the prepending of an
underscore). As regards argument types, of necessity the only function
with C language linkage which could take, say, a std::string argument
is a C++ function declared to have C language linkage.

James Kuyper

unread,
Sep 12, 2021, 11:14:02 PMSep 12
to
On 9/12/21 9:44 AM, Chris Vine wrote:
> On Sun, 12 Sep 2021 09:20:23 -0000 (UTC)
> Juha Nieminen <nos...@thanks.invalid> wrote:
...
>> Indeed, extern "C" doesn't mean that the code being called using
>> those functions has actually been compiled as C. It merely changes
>> the naming of the functions in the object files to be compatible
>> with how functions are named in C.
>
> For functions, it does more than affecting naming. The language linkage
> of a function determines two things: the function's name (name mangling)
> and the function's type (calling convention).

Furthermore, and not widely appreciated, those two aspects are separable
by using a typedef for a function's type. The language linkage of the
typedef determines the calling convention, while the language linkage of
the function itself determines the name mangling.

Juha Nieminen

unread,
Sep 13, 2021, 1:34:34 AMSep 13
to
Chris Vine <chris@cvine--nospam--.freeserve.co.uk> wrote:
> gcc allows you to pass a function pointer
> with C++ language linkage to a C function expecting a C function
> pointer, but strictly speaking it is undefined behaviour.

Does that mean that if you are using a C library and you give it a
function pointer (eg. a callback function), you ought to make that
function extern "C"?

How about std::qsort()? Does its comparator function pointer need to be
declared extern "C"? (Or does the standard require std::qsort() to be
usable with C++ function pointers directly?)

Paavo Helde

unread,
Sep 13, 2021, 2:55:22 AMSep 13
to
13.09.2021 08:34 Juha Nieminen kirjutas:
> Chris Vine <chris@cvine--nospam--.freeserve.co.uk> wrote:
>> gcc allows you to pass a function pointer
>> with C++ language linkage to a C function expecting a C function
>> pointer, but strictly speaking it is undefined behaviour.
>
> Does that mean that if you are using a C library and you give it a
> function pointer (eg. a callback function), you ought to make that
> function extern "C"?

Strictly speaking, yes, otherwise the calling conventions might be mixed
up. But meanstream C++ implementations are using the same calling
conventions for C and C++ code, so they don't mind.

>
> How about std::qsort()? Does its comparator function pointer need to be
> declared extern "C"? (Or does the standard require std::qsort() to be
> usable with C++ function pointers directly?)

Indeed, the C++ standard requires qsort to accept both C and C++
callbacks. This is a copy-paste from N4842:

void qsort(void* base, size_t nmemb, size_t size, c-compare-pred * compar);
void qsort(void* base, size_t nmemb, size_t size, compare-pred * compar);



David Brown

unread,
Sep 13, 2021, 3:30:43 AMSep 13
to
That is not something I knew.

It does not affect my own coding, as I know the ABI's are the same for C
and C++ and everything is handled by the same compiler. But it might be
relevant in odd cases.

One thing that might be relevant in the future is some of the ideas
being considered for changing exceptions into a simpler and more
deterministic system, with far lower overheads, clearer code and better
checking by making them more static and less run-time. Some of the
papers around this have suggested using additional registers or
processor flags as cheap ways to return exception information to the
caller - and that might mean a change to the C++ ABI compared to the C
ABI on the same platform, even for otherwise simple function parameters.

red floyd

unread,
Sep 13, 2021, 4:21:49 PMSep 13
to
I can see how the following case might throw....

Disclaimer: I have no idea what the Standard says about it.


class C
{
public:
static void i_throw()
{
throw 1;
}
};

extern "C" throwing_c_func()
{
C::i_throw();
}

Tim Rentsch

unread,
Oct 6, 2021, 4:12:52 PMOct 6
to
Can you illustrate how that would be done? Since the language
linkage of a function is part of its type, it sounds like trying
to do such a thing would inevitably lead to undefined behavior.

daniel...@gmail.com

unread,
Oct 6, 2021, 4:37:03 PMOct 6
to
On Saturday, September 11, 2021 at 11:00:42 AM UTC-4, Alf P. Steinbach wrote:

> And these are the guys creating a Windows calculator
> that by default evaluates 2+3*4 as (2+3)*4.

In Standard mode, but not in Scientific or Programmer mode :-)

Branimir Maksimovic

unread,
Oct 6, 2021, 4:42:18 PMOct 6
to
--main = print $ calculate "3 * 2 + 5 / 2"

calculate :: String -> String
calculate str = case (eval operatorRegister . words) str of
Just r -> printf "%.2f" (fromRational r::Double)
Nothing -> "Nothing"

eval :: Register -> [String] -> Maybe Rational
eval [] _ = Nothing -- No operator found.
eval _ [] = Nothing -- If a operator don't have anything to operate on.
eval _ [number] = let a :: Maybe Double = readMaybe number
in case a of
Just a -> Just (toRational a)
Nothing -> Nothing
eval ((operator, function):rest) unparsed =
case span (/=operator) unparsed of
(_, []) -> eval rest unparsed
(beforeOperator, afterOperator) ->
function
<$> (eval operatorRegister beforeOperator)
<*> (eval operatorRegister $ drop 1 afterOperator)


--

7-77-777
Evil Sinner!
to weak you should be meek, and you should brainfuck stronger
https://github.com/rofl0r/chaos-pp

Keith Thompson

unread,
Oct 6, 2021, 7:13:06 PMOct 6
to
I don't know what language that is, and you haven't bothered to tell us.
(No, I'm not asking, just saying that whatever point your post might
have is defeated by not knowing what language you're using.)

Again, you've posted something that isn't relevant either to the subject
of this newsgroup (the C++ language) or to the thread (which is about
extern "C" functions throwing exceptions, with a passing reference to
the Windows calculator program).

This kind of thing is a problem. I can solve it for myself by
configuring my newsreader to stop showing me your posts. You can solve
it for everyone by not making off-topic posts. (The fact that you post
obscure things without any explanation is just icing on the unpleasant
cake.) Please don't just post whatever you think is interesting.
Consider whether it's appropriate.

--
Keith Thompson (The_Other_Keith) Keith.S.T...@gmail.com
Working, but not speaking, for Philips
void Void(void) { Void(); } /* The recursive call of the void */

Branimir Maksimovic

unread,
Oct 6, 2021, 7:23:05 PMOct 6
to
Petty, one of the favorite Hacker languages :P
(Haskell) , besides /forth and ASSE/mbler :P

> Again, you've posted something that isn't relevant either to the subject
> of this newsgroup (the C++ language) or to the thread (which is about
> extern "C" functions throwing exceptions, with a passing reference to
> the Windows calculator program).
>

Who cares, this is not *mderated* group, you just embarrased yourself
not recognizing simple calculator in Haskell :P

> Consider whether it's appropriate.
>
It is talk was about calculator :p

Keith Thompson

unread,
Oct 6, 2021, 8:04:00 PMOct 6
to
Branimir Maksimovic <branimir....@icloud.com> writes:
> On 2021-10-06, Keith Thompson <Keith.S.T...@gmail.com> wrote:
[...]
>> Again, you've posted something that isn't relevant either to the subject
>> of this newsgroup (the C++ language) or to the thread (which is about
>> extern "C" functions throwing exceptions, with a passing reference to
>> the Windows calculator program).
>
> Who cares, this is not *mderated* group, you just embarrased yourself
> not recognizing simple calculator in Haskell :P

I care, and most readers here do as well.

>> Consider whether it's appropriate.
>>
> It is talk was about calculator :p

*PLONK*

james...@alumni.caltech.edu

unread,
Oct 7, 2021, 10:09:18 AMOct 7
to
On Wednesday, October 6, 2021 at 4:12:52 PM UTC-4, Tim Rentsch wrote:
> James Kuyper <james...@alumni.caltech.edu> writes:
>
> > On 9/12/21 9:44 AM, Chris Vine wrote:
...
> >> For functions, it does more than affecting naming. The language
> >> linkage of a function determines two things: the function's name
> >> (name mangling) and the function's type (calling convention).
> >
> > Furthermore, and not widely appreciated, those two aspects are
> > separable by using a typedef for a function's type. The language
> > linkage of the typedef determines the calling convention, while the
> > language linkage of the function itself determines the name
> > mangling.
> Can you illustrate how that would be done? Since the language
> linkage of a function is part of its type, it sounds like trying
> to do such a thing would inevitably lead to undefined behavior.

The language linkage of a function's type is indeed part of that type. The
language linkage of a function's name is not. The standard's description of
language linkage in 9.11p1 starts with the sentence "All function types,
function names with external linkage, and variable names with external
linkage have a language linkage.", clearly making the point that the
language linkage of a function's type is a distinct thing from the language
linkage of a function's name. The standard could have inextricably linked
them together - but it does not. In fact, 9.11p5 includes an example
demonstrating how they can be separated:

extern "C" typedef void FUNC();
FUNC f2; // the name f2 has C ++ language linkage and the
// function’s type has C language linkage

Now, the standard can and does contain example code that violates a C++
rule, as a way of explaining what that rule means. However, when it does so,
it almost always includes a comment next to the offending line pointing out
that it violates a rule. There's no such comment in this example. In fact, the
only comments explicitly describe the well-defined meaning of that code.

All quotes are from n4860.pdf, the latest draft version of the C++ language
that I have access to. However, this is not a new feature of the language; it
has been part of standard C++ for as long as C++ has been standardized.
I'm not sure about pre-standard C++ - my copy of Stroustrup's book went
missing many years ago.

The C++ language linkage of f2's name means that it can be referred to by
name in C++ code, but not in C code. The C language linkage of f2's type
means that f2 can called from C as well as from C++. However, since the
function's name can't be referred to by C code, it must be called using a
pointer, and that pointer's value can only come from C++ code. It could be
passed to the C part of a program as a function parameter, or returned as
the value of a function call, or placed in a function pointer object shared
between the C and C++ parts of a program.

I don't see any reasonable use for the opposite: a function whose name has
C language linkage and whose type has C++ language linkage. C has no way
of correctly declaring the type of the function associated with that name,
and the function's name cannot be declared without specifying it's type.
Such a function would be callable by name from C++ but I don't see any
particular advantage of that.

I don't think that the ability to separate the language linkage of a function's
type from the language linkage of the function's name was a strongly desired
feature in itself. I suspect that it is simply a side-effect that arose naturally
from the fact that those are two different aspects of language linkage,
combined with the way typedefs work.
Reply all
Reply to author
Forward
0 new messages