1

Functions in extern "C" are interpreted in C manners, e.g. no name mangling. However, why do C++ features, such as STL, std::string, smart pointer and so on, can be used in the function definition but cannot be used in the function declaration (to link with other C code)?

For example, I want to use std::vector in extern "C". As far as I know, if it's used in the implementation, the .obj file will have such instructions to jump to the ctor, etc; if it's used in the prototypes, it should do so too, and there seems to be no difference between them.

Besides, there is a related question for currently I'm studying ABI. What's the relationship between so-called ABI and language linkage in C++? How does ABI influence linking with extern "C" C++ code in C?

o_oTurtle
  • 1,091
  • 3
  • 12
  • *Functions in extern "C" are interpreted in C manners, e.g. name mangling.* Huh? Functions in C and `extern "C"` functions in C++ **do not** undergo name mangling. – Andrew Henle Aug 17 '22 at 11:41
  • @AndrewHenle I've changed my wording. Sorry for the ambiguity in my poor English :(. – o_oTurtle Aug 17 '22 at 11:47

1 Answers1

3

Why are C++ features unable to be used in extern "C" prototypes but able to be used in the implementation to link in C?

Because the C compiler doesn't understand C++ specific code.

The implementation of the extern "C" function that uses C++ classes is compiled with a C++ compiler.

The C compiler only sees the function declaration, which can not contain C++ specific things, like std::vectors.

header.h - seen by both C and C++ compilers

#pragma once

#ifdef __cplusplus
extern "C" {
#endif

int foo(const char*);          // works in both C and C++
// int bar(std::vector<int>&); // a C compiler can't compile this

#ifdef __cplusplus
}
#endif

impl.cpp - never seen by the C compiler, only the C++ compiler

#include "header.h"

int foo(const char* something) {
    std::vector<int> bar;
    // use bar and do stuff
    return result;
}
Ted Lyngmo
  • 93,841
  • 5
  • 60
  • 108
  • I mean, the function prototypes have been changed into symbols and there is no real `std::vector` in the .obj file. What it implies is just operations on registers and stack, etc., to convey parameters. So when a C compiler tries to link that .obj file with C .obj files, how can it know "I don't recognize `std::vector`"? – o_oTurtle Aug 17 '22 at 11:54
  • @o_oTurtle _"have been changed into symbols and there is no real std::vector in the .obj file"_ - If you put `int bar(std::vector&);` in the header file, the C compiler reading the header file needs to be able to understand it - which it doesn't. If you only use the header file from C++ code - fine, but then, why `extern "C"` link it? You can't use it from C code anyway. – Ted Lyngmo Aug 17 '22 at 11:57
  • 1
    @o_oTurtle - The C compiler never sees the .obj file with C++ code. That is the job of the linker. And the C++ compiler produces code that the linker can handle. – BoP Aug 17 '22 at 11:58
  • 1
    @TedLyngmo That makes sense, I get it. – o_oTurtle Aug 17 '22 at 12:00
  • If you want to pass `vector` as argument then you have to pass that as `void*`. And you need some C++ function that creates the `vector` and passes it as `void*` to the C code because C code can't create it's own `vector`s. – Goswin von Brederlow Aug 17 '22 at 13:41
  • @GoswinvonBrederlow Indeed. I often hide C++ types in `typedef struct { void* data; } sometype_t;` -like structs and use them in the C API. When such a struct eventually reaches a function compiled with a C++ compiler, the pointer is cast back to the proper type and dereferenced. – Ted Lyngmo Aug 17 '22 at 13:44
  • @TedLyngmo Dart FFI allows for C++-isms in the declarations. You're supposed to compile a shared library that Dart can then interface with, so the only reason they bother to `extern "C"` stuff is just to not mangle names. – sweenish Aug 17 '22 at 13:53
  • 1
    @TedLyngmo Good suggestion hiding the `void*` in a struct so you still have type safety. Alternatively you can keep the type abstract, the C code doesn't need to know that `int_vec_t` is a `std::vector*`. – Goswin von Brederlow Aug 17 '22 at 13:53
  • @sweenish Yes, if one protects the C compiler from C++-isms by processing the file with another tool/preprocessor/compiler, anything is possible. – Ted Lyngmo Aug 17 '22 at 14:01