I am writing some C code to interface with a piece of hardware. This hardware has built-in API functions, which are defined in a header file, enclosed in an extern "C" block. For some reason, I cannot get the program to compile when I select "compile as C code" in the property sheets. Even stranger, I can get it to give no errors when I "compile as C++", don't change any code, then change it back to "compile as C". What is going on? The only two files I have are main.c, header_file.h, and a defines_file.h (which is #included in header_file.h).
I wrote a very simple program to illustrate what is happening:
#include "header_file.h"
int main(){
return 0;
}
To give some more detail, the error I am getting is coming from the header file, which looks something like:
#ifdef __cplusplus
extern "C" {
#endif
#include defines_file.h
FUNC_API(int) Function(Type1 var1, Enum2 var2, Type3 var3, void* ptr=0);
#ifdef __cplusplus
}
#endif
The defines_file.h looks like:
enum Enum2{
//enumerator list
};
The errors are:
1>c:\header_file\include\header_file.h(78): error C2146: syntax error : missing ')' before identifier 'var2'
1>c:\header_file\include\header_file.h(78): error C2081: 'Enum2' : name in formal parameter list illegal
1>c:\header_file\include\header_file.h(78): error C2061: syntax error : identifier 'var2'
1>c:\header_file\include\header_file.h(78): error C2059: syntax error : ';'
1>c:\header_file\include\header_file.h(78): error C2059: syntax error : ','
1>c:\header_file\include\header_file.h(78): error C2059: syntax error : ')'
It seems like it's thinking that Enum2 is a variable name of Type1. It's as though I'm missing the Enum2 definition, but I'm not - it's defined in defines_file.h. One thing I did notice is that Enum2 is not defined inside an "extern C" block. Is that a problem? I don't want to change the header files because I didn't write them. (I'm still not 100% sure how "extern C" works.)
The interface declared extern "C" must itself be valid C; so the interface:
May not be a class member
May not be overloaded
May not involve class types
May not have optional arguments
Must be syntactically valid as C code.
You can use POD structs so long as they themselves are declared extern "C" in C++ compilation. In C, unlike C++ struct, enum and union tags are not on their own type names, so must be explicitly qualified, or a typedef alias defined.
Note that the following:
#if defined __cplusplus
extern "C"
{
#endif
// Declarations must be valid C syntax
int function() ;
#if defined __cplusplus
}
#endif
Resolves to:
extern "C"
{
// Declarations must be valid C syntax
int function() ;
}
in C++, and just:
// Declarations must be valid C syntax
int function() ;
in C
The important point is that when compiled as C code, nothing C++ specific may be included.
The extern "C" is C++ syntax to switch of all symbol name mangling that is necessary to support overloading, class membership, and optional parameters etc. and which is not valid C. It forces the symbol name in C++ compilation to be the same as that in C compilation.
In C you need to define an enum as a type or actually prefix it with the word enum when you use it like a type name.
So either you need to modify the definition
//replace this:
enum Enum2{A,B,C,D};
//with this
typedef enum{A,B,C,D} Enum2;
or you can keep the definition the same and change the function signature to
FUNC_API(int) Function(Type1 var1, enum Enum2 var2, Type3 var3, void* ptr);
The fact of the matter is that C and C++ are two distinct languages and if the header wasn't written with one of the two in mind, then you can just not use it without modifying it.
edit: as #Clifford mentioned, the same goes for struct types.
struct Type1 //this is passed as "struct Type1 pName"
{
int a,b,c;
};
typedef struct //this is passed as "Type2 pName"
{
int a,b,c;
}Type2;
//this is passed as either "struct type3 pName" or "type3_t pName"
typedef struct type3
{
int a,b,c;
}type3_t;
Related
I have mixed code base of C/C++ in a MS Visual Studio 2010 project and am trying to call a static function defined in C++ file from a C src file. Right now I get it to work by renaming the C src to CPP (a.c -> a.cpp) Just wondering if there's a more graceful way of getting around it (turning some magic compiler flags on etc) without doing any large scale surgery to the code base(like using opaque pointers as suggested in this thread)
Pls note my code base is pretty complex, I have created this small VS snippet to reproduce the error with bare minimum demonstrable code
a.c
#include "b.h"
void test()
{
B::func();
}
b.h
#ifdef __cplusplus
extern "C" {
#endif
class B{
public:
static void func();
};
#ifdef __cplusplus
}
#endif
b.cpp
#include "b.h"
#ifdef __cplusplus
extern "C" {
#endif
void B::func()
{
return;
}
#ifdef __cplusplus
}
#endif
Error:- In MS Visual Studio 2010
1>c:\.....\b.h(5): error C2061: syntax error : identifier 'B'
1>c:\.....\b.h(5): error C2059: syntax error : ';'
1>c:\.....\b.h(5): error C2449: found '{' at file scope (missing function header?)
1>c:\.....\b.h(8): error C2059: syntax error : '}'
First, :: is not valid in C.
Second, including a header is equivalent to copy-pasting a .h file into your C file. Your header must be valid C. Here's some deeper insight:
How to call C++ function from C?
Elegantly call C++ from C
Though, my alternative advice is, compile your C as C++. There's a chance it would take minimal or no work to turn out as valid C++.
I'm attempting to translate a C++ DLL header file into a C/C++ compatible header. While I've gotten most of the major constructs in, I'm running into one last compiler issue I can't seem to explain. The following code works fine in C++ but when I attempt to compile a C application which just includes this file I get errors for my function definitions in my header file.
Code.h:
typedef void *PVOID;
typedef PVOID HANDLE;
#define WINAPI __stdcall
#ifdef LIB_EXPORTS
#define LIB_API __declspec(dllexport)
#else
#define LIB_API __declspec(dllimport)
#endif
struct ToolState
{
HANDLE DriverHandle;
HANDLE Mutex;
int LockEnabled;
int Type;
};
#ifdef __cplusplus
extern "C" {
#endif
(LIB_API) int SetRate(ToolState *Driver, int rate);
(LIB_API) void EnableLock(ToolState *Driver) ;
(LIB_API) int SendPacket(ToolState *Driver, unsigned char *OutBuffer, int frameSize);
//These also give me the same error:
//LIB_API WINAPI int SendPacket(ToolState *Driver, unsigned char *OutBuffer, int frameSize);
//__declspec(dllimport) WINAPI int SendPacket(ToolState *Driver, unsigned char *OutBuffer, int frameSize);
//Original C++ call that works fine with C++ but has multiple issues in C
//LIB_API int SetRate(ToolState *Driver, int rate);
#ifdef __cplusplus
}
#endif
Errors:
error C2059: syntax error : 'type'
error C2059: syntax error : 'type'
error C2059: syntax error : 'type'
Google searching hasn't generated any relevant results. The following threads were close but don't exactly answer my question:
C2059 syntax error using declspec macro for one function; compiles fine without it
http://support.microsoft.com/kb/117687/en-us
Why is this syntax error occuring?
In C, structs are not types, so you must use struct Foo and enum Bar where in C++ you are able to use Foo and Bar.
Notes:
In C++, you can still use the old syntax even when the type is a class.
In C, people often use typedef struct Foo Foo which allows the same syntax as in C++ then.
My C++ program needs to use an external C library.
Therefore, I'm using the
extern "C"
{
#include <library_header.h>
}
syntax for every module I need to use.
It worked fine until now.
A module is using the this name for some variables in one of its header file.
The C library itself is compiling fine because, from what I know, this has never been a keyword in C.
But despite my usage of the extern "C" syntax,
I'm getting errors from my C++ program when I include that header file.
If I rename every this in that C library header file with something like _this,
everything seems to work fine.
The question is:
Shouldn't the extern "C" syntax be enough for backward compatibility,
at least at syntax level, for an header file?
Is this an issue with the compiler?
Shouldn't the extern "C" syntax be enough for backward compatibility, at least at syntax level, for an header file? Is this an issue with the compiler?
No. Extern "C" is for linking - specifically the policy used for generated symbol names ("name mangling") and the calling convention (what assembly will be generated to call an API and stack parameter values) - not compilation.
The problem you have is not limited to the this keyword. In our current code base, we are porting some code to C++ and we have constructs like these:
struct Something {
char *value;
char class[20]; // <-- bad bad code!
};
This works fine in C code, but (like you) we are forced to rename to be able to compile as C++.
Strangely enough, many compilers don't forcibly disallow keyword redefinition through the preprocessor:
#include <iostream>
// temporary redefinition to compile code abusing the "this" keyword
#define cppThis this
#define this thisFunction
int this() {
return 1020;
}
int that() {
return this();
}
// put the C++ definition back so you can use it
#undef this
#define this cppThis
struct DumpThat {
int dump() {
std::cout << that();
}
DumpThat() {
this->dump();
}
};
int main ()
{
DumpThat dt;
}
So if you're up against a wall, that could let you compile a file written to C assumptions that you cannot change.
It will not--however--allow you to get a linker name of "this". There might be linkers that let you do some kind of remapping of names to help avoid collisions. A side-effect of that might be they allow you to say thisFunction -> this, and not have a problem with the right hand side of the mapping being a keyword.
In any case...the better answer if you can change it is...change it!
If extern "C" allowed you to use C++ keywords as symbols, the compiler would have to resolve them somehow outside of the extern "C" sections. For example:
extern "C" {
int * this; //global variable
typedef int class;
}
int MyClass::MyFunction() { return *this; } //what does this mean?
//MyClass could have a cast operator
class MyOtherClass; //forward declaration or a typedef'ed int?
Could you be more explicit about "using the this name for some variables in one of its header files"?
Is it really a variable or is it a parameter in a function prototype?
If it is the latter, you don't have a real problem because C (and C++) prototypes identify parameters by position (and type) and the names are optional. You could have a different version of the prototype, eg:
#ifdef __cplusplus
extern "C" {
void aFunc(int);
}
#else
void aFunc(int this);
#endif
Remember there is nothing magic about header files - they just provide code which is lexically included in at the point of #include - as if you copied and pasted them in.
So you can have your own copy of a library header which does tricks like the above, just becoming a maintenance issue to ensure you track what happens in the original header. If this was likely to become an issue, add a script as a build step which runs a diff against the original and ensures the only point of difference is your workaround code.
So I'm having issues with some code that I've inherited. This code was building fine in a C-only environment, but now I need to use C++ to call this code. The header problem.h contains:
#ifndef _BOOL
typedef unsigned char bool;
static const bool False = 0;
static const bool True = 1;
#endif
struct astruct
{
bool myvar;
/* and a bunch more */
}
When I compile it as C++ code, I get error C2632: 'char' followed by 'bool' is illegal
I get the same error if I wrap the #include "problem.h" in extern "C" { ... } (which I don't understand, because there should be no keyword bool when compiling as C?)
I tried removing the block from #ifndef _BOOL to #endif, and compiling as C++, and I get errors:
error C2061: C requires that a struct or union has at least one member
error C2061: syntax error: identifier 'bool'
I just don't understand how the C++ compiler is complaining about a redefinition of bool, yet when I remove the redefinition and try to just use bool to define variables, it doesn't find anything.
Any help is greatly appreciated.
Because bool is a basic type in C++ (but not in C), and can't be redefined.
You can surround your code with
#ifndef __cplusplus
typedef unsigned char bool;
static const bool False = 0;
static const bool True = 1;
#endif
You can use C99's bool:
#ifndef __cplusplus
#include <stdbool.h>
#endif
bool myBoolean; // bool is declared as either C99's _Bool, or C++'s bool data type.
Why should you use this?
For compatibility with other C99 code. _Bool is commonly used in C99 Code, and is very useful. It also grants you the ability to have a boolean datatype without the need to typedef a lot of stuff, as behind the scenes, _Bool is a datatype defined by the compiler.
You should use the __cplusplus macro:
#ifndef __cplusplus
#ifndef _BOOL
typedef unsigned char bool;
static const bool False = 0;
static const bool True = 1;
#endif
#endif
Check out this link to the C++ FAQ for further details.
I had this "'char' followed by 'bool' is illegal" problem in VS also.
The problem for me was I did not end my class declaration with a semi-colon- which I was not expecting to be the issue as this was in the header file and the problem was coming up in the cpp file!
eg:
class myClass
{
}; // <-- put the semi colon !!
This warning is triggered multiple times in my code by the same declaration, which reads :
// Spreadsheet structure
typedef struct SPREADSHEET
{
int ID; // ID of the spreadsheet
UINT nLines; // Number of lines
void CopyFrom(const SPREADSHEET* src)
{
ID = src->ID;
nLines = src->nLines;
}
};
I don't want to just turn off that warning,
but rather change the code so that the warning doesn't come up !
NOTE : I don't want to declare any variables here (it's a header file), only define what the struct 'SPREADSHEET' should include...
Delete typedef. It's the C way of declaring structs, C++ does it automatically for you.
If you use same header for both C and C++, you need to add some identifier before the terminating ";", something like:
typedef struct BLAH { ... } BLAH;
But if you use it only for C++, instead simply remove "typedef " part (and don't add identifier before the terminating ";", as without "typedef " part that creates a variable).
Also, you may want to edit C-only-headers and wrap everything in extern "C" { ... }, to support C++, like:
#ifndef MY_HEADER_H
#define MY_HEADER_H
#ifdef __cplusplus
extern "C" {
#endif // __cplusplus
// ... Content: Includes and Type-defs go here ...
#ifdef __cplusplus
} // extern C
#endif // __cplusplus
#endif // MY_HEADER_H
I mean, else each C++ file which includes your C-only-header needs to wrap separately.
Just remove "typedef". You declare a new struct and the typedef keyword isn't used for that. You would use typedef to define a new name for an existing type, like this:
typedef int number;
Yes, the BLAH after the closing brace is important to make the typedef a valid one. You can remove the SPREADSHEET from the present place and keep it in between the } and the ;.
My interpretation of this warning is that the compiler is indicating that the typedef keyword is unnecessary because a variable is not being declared. and therefore if the intention of the code is to simply declare a struct the typedef is superfluous.