Is it legal to forward-declare a struct as a C-struct
// api.h
#ifdef __cplusplus
extern "C" {
#endif
typedef struct handle_tag handle_t;
handle_t *construct();
void destruct(handle_t *h);
void func(handle_t *h);
#ifdef __cplusplus
}
#endif
and subsequently define it as a C++-struct, i.e. as a non-POD type?
// api.cpp
struct handle_tag {
void func();
std::string member;
};
void func(handle_t *h) {
h->func();
}
The general intention is to get via a C interface an externally accessible opaque type handle_t which is internally implemented as an C++ data type.
Yes, that will work fine as long as the C code never needs to see "inside" the handle_tag structure, and the appropriate C++ construction/destruction is performed by the C++ code (which I preseume the construct and destruct are for).
All that the C code needs is a pointer to some datastructure - it won't know what the contents is, so the content can be anything you like it to to be, including constructor/destructor reliant data.
Edit:
I should point out that this, or methods similar to it (e.g. using a void * to record the address of an object for the C portion to hold), is a fairly common way to interface C-code to C++ functionality.
Edit2:
It is critical that the C++ code called doesn't "leak" exceptions into the C code. That is undefined behaviour, and very much liable to cause crashes, or worse, "weird things" that don't crash... So unless the code is guaranteed to not cause exceptions (and for example std::string is liable to throw bad_alloc in case of low memory), it is required to use a try/catch block inside code like construct anf func in the C++ side.
Won't work quite as is, but the concept's ok. When the functions are defined, you also need to make sure the names aren't mangled so that the C code can find them. That means #include "api.h" should be added atop your api.cpp file.
Related
I have started using C++ for win32. As we know C++ struct is same as class but default public member etc... Now I want is simple C struct that has no default constructor or copy or move operation or any other magic. Because I want to store it in file also perform memcpy, use as BYTE array etc... So I thought of defining it in header with #ifdef __cplusplus as follow.
#ifdef __cplusplus
extern "C" {
#endif
typedef struct PersonTag
{
int ID;
char Name[200];
} Person, *PPerson;
#ifdef __cplusplus
}
#endif
But this only prevent name mangling for function. But struct still compile as cpp struct if in cpp file or as C struct if in C file. I tested it by including header in cpp file and in C file. Both compiles well but if I add constructor in struct body as following.
#ifdef __cplusplus
extern "C" {
#endif
typedef struct PersonTag
{
PersonTag();
int ID;
char Name[200];
} Person, *PPerson;
#ifdef __cplusplus
}
#endif
C++ compile it without err but C fails as it should be. Does that means that even if I include struct definition inside #ifdef __cplusplus it will still compile as C++ struct with all copy and move magic with it ?
I was thinking that by defining struct in side ' extern "C" ' will also produce err in cpp file if struct has c++ style constructor etc...
So my question is, Is there a way to tell c++ compiler(vc++) to consider and compile it as like it is pure C struct, no default constructor, no default destructor, move or copy like thing or anything else.
My second question is about typdefing cpp function pointer and passing this function pointer to C function which may call it back.
I am using C lib (sqlite3) and passing callback fun. Now if I typedef function pointer in cpp file by following way.
typedef int (*SQL_CALLBACK_FUN)(void* NotUsed, int argc, char** argv, char** azColName);
in Cpp file the function might be like
int Callback_GetAllPerson(void* NotUsed, int argc, char** argv, char** azColName);
and than the function pointer be
SQL_CALLBACK_FUN sql_callback_fun = Callback_GetAllPerson;
Now I am going to pass this "sql_callback_fun" function pointer to C lib function (sqlite3_exec).
My question is, here I typdef function pointer in CPP also the callback function will compile as cpp. now I am passing it to C lib as pointer and the C lib function will call back this cpp function via pointer.
Is there any runtime err I might face? or the function pointer (typdef) is same for both cpp and c. regardless of name mangling things?
But struct still compile as cpp struct if in cpp file or as C struct if in C file
You can't compile some code as C using a C++ compiler. A C++ compiler compiles everything as C++ code.
Fortunately the C++ language guarantees that "standard-layout" classes are layout-compatible with C. So you have nothing to worry about, your struct will be identical when compiled as C or C++. The extern "C" only affects name mangling (adds a leading underscope, for example), so that identifiers compiled as C++ can be "found" by C code during linking.
Obviously you should not add C++ features like a constructor, if the struct is to be compiled as C as well as C++.
For free function pointers, make sure to use extern "C" to enforce C language linkage.
So your very first code fragment looks fine.
But this only prevent name mangling for function. But struct still compile as cpp struct if in cpp file or as C struct if in C file.
This is actually a good thing. This allows you to use the struct in both languages.
I was thinking that by defining struct in side ' extern "C" ' will also produce err in cpp file if struct has c++ style constructor etc
You were thinking wrong. It only affects name mangling.
Is there a way to tell c++ compiler(vc++) to consider and compile it as like it is pure C struct
No. C++ compiler compiles C++; C compiler compiles C.
If you want a struct definition to be compatible in both languages, then you must write it in common subset of both languages.
Is there any runtime err I might face [with callbacks]?
Typical potential problem is that if the callback throws, then things blow up because the calling C code won't deal with exceptions. You must make sure that the callback doesn't throw.
extern "C" has nothing to do with the struct. But you are on the right track though, you just need to #ifdef the constructor:
/* Compiles as both C++ and C */
struct Person {
int ID;
char Name[200];
#ifdef __cplusplus
Person() : ID(0), Name("") {}
#endif
};
typedef struct Person Person, *PPerson;
If you really don't want to have access to the constructors generated by the compiler, you can delete them:
#ifdef __cplusplus
Person() = delete;
Person(const Person&) = delete;
Person(Person&&) = delete;
#endif
Compiling the following code to test proves that these structures have identical sizes:
int main () {
printf("Person is %d bytes\n", sizeof(Person));
}
output:
$ g++ -o foo_cpp foo.c
$ ./foo_cpp
Person is 204 bytes
$ gcc -o foo_c foo.c
$ ./foo_c
Person is 204 bytes
As for your second question: yes, passing a C++ function pointer will work as expected. Symbol mangling and conversion to an address is handled by the C++ compiler.
For a simple struct with no class members, the default constructor and destructor will be empty functions. No members will be initialized, and no members will be destroyed. A good optimizer will completely eliminate the calls to those empty functions, leaving you with the same generated code under both C and C++.
Similarly with the default copy constructor and copy operator, they will do a simple bit-wise copy of the entire struct. Same operation regardless of whether it's C or C++.
Since C can't do move operations, no need to even think about it.
In short, there's nothing to worry about. Consider that Microsoft defines the entire Windows API with C structures, but people call it from C++ every day.
I am attempting to write use the this C library without modification in a C++ application. It uses C11 atomics.
Consider the following program, which we can put in a file called main.cc.
#include "mpscq.h"
int main(){}
If I compile this with the g++ -std=c++11 -c main.cc, I get a whole collection of errors that look like the following.
usr/lib/gcc/x86_64-linux-gnu/4.9/include/stdatomic.h:68:9: error: ‘_Atomic’ does not name a type
typedef _Atomic __UINT_FAST32_TYPE__ atomic_uint_fast32_t;
^
/usr/lib/gcc/x86_64-linux-gnu/4.9/include/stdatomic.h:69:9: error: ‘_Atomic’ does not name a type
typedef _Atomic __INT_FAST64_TYPE__ atomic_int_fast64_t;
Is it possible to fix these errors without any modification to the library code?
That is, I am willing to use whatever magic incantations are needed in my c++ code or my compiler flags, but I'd rather not change the library code.
I have seen this answer but it requires modification of the header file.
Is it possible to fix these errors without any modification to the library code?
C++ does not have an _Atomic type qualifier, but the structure declared in the library header has members, including the first, with _Atomic-qualified type. C++ will not accept this (and wrapping the header inclusion in an extern "C" block will not help).
C allows objects of _Atomic-qualified type to have different representations than objects of the non-_Atomic version of the same type, so in a general sense, this means that C++ cannot directly access any members of any instance of that structure.
If your C implementation happened to use the same representation for corresponding _Atomic and non-_Atomic types, then you could probably make your C++ compiler accept the header by #defineing the symbol _Atomic to an empty string, but this would be a gateway to more subtle bugs. You could not expect C++ to provide the atomic access semantics that the library expects, so even in this case C++ cannot safely access the structure's members (directly).
If it is sufficient to manipulate instances of the struct only via the provided functions, then you can do so via opaque pointers. Create a version of the header that provides a forward declaration of the structure, but no definition, and that provides all the function declarations (with C linkage). You C++ code should accept the header and be able to call the functions, both receiving and returning pointers to instances of the structure that it must never try to dereference.
#ifndef __MPSCQ_H
#define __MPSCQ_H
#ifdef __cplusplus
extern "C" {
#endif
struct mpscq;
struct mpscq *mpscq_create(struct mpscq *n, size_t capacity);
// other functions ...
#ifdef __cplusplus
}
#endif
#endif
If you need more than the existing functions provide, then you'll need to write additional helper functions, in C.
You do not need wrapper for using that mpscq in C++ code but you need different
header to include it. That would work:
#ifndef MPSCQ_H_FOR_CPP
#define MPSCQ_H_FOR_CPP
extern "C"
{
struct mpscq;
mpscq *mpscq_create(mpscq *n, size_t capacity);
bool mpscq_enqueue(mpscq *q, void *obj);
void *mpscq_dequeue(mpscq *q);
size_t mpscq_count(mpscq *q);
size_t mpscq_capacity(mpscq *q);
void mpscq_destroy(mpscq *q);
}
#endif
That said I would anyway write a wrapper to take care of RAII and such.
I have this code:
(exe)
#include <Windows.h>
#pragma comment(lib, "user32.lib")
class Dummy;
typedef void(Dummy::*Referece)(int i);
typedef void(*InitCall)(void*, Referece);
class Dummy
{
public:
Dummy(){}
void callMe(int val)
{
MessageBoxA(0, "ok", "ok", 0);
}
};
int main()
{
Dummy* obj = new Dummy();
HMODULE ha= LoadLibraryA("aa.dll");
InitCall val = (InitCall)GetProcAddress(ha, "Init");
val(obj, &Dummy::callMe);
}
and my dll:
(.h)
#pragma once
#define DLL_EXPORT __declspec(dllexport)
class Test;
typedef void (Test::*Reference)(int a);
#ifdef __cplusplus
extern "C"
{
#endif
void DLL_EXPORT Init(Test* Object, Reference reference);
#ifdef __cplusplus
}
#endif
(.cpp)
#include "your.h"
void DLL_EXPORT Init(Test * Object, Reference reference)
{
(Object->*reference)(1);
}
I reproduced the system and should be like this cause i can't change code in one side.
Why i get access violation? Calling "val(obj, ref)" i expect a pointer to class + offset to method call.
A pointer to member is not an "offset into the class." There's no such thing. In some cases (such as a pointer to a virtual member function in a class with a simple inheritance hierarchy), its implementation can consist of such an offset (plus potentially a few other bits of data).
However, for a non-virtual function (like in your example), it likely has a plain pointer to function underneath it. Non-virtual functions are not stored in any "table" with "offsets" (there is at least no reason to store them that way), they're most likely implemented as normal bog-standard functions with a mangled name and a prepended prameter.
Pointers to member are a somewhat tricky part of C++, largely due to the fact that there is no obvious mapping to an implementation concept and different compilers can handle them in different ways. Trusting that a void (Dummy::*)(int) and void (Test::*)(int) are binary compatible is quite fragile.
In general, you cannot expect the binary representation of a pointer to Dummy::callMe to be in any way similar to that of a pointer to a member function of Test, as it can depend too much on the definitions of Dummy and Test, and how the compiler implements pointers to member.
To top it off, the way in which Visual Studio's compiler handles pointers to member by default is non-conforming (so, from most perspectives, broken). This default handling is such that to correctly form a pointer to member of a class, the compiler needs to have seen the definition of the class. The reason is that the most general implementation of a pointer to member is quite large (4 native words, I believe), because it has to account for virtual inheritance and such. The most common case of a single-base class with no virtuals can fit in a native word.
Therefore, if you want to reliably use perfectly standard C++ constructs like accepting a pointer to member of a class whose definition is not visible at the site, you have to use the compilation flag /vmg. With this, the most general representation will always be used.
The default behaviour, /vmb, optimises the binary representation (including size!) of A::* based on the definition of A. It's therefore impossible to create a typedef such as yours with this behaviour in effect.
As to what your options are:
If you absolutely have to pass through a C-style interface, force use of a C-style function as a callback on the side calling the callback, and create a wrapper C-style function on the registering side. Something like this:
class Dummy
{
void callMe(int) {}
};
extern "C" void fw_Dummy_callMe(void *self, int i)
{ static_cast<Dummy*>(self)->callMe(i); }
Plus
#ifdef __cplusplus
extern "C"
{
#endif
void DLL_EXPORT Init(void* Object, void (*reference)(void*, int));
#ifdef __cplusplus
}
#endif
If you can have C++ in the interface (that is, the compiler and version will always be the same on both sides of the DLL interface), you can use a pointer to member function provided that:
The two sides will not see different definitions of the class. It's fine if one of them only has a non-defining declaration, though. To be 100% C++ conformant, the names of the class should be the same.
You use /vmg when building both the DLL and its client.
I want to write a C-wrapper around an existing C++ codebase. So I need to implement some C-API functions that merely forward their operations to the corresponding C++ methods.
My problem is, I cannot figure out how to implement a forward-defined struct by means of an existing class:
//Foo.hpp
namespace myLib {
struct Foo {
//some meaningful C++ body
};
}
//foo.h
//#ifdef __cplusplus etc. left out
extern "C" {
struct myLib_foo;
myLib_foo* mkfoo();
//etc.
}
//foo.cpp
extern "C" {
#include "Foo.hpp"
#include "foo.h"
typedef myLib_foo myLib::Foo; //this does not work
myLib_foo* mkfoo() { return new myLib::Foo(); }
}
In this situation, the C-API can and shall only work with pointers to myLib::Foo, which obviously works well, if I define myLib_foo as a new struct inside foo.cpp. I guess it also works, if I define a struct myLib_foo somewhere else. Yet, since I want to keep my namespaces manageable, I am searching a way to define myLib_foo to be equivalent to some existing (and completely defined) struct. This, however does not work, since my compiler refuses the code above with "typedef redefinition with different types". Apparently, it distinguishes between type-aliases and structs.
Is there even a way to achieve what I want or does C++ have no means for real type-aliases?
edit:
By the answer below, I figured I can use inheritance plus static_cast:
//foo.cpp
extern "C" {
#include "Foo.hpp"
#include "foo.h"
struct myLib_foo : public myLib::Foo {}; //this does work
myLib_foo* mkfoo() { return static_cast<myLib_foo*>(new myLib::Foo()); }
}
The C code does not ever need a definition of the struct myLib_foo that it sees.
It handles it only through pointers and functions.
On the C++ side a simple implementation is a struct containing a pointer to the "real" object that it represents. You then define it in the ordinary way (in the C++ code), but of course with the name already established for the C code usage.
If you want to avoid even that little inefficiency, to hand out real object pointers to the C code, well then you have essentially two options:
reinterpret_cast, or
static_cast with the not-quite-C struct as a (possibly empty) base class.
But I would go for the simple implementation of myLib_foo as a struct with a pointer to the real one. The KISS principle: Keep It Simple, Stupid*. On second thoughts, to avoid allocation and deallocation issues, and to also avoid a formal dependency on the compiler (even if that would just be academic, a formality), I would go for the static_cast. For thinking about what it all entails, this seems simplest.
*: Oh, the last few times I mentioned this principle on SO, those answers were heavily downvoted. That has also happened when I have (correctly) proposed macros as solutions. I think one should be technically honest and ignore the general SO readership, so I mention it anyway.
I observed a function in a dll which has C linkage. This function returns class type. I am not sure how this is made possible as C doesn't understand class.
I wrote a sample dll and program myself and noted that the VC++ compiler shows a warning to this effect but doesn't stop you. The program is able to GetProcAddress of this function and call it to receive the returned object. The class definition was made available to program.
Furthermore, if I write a function with C linkage that returns a class type where this class is not even exported, the compiler doesn't issue any warning. The program can consume this function from the dll provided the class definition is made available to it.
Any thoughts on how this works? Is such a behavior compiler/platform specific?
You are misunderstanding the behavior of extern "C".
It only impacts the name of the function in the object file by preventing name mangling. It does not make a function neither "more C" or "less C++". The only additional limitation which extern "C" adds to a C++ function is that is shall not be overloaded.
It's possible for functions with C linkage to return objects that can't be expressed in C so long as they can be manipulated in C. According to my copy of Design & Evolution of C++ (section 11.3.3):
We considered several alternatives to the type-safe linkage schemes before deciding on the one actually added to the language [Stroustrup,1988]: ...
provide type-safe linkage only for functions that couldn't be C functions because they had types that couldn't be expressed in C. ...
A function declared to have C linkage still has C++ calling semantics. That is, the formal arguments must be declared, and the actual arguments must match under the C++ matching and ambiguity control rules. ... Had we provided special services for C, we would have been obliged to add an unbounded set of language calling conventions to C++ compilers [for linking to Pascal, Fortran, PL/I, etc.]. ...
Linkage, inter-language calls, and inter-language object passing are inherently difficult problems and have many implementation-dependent aspects. ... I expect we haven't heard the last of this matter.
That is, C++ linkage isn't based on whether the types involved are valid C types. That is an intentional design decision. It allows you to create a DLL compiled in C++ but that can be used from C via a header:
// in the header
struct Foo; // forward declaration
#ifdef __cplusplus
extern "C" {
#endif
struct Foo* create_foo();
void destroy_foo(struct Foo*);
void foo_bar(struct Foo*);
#ifdef __cplusplus
} // extern "C"
// now declare Foo
struct Foo {
void bar();
};
#endif
// in the implementation file
#include <iostream>
extern "C" {
Foo* create_foo()
{
return new Foo();
}
void destroy_foo(Foo* f)
{
delete f;
}
void foo_bar(Foo* f)
{
f->bar();
}
}
void Foo::bar()
{
std::cout << "Foo::bar() called\n";
}
Note the calls to new, delete and std::cout. These all require the C++ runtime. Therefore, the implementation file must be compiled with C++, but since the functions have C linkage, the header can be used from either C or C++.
So how do you get a Foo in C? In this case, you don't, because there's no way to fully declare it in the header in a way that C will understand. Instead, C only sees the forward declaration, which creates an incomplete type. C doesn't know how large an incomplete type is, so C functions can't create it or operate on them directly, but C does know how large a pointer to an incomplete type is, so C can operate on a pointer to an incomplete type. You can get much more creative and actually create POD types in C++ that you can operate on directly in C.
As long as you only use Foo*s in C, you don't have to actually define the Foo struct in C. It so happens that APR uses a similar design ("Creating an APR Type," I couldn't find a better link).
A class object is basically just a struct object, with some extra "hidden" members for the vtbl and so on.
However, I'm not sure what you mean by "the program can consume this function provided the class definition is made available to it". C would throw a compiler error on seeing class Blah { ... };.