External symbol link error when "compile as" set to 'default' [duplicate] - c++

This question already has answers here:
Is extern "C" required also for linking global variables used in Cpp file to the one defined in a cfile?
(2 answers)
Closed 7 years ago.
I am using Visual Studio 2013. I have a global variable declared in a C source file (file1.c) and used in a method defined in a C++ source file (file2.cpp). A header included from both files declares the variable as extern. The project property C\C++ -> Advanced -> compile as is set to defualt, which according to the documentation means the compiler uses the file extension to infer file type. This setup results in an unresolved external symbol linking error. If I set this option to either Compile as C code or Compile as C++ code, the project compiles and link without an error. I fail to understand this behavior. (Btw, under linux/GCC the code compiles OK).
Here's a minimal example reproducing the problem:
// file1.h
extern int g_i;
// file1.c
#include "file1.h"
#include "file2.h"
int g_i;
int main() {
g_i = 1;
foo();
return 0;
}
// file2.h
#ifdef __cplusplus
extern "C"
#endif
void foo();
// file2.cpp
#include "file1.h"
#include "file2.h"
void foo() {
int i = g_i;
}

Language linkage applies to variables as well as functions. In the C file you define a variable g_i which obviously will have C language linkage (from the point of view of a C++ compiler). In the CPP file (or at least the header file) you need to declare the variable as having C language linkage. So what you need is:
// file1.h
#ifdef __cplusplus
extern "C"
#endif
extern int g_i;
Alternatively, assuming that the real code has more than one variable in the header file, you may prefer:
// file1.h
#ifdef __cplusplus
extern "C" {
#endif
extern int g_i;
... // More declarations to taste
#ifdef __cplusplus
}
#endif
GCC obviously doesn't distinguish between C and C++ linkage for variables (which is perfectly allowable).
If you force everything to be compiled as C or if you force everything to be compiled as C++, then of course the definition of the variable and the usage of the variable are defaulting to the same language linkage - so it just works.
See http://en.cppreference.com/w/cpp/language/language_linkage for details. In particular
... every variable name with external linkage, has a property called
language linkage.

Related

how does extern "C" allow C++ code in a C file?

In order to use C++ code in a C file, I read that we can just do extern "C" { (where the c++ code goes here)}, but when I try printing something out using cout, I keep getting an error because it does not recognize the library . I think I am just confused on how extern "C" allows you to use C++ code in C.
The opposite is true. You can use extern C to add code you want to compile as C code using a C++ compiler.
Unless I'm missing something you can't compile C++ code with a C compiler.
The extern "C" construct is a C++-specific syntax, no C compiler will understand it.
That's why you will almost always see it paired with some conditional compilation like
#ifdef __cplusplus
extern "C" {
#endif
...
#ifdef __cplusplus
}
#endif
What extern "C" does is simply to inhibit name mangling meaning that symbols defined in a C++ source file can be used in a C program.
This allows you to have a project with mixed C and C++ sources, and the C source can call the C++ functions that have been defined as extern "C".
Very simple, and stupid, example just to show the point:
Lets say we have a C source file, compiled with a C compiler:
#include <stdio.h>
void foo(void); // Declare function prototype, so it can be called
int main(void)
{
printf("Calling foo...\n");
foo();
printf("Done\n");
return 0;
}
Then we have a C++ source file for the foo function:
#include <iostream>
extern "C" void foo()
{
std::cout << "Hello from foo\n";
}
The C source file is compiled with a C compiler, and the C++ source file is compiled with a C++ compiler. Then the two object files are linked together to form the executable program. Because foo was defined as extern "C" it's symbol name in the object file is not mangled, and the linker can resolve the reference from the object file created by the C compiler.
It works in the other direction as well. Because symbols in C are not mangled the C++ compiler needs to know that, and that is done by declaring the C symbols extern "C", usually in a header file using the conditional compilation as shown above. If extern "C" was not used, the C++ compiler would think that the symbols were C++ symbols, and mangle the names leading to linker problems when the linker can't find the mangled symbols.
Equally stupid example: First a C++ source file
#include <iostream>
extern "C" void bar(); // Declare function prototype as an unmangled symbol
int main()
{
std::cout << "Calling bar...\n";
bar();
}
Then the C source file
#include <stdio.h>
void bar(void)
{
printf("Inside bar\n");
}
extern "C" is a way of putting C code in C++ code. More specifically it tells the compiler to disable certain things like function overloading so that it can also turn off the name mangling. In C++ a simple function like:
int add(int a, int b) {
return a+b;
}
Will actually get some funky name in the library to denote the parameters so that if you define another function like:
double add(double a, double b) {
return a+b;
}
That it knows which one to call. You don't want that if you're trying to use a C library and that's what extern "C" is for. All of this being said, extern "C" does not allow C++ in a C program.
Exported C++ symbols, as generated my the compiler, are mangled to include additional type information about the symbol to the linker.
So the following overloaded functions would be distinguishable by the linker in C++, but not C:
int fn();
int fn(int);
int fn(char*);
int fn(char*) const;
int fn(const char*);
The extern "C" { ... } syntax allows symbols (or references to C symbols) to be defined in C++ code without using the mangling rules.
This allows such C++ code to be linked with C libraries.

Alias a function in a different source file

I've got a .h file and three .cpp files. All cpp files include the .h file.
I want to create a useless function in one of the cpp files and use alias in the other cpp files to refere to this useless function. But if i move the function from important.cpp to useless.cpp it does not want to compile anymore. For some reason it doesnt see the function, even if its declared in the header.
useless.h
#ifdef __cplusplus
extern "C" {
#endif
extern void useless(void);
#ifdef __cplusplus
}
#endif
useless.cpp
#ifdef __cplusplus
extern "C" {
#endif
void useless(void) { }
#ifdef __cplusplus
}
#endif
important.cpp
void important(void) __attribute__((alias("useless")));
error: 'void important()' aliased to undefined symbol 'useless'
void important(void) attribute((alias("useless")));
Why are you using a bunch of extern "C" stuff?
If it's a C++ project and a global C++ function, you really should claim it's a C function. Doing so can lead to undefined reference-type errors, as the symbol names are different for C and C++.
What you're trying to do is probably impossible. Take a look at gcc docs https://gcc.gnu.org/onlinedocs/gcc/Function-Attributes.html It says:
It is an error if ‘__f’ is not defined in the same translation unit.
So if you remove useless from the translation unit where important is it looks like an error to me.

C2732 - Linkage specification error

I'm using VS2008. I'm getting the following error.
BUILD: [02:0000000295:ERRORE] c:\wince700\platform\am33x_bsp\src\bootloader\bootpart\bootpart_e.cpp(61) : error C2732: linkage specification contradicts earlier specification for 'SdhcInitialize' {log="C:\WINCE700\platform\AM33X_BSP\SRC\BOOTLOADER\bldsys.log(103)"}
BUILD: [02:0000000297:ERRORE] NMAKE : fatal error U1077: 'C:\WINCE700\sdk\bin\i386\ARM\cl.EXE' : return code '0x2' {log="C:\WINCE700\platform\AM33X_BSP\SRC\BOOTLOADER\bldsys.log(104)"}
BUILD: [02:0000000299:ERRORE] clean TargetCompilePass -nologo BUILDMSG=Stop. BUILDROOT=C:\WINCE700\platform\AM33X_BSP CLEANBUILD=1 NOLINK=1 NOPASS0=1 failed - rc = 2. {log="C:\WINCE700\platform\AM33X_BSP\SRC\BOOTLOADER\bldsys.log(105)"}
file_1.cpp
extern "C"
{
// some extern declarations
extern void SdhcInitialize(DWORD slot);
}
file_2.c
void SdhcInitialize(DWORD slot)
{
//some code
}
Please guide me how to resolve.
I'm guessing that you have a header that contains a prototype for the SdhcInitialize() function, and that the header was written for use by C programs. So for example, the header file might include something like the following line:
SD_API_STATUS SdhcInitialize(DWORD slot);
without being enclosed in an extern "C" {} block (since the header is intended for C programs).
Additionally, I suspect that this header is being included - directly or indirectly - by file_1.cpp
This means that the header cannot be included in a C++ program without some additional work being done, otherwise the C++ program will see the declaration as meaning that SdhcInitialize() has C++ linkage.
You have two reasonable approaches to fixing this:
if you can modify the header, add the following lines around the declarations in the header:
#if __cplusplus
extern "C" {
#endif
// declarations go here
#if __cplusplus
}
#endif
This way, C++ files will have the declarations enclosed in a extern "C" linkage block, while C program will not see the extern "C" bits (which would otherwise confuse the C compiler).
I think an argument can be made that all C headers should include something like those lines so that the C functions can be consumed by C++ programs without hassle.
if you cannot modify the header for some reason, you can work around the problem by including the header in C++ files like so:
extern "C" {
#include "Sdhc-header.h"
}
If you surround a set of function declaration by extern "C" { ... }, you don't need to use an additionnal externkeyword in front of the function identifier.
extern "C"
{
// some extern declarations
SD_API_STATUS SdhcInitialize(DWORD slot);
}
When you try to include the "some header files of C" file in "C++ file"(the header file has some where extern "C" for some functions).
include the header earlier will solve the problem.
e.g. Try to move #include "myHeader.h" on the top lines of your C++ file.
This solves my problems.
Hope it helps....
I have solved this as follows (the other solutions did not work for me):
In the file vector.cc:
#define __INVECTOR //solves
#include "vector.h"
In vector.h:
#ifndef __INVECTOR
void function(...etc..);
#endif
This way the declaration isn't read unless we want to call the function from a different file.

C linkage and c++ headers

I want to use some c++ classes in shared library with C linkage. And i got following problems.
If
#include <iostream>
extern "C"
{
void f(){}
}
Compiles and links succesfully, but f() could not be found in resulting library.
If
extern "C"
{
#include <iostream>
void f(){}
}
I got many compiler errors (just dont know how to correctly translate them in english, something about template with C linkage) on every occurence of C++ keyword "template" in iostream and included headers.
What should be done?
The first variant is correct. Only stuff that even exists in C can be declared in a extern "C" block, and templates and classes certainly don't belong in that category. You only have to make sure, that your function is also declared as extern "C" in the header if you are using a C++-compiler. This is often achieved by writing
#ifdef __cplusplus
// C++ declarations (for example classes or functions that have C++ objects
// as parameters)
extern "C"
{
#endif
// C declarations (for example your function f)
#ifdef __cplusplus
}
#endif
in the header.
The first is correct; system headers (and most other headers as well)
can only be included at global scope, outside any namespaces, classes,
functions or linkage specification blocks. There are likely things in
<iostream> that wouldn't be legal in an extern "C", and even if
there weren't, the name mangling for extern "C" is almost certainly
different from that of C++, and the library itself was surely compiled
as extern "C++".
How did you define f? This could be a similar problem: if the source
file compiles it as an extern "C++" function, then the name will be
mangled differently than it was in the client files which compiled it as
an extern "C" function.
The general policy here is to handle the headers and the function
definitions/declarations in the same way you normally do, except that
you ensure that the header can be included in both C and C++ sources,
e.g.:
#if __cplusplus
extern "C" {
#endif
void f();
// Other `extern "C"` functions...
#if __cplusplus
}
#endif
Then include this header in all files where the function f() is used,
and in the file where it is defined.
Use __declspec to export functions, classes, etc...
Also: http://msdn.microsoft.com/en-us/library/a90k134d(v=vs.80).aspx
And this one is very good: http://www.flounder.com/ultimateheaderfile.htm

Including functions with extern C linkage in library

I have included some C functions with extern c linkage in c++ code. E.g.
// File Y.cpp:
extern C {
void fnA(void) { }
void fnB(void* a, void* b) { }
}
class test {
....
};
// end of file
File Y is under module Mod. While building library libMod-O.a for module Mod, I don't see the functions under extern block included, unless Y.h is included in some other file (Mod.cpp) and the class test is used. So unless I create an object of test class in Mod.cpp, I do not see the extern functions (fnA, fnB) in the libMod-O.a, even through Y.cpp is compiled during build of libMod-O.a. The result of this is that linker error happens as another module uses fnA, fnB.
I do not see the connection between the extern functions fnA and fnB being included and usage of class test in Mod.cpp. Is this expected or is there a better way to define this?
You mean extern "C" of course.
You need to have a clean separation between your C code and your C++ code.
In YourCCode.h:
#ifdef __cplusplus
extern "C" {
#endif
void fnA(void);
void fnB(void* a, void* b);
#ifdef __cplusplus
}
#endif
In YourCCode.c:
void fnA(void) {}
void fnB(void* a, void* b) {}
Make sure your compiler compiles YourCCode.c as C, not as C++.
In your C++ code
#include "YourCCode.h"
fnA();
// etc.
you might have a link order problem where the files that use fnA come after the link of libMod-O.a but where Mod.cpp with object test comes before libMod-O.a so the obj file is pulled in before fnA/fnB are needed later. the gnu linker is a single pass linker by default.