8

From Here it seems that default argument are not supported by C.

I have the following method in exported library:

extern "C" 
{
    __declspec (dllexport) uintptr_t Method(int freq, int *pRetval, bool *support2MHz);
}

If i made last argument optional like this:

extern "C" 
{
    __declspec (dllexport) uintptr_t Method(int freq, int *pRetval, bool *support2MHz = NULL);
}

My dll is still compiled. My question is why? Everyone says the default arguments are not supported in C code.

I use C++ for MS 2015.

gflegar
  • 1,583
  • 6
  • 22
Erik Šťastný
  • 1,487
  • 1
  • 15
  • 41
  • 2
    i think it works because default argument are handle by the caller – Tyker May 28 '18 at 12:16
  • 4
    `extern "C"` does not mean "this is C code", it only affects the form of the exported symbols (i.e. "Method"). If you feed that to a C compiler, it will complain about both the `extern "C"` and the default argument. – molbdnilo May 28 '18 at 12:23
  • 2
    As far as I know, when you try to use this export, you will still need to provide 3 parameters. this is c++, that's why you can provide default argument here, but when you try to use exported C function, you need to provide all 3. – Afshin May 28 '18 at 12:27
  • @Afshin "use exported C function", you mean use the exported in C code or C++ code? – John Oct 26 '21 at 02:57
  • @John I mean when you want to use exported C++ code in a C code. – Afshin Oct 26 '21 at 05:30

1 Answers1

8

As molbdnilo already noted in the comments, extern "C" does not mean "this is C code", but "this is code with C linkage" - i.e. name mangling for this function will not be performed, so you will be able to call it from C with the "expected" function call syntax. C++ compilers mangle the names of functions so they can support function overloading, since symbol names of different overloads of the same function have to be unique (basically, they use function's scope, name and argument types to create a unique symbol name).

According to [dcl.fct.default], paragraphs 1 and 2 of the standard:

If an initializer-clause is specified in a parameter-declaration this initializer-clause is used as a default argument. Default arguments will be used in calls where trailing arguments are missing.

[Example: The declaration

void point(int = 3, int = 4);

declares a function that can be called with zero, one, or two arguments of type int. It can be called in any of these ways:

point(1,2); point(1); point();

The last two calls are equivalent to point(1,4) and point(3,4), respectively. — end example ]

In addition, paragraph 9 specifies:

A default argument is not part of the type of a function. [ Example:

int f(int = 0);

void h() {
    int j = f(1);
    int k = f();                      // OK, means f(0)
}

int (*p1)(int) = &f;
int (*p2)() = &f;                   // error: type mismatch

— end example ]

Thus, for a function with a default argument int foo(int x, int y = 0), the compiler will not generate two overloads (int foo(int x, int y) and int foo(int x), with y fixed to 0), but instead replace every call of the form foo(x) with a call to foo(x, 0). In your concrete example with extern "C" this means that the compiler generates only one symbol with C linkage for Method so there is no name clash.

You can see this behavior for various compilers in a live example on godbolt.

However, as Afshin already mentioned in the comments, the way this is implemented makes it impossible to "propagate" default arguments to other users of your shared library, so if you want to use this from C, you'll still need to pass all arguments to the function.

gflegar
  • 1,583
  • 6
  • 22