The following code explains the situation I encountered:
#ifdef __cplusplus
namespace ns
{
class pod
{
const short foo;
const char bar;
public:
pod(short f,char b):foo(f),bar(b){}
char foobar();
};
}
#else
typedef struct pod pod;
#endif
#ifdef __cplusplus
extern "C"{
#endif
extern pod* pod_new(short f, char b);//BANG!!!
extern char pod_foobar(pod* p); //BANG!!!
#ifdef __cplusplus
}
#endif
I can't put the C linkage functions inside the namespace ns, or the C client won't find their definitions. When I pull them out of the namespace, the C++ definition won't compile either,all because of the pod solution, which I learned from C++ FAQ Lite,it's only a preprocessor trick. And sadly, this trick couldn't deal with namespaces.
What am I supposed to do? Should I throw out all type-safety, and replace pod* with void*, or is there a nicer solution for this kinda situation? Any advices? Please!
I'd just give two different declarations for the functions
for C++:
extern "C" ns::pod* pod_new(short f, char b);
extern "C" char pod_foobar(ns::pod* p);
and for C:
typedef struct pod pod;
extern pod* pod_new(short f, char b);
extern char pod_foobar(pod* p);
But if this doesn't satisfy you, for C++ you could also have a typedef
typedef ns::pod ns_pod;
for C
typedef struct ns_pod ns_pod;
and then have the same common function prototype.
extern ns_pod* pod_new(short f, char b);
extern char pod_foobar(ns_pod* p);
Edit: In C the struct pod or struct ns_pod is an incomplete type, so in C directly you could never do anything that uses the fields or asks for its size. The difference between a pointer to an incomplete type and a void* is that you can only assign such a struct pointer to another struct pointer of the same incomplete type.
typedef struct ns_pod2 pod2;
ns_pod* q = pod_new(...); // valid
ns_pod2* r = pod_new(...); // a constraint violation! (= compiler error)
The second one would need an explicit cast, if you want to insist. This is one of the reasons why casts are frowned upon by many C programmers.
I figured it out myself :) by checking the symbols of the obj files with nm.
It turns out that the C++ namespaces have no effects on functions with C linkage, therefore I can rewrite the code above like this:
#ifdef __cplusplus
namespace ns
{
class pod
{
const short foo;
const char bar;
public:
pod(short f,char b):foo(f),bar(b){}
char foobar();
};
}
#else
typedef struct pod pod;
#endif
#ifdef __cplusplus
namespace ns{
extern "C"{
#endif
pod* pod_new(short f, char b);
char pod_foobar(pod* p);
void pod_free(pod* p);
#ifdef __cplusplus
}
}
#endif
Related
I am writing my first DLL in C++. Using __declspec(dll_export), I am able to read it on Python and C++ using a C wrapper. But I want now to read it on C too, so I have to add now the __stdcall convention. But I don't know how to apply it to a typedef struct. For example:
Projet.h
#pragma once
#include "Projet_inc.h"
class Projet // before, class _declspec(dll_export) Projet
{
public:
Projet();
~Projet();
int multiply(int arg1, int arg2);
int result;
};
Projet_inc.h
#ifdef PROJET_EXPORTS
# define EXPORT __declspec(dllexport)
#else
# define EXPORT __declspec(dllimport)
#endif
#define CALLCONV_API __stdcall // before, this line didn't exist
extern "C" // C wrapper
{
typedef struct Projet Projet; // make the class opaque to the wrapper
Projet* EXPORT CALLCONV_API cCreateObject(void);
int EXPORT CALLCONV_API cMultiply(Projet* pDLLobject, int arg1, int arg2);
}
and Projet.cpp
#include "stdafx.h"
#include "Projet.h"
Projet::Projet() {}
Projet::~Projet() {}
int Projet::multiply(int arg1, int arg2) {
result = arg1 * arg2;
return result;
}
Projet* EXPORT CALLCONV_API cCreateObject(void)
{
return new Projet();
}
int EXPORT CALLCONV_API cMultiply(Projet* pDLLtest, int arg1, int arg2)
{
if (!pDLLtest)
return 0;
return pDLLtest->multiply(arg1, arg2);
}
On Visual Studio 2017, the compilation return (first lines) :
dir\projet_inc.h(11) : warning C4229: anachronisme utilisé : modificateurs de données ignorés
dir\projet_inc.h(13) :error C2059: erreur de syntaxe : '__declspec(dllimport)'
And MSDN told that for C2059 error, I have to check on the typedef struct first.
Export specifiers apply only to functions and variables. Calling convention specifiers apply only to functions. So type alias (C-style) should look like this:
typedef struct Projet_I2M Projet_I2M;
Export specification should be infront of declaration:
EXPORT Projet * CALLCONV_API cCreateObject(void);
You seem to intentionally export C interface so you should prevent C++ exceptions from crossing language boundary.
extern "C" is should be conditionally included:
#ifdef __cplusplus
extern "C"
{
#endif
#ifdef __cplusplus
}
#endif
assuming there is a C++ class defined as follow,
/* goo.h */
#ifdef __cplusplus
class goo{
public:
vector<int> a;
vector<int> b;
void calc();
};
#else
typedef struct goo goo;
#endif
#ifdef __cplusplus
extern "C" {
#if defined(__STDC__) || defined(__cplusplus)
void goo_calc(goo *);
#endif
#ifdef __cplusplus
}
#endif
then i want to implement goo::calc() in C, so i write following codes.
/* goo_cpp.cpp */
#include "goo.h"
void goo::calc(){
goo_calc(this);
}
/* goo_c.c */
#include "goo.h"
void goo_calc(goo *g){
/* do something with g->a and g->b */
}
but gcc says goo has no fields named a and b, so what's wrong?
UPDATE:
Supposing vector<int> a is replaced by int a[3], and vector<int> b is replaced by int b[3].
Is this correct to access goo->a and goo->b from C function?
You need to implement goo_calc in a C++ translation unit, with C language linkage.
// goo_cpp.cpp
extern "C" void goo_calc(goo *g){
/* do something with g->a and g->b */
}
No other way to use a C++ type other than in a C++ translation unit.
I'm trying to understand function declaration using typedefs.
What does this code do in C++?
typedef void fcn_t(void);
typedef void (*ptr_t)(void);
fcn_t f;
fcn_t *pf;
ptr_t pf2;
In my understanding:
fcn_t is the type of a function, and so the line with f is a function declaration (not a definition), and I could later define it like void f(void) { blabla(); bleble(); } just as if I had declared void f(void); instead of fcn_t f;;
fcn_t * is the type of a function pointer, and the line with pf is a pointer variable definition, and pf is default-initialized (assuming the code excerpt is from the global scope);
There is no difference between fcn_t* and ptr_t, thus everything I said about pf applies to pf2.
Did I get it right? Would any of the three declarations have its meaning changed if I marked them extern? What would change if the code was compiled as C instead of as C++?
Yes you are right on all three counts. The only thing that would change if you marked them extern are the function pointers. Function declarations are by default extern in C++.
Try and compile the following program
template <typename...>
struct WhichType;
typedef void fcn_t(void);
typedef void (*ptr_t)(void);
// constexpr auto one = WhichType<fcn_t>{};
// constexpr auto two = WhichType<fcn_t*>{};
// constexpr auto three = WhichType<ptr_t>{};
fcn_t f;
void f() {}
int main() {
f();
}
Uncommenting the commented lines will likely give you a compiler error that tells you what types the WhichType instance is being instantiated with, and as a result it should show you the exact types of all three things you asked about. It's a trick I picked up from Scott Meyers' book "Effective Modern C++".
To test whether the declarations are extern or not, write two simple implementation files, with one containing the definition of the variable
main.cpp
template <typename...>
struct WhichType;
typedef void fcn_t(void);
typedef void (*ptr_t)(void);
fcn_t f;
int main() {
f();
}
definition.cpp
void f() {}
and then compile, link and run definition.cpp and main.cpp (via g++ -std=c++14 definition.cpp main.cpp and then ./a.out). If the declaration was not extern then the compile would fail with an undefined symbol error.
Here is a simple illustration of these typedef
#include <stdio.h>
typedef void fcn_t(void);
typedef void (*ptr_t)(void);
fcn_t f;
fcn_t *pf;
ptr_t pf2;
void
f(void)
{
printf("I am void\n");
}
void
vSayHi(void)
{
printf( "Hi\n" );
}
int
main(void)
{
pf2 = vSayHi;
pf2();
pf = vSayHi;
pf();
f();
return 0;
}
OUTPUT:
Hi
Hi
I am void
I'm trying to call a function defined in a C file from my CPP code and I think I am having issues getting the correct namespace. When compiling I get the error: "Undefined reference to 'Get'".
My C header:
// c.h
#ifndef C_H
#define C_H
#ifdef __cplusplus
extern "C" {
#endif
typedef enum
{
VAL_A1,
VAL_A2
} TYPE_A;
typedef enum
{
VAL_B1,
VAL_B2
} TYPE_B;
typedef enum
{
VAL_C1,
VAL_C2
} TYPE_C;
typedef struct
{
TYPE_B b;
TYPE_C c;
} TYPE_D;
TYPE_A Get(TYPE_B b, TYPE_D *d);
#ifdef __cplusplus
}
#endif
#endif
And my CPP file:
// main.cpp
...
extern "C" {
#include "c.h"
}
...
namespace MyNamespace
{
...
MyClass::MyFunc()
{
TYPE_D d;
// None of these calls will compile
// Get(VAL_B1, &d);
// ::Get(VAL_B1, &d);
}
...
}
I have tried calling without namespace reference and also with the "root" namespace using "::" with no luck. Any help is appreciated. I've read through this which seems to clarify it but I don't really understand it:
using C++ with namespace in C
"Undefined reference" means that the function has been declared (in the header), but not defined. You'll need to define the function in a source file somewhere (presumably the C file you refer to), and make sure that is linked when you build the program.
First, let's note what that error means. An undefined reference at the linker stage means that the compiler is unable to find the instance of something. In this case, the implementation of a function.
Let's look at your code.. There are a few things missing that we need to add to make it compilable:
A definition for Get().
main()
The class definition for MyClass.
Once we added those three fixes, the code compiles without error.
extern "C" { extern "C" {
typedef enum {
VAL_A1,
VAL_A2
} TYPE_A;
typedef enum {
VAL_B1,
VAL_B2
} TYPE_B;
typedef enum {
VAL_C1,
VAL_C2
} TYPE_C;
typedef struct {
TYPE_B b;
TYPE_C c;
} TYPE_D;
TYPE_A Get(TYPE_B b, TYPE_D *d) {
return VAL_A1;
}
}}
namespace MyNamespace {
struct MyClass {
void MyFunc();
};
void MyClass::MyFunc() {
TYPE_D d;
Get(VAL_B1, &d);
::Get(VAL_B1, &d);
}
}
int main() {}
The definition of Get (not shown in the question) also needs to be enclosed in extern "C".
The main difference between C and C++ functions, in practice, is the way they are named in the executable format. C++ functions get "name mangling" treatment by the linker but C functions do not. The linker will see the C++ definition of Get and it will have no idea of its relation to the C declaration, even if they have the same signature.
main.c
#include "stackg.h"
int main()
{
return 0;
}
stackg.h
#ifndef STACKG_H
#define STACKG_H
#ifdef __cplusplus
extern "C" {
#endif
typedef struct stack_gt* stack_gt;
stack_gt stkg_init(
void* (*alloc)(const void* data, const int size),
void (*dealloc)(void* data),
void (*copy)(void* data_d, const void* data_s),
const int size
);
void stkg_free(stack_gt s);
int stkg_is_empty(stack_gt s);
int stkg_is_full(stack_gt s);
const int stkg_size(const stack_gt s);
void stkg_clear(stack_gt s);
int stkg_push(stack_gt s, const void* data);
int stkg_pop(stack_gt s, void* data);
int stkg_peek(stack_gt s, void* data);
#ifdef __cplusplus
}
#endif
#endif
The above program compiles successfully with the GCC compiler, but in MSVC2008 it gives the following error :
error C2040: 'stack_gt *' differs in levels of indirection from 'stack_gt'
What should I tell MSVC to make it compile the program without changing anything in the code?
Edit
Error occurs at line 8 of stackg.h :: typedef struct stack_gt* stack_gt;
Edit 2
If nothing else, I'll go with typedef struct _stack_gt* stack_gt;
The problem is that here:
typedef struct stack_gt* stack_gt;
you are giving stack_gt a different type, while this works fine:
typedef struct stack_gt* stack_gtB;
clang gives us a nicer error message:
error: typedef redefinition with different types ('struct stack_gt *' vs 'stack_gt')
This is covered in the draft C++ standard section 7.1.3 The typedef specifier paragraph 6:
In a given scope, a typedef specifier shall not be used to redefine the name of any type declared in that scope to refer to a different type. [ Example:
class complex { / ... / };
typedef int complex; // error: redefinition
—end example ]
Using the same name though is fine, so this would be ok:
typedef struct stack_gt stack_gt;
covered in paragraph 3:
In a given non-class scope, a typedef specifier can be used to redefine the name of any type declared in that scope to refer to the type to which it already refers. [ Example:
typedef struct s { / ... / } s;
typedef int I;
typedef int I;
typedef I I;
—end example ]
Another idea:
#ifdef __cplusplus
extern "C" {
typedef void * stack_gt
#else
typedef struct stack_gt* stack_gt;
#endif
This is ugly, but you don't need to rewrite any other part of the code, only this header included in C++ . It is only used as an opaque pointer in C++ anyways, and C doesn't notice.