I have some C code that has some structs that looks like this:
typedef struct my_library_a_t my_library_a_t;
typedef struct my_library_b_t my_library_b_t;
typedef struct my_library_c_t my_library_c_t;
struct my_library_a_t {
struct my_library_b_t {
int data;
struct my_library_c_t {
int data;
} c;
} b;
int data;
};
This doesn't work in C++, because in C struct my_library_b_t defines a global struct my_library_b_t, whereas in C++ it defines ::my_library_a_t::my_library_b_t.
How can I get the inner struct definition to define a global struct in C++? Or even just not have to change too much code for it to work (I don't mind having a #ifdef __cplusplus block, but I don't want to pull the structs out because the reason they are nested in the first place is that they are used only one time each, and it's really hard to read when the inner class definitions are above the outer class definitions)
I tried struct ::my_library_b_t in the definition, but this doesn't work.
For context, I'm parsing a string that has a definition that would look like this:
a = b "," data
b = data "," c
c = data
data = %x31-39 *DIGIT ; 1-9 followed by *(0-9)
/ "0"
And the intermediate parts have meaning, so it's useful to be able to have functions that take my_library_b_t* or my_library_c_t*.
I would prefer to have a solution that looks like this:
#ifdef __cplusplus
#define GLOBAL_STRUCT(name) ??? (I tried `:: name`)
extern "C" {
#else
#define GLOBAL_STRUCT(name) name
#endif
struct my_library_a_t {
struct GLOBAL_STRUCT(my_library_b_t) {
// ...
I recommend this
struct my_library_a_t {
struct my_library_b_t {
int data;
struct my_library_c_t {
int data;
} c;
} b;
int data;
};
#ifdef __cplusplus
typedef struct my_library_a_t::my_library_b_t my_library_b_t;
typedef struct my_library_b_t::my_library_c_t my_library_c_t;
#else
typedef struct my_library_a_t my_library_a_t;
typedef struct my_library_b_t my_library_b_t;
typedef struct my_library_c_t my_library_c_t;
#endif
Notice that after alias my_library_b_t, you don't need to use my_library_a_t::my_library_b_t::my_library_c_t
Related
I'm trying to do a C wrapper for a C++ third-party library because I need to use it in my C project.
I've seen examples of a C++ class wrapper but I don't undertood the process and I can't wrapper a C++ struct.
struct I want to wrapper:
struct confGlobal{
long int device;
string name;
int number;
string uid;
string message;
bool mode;
confiGlobal():device{LONG_MAX}, number{INT_MAX}{}
};
struct product{
string id;
string name;
};
struct category{
unsigned char id;
string name;
string description;
category():id{UCHAR_MAX}{}
};
struct subCategory{
unsigned char id;
string name;
string description;
unsigned char idRoot;
subCategory():id{UCHAR_MAX}, idRoot{UCHAR_MAX}{}
};
struct confPartner{
vector<struct product> tableProduct;
vector<struct category> tableCategory;
vector<struct subCategory> tableSubCategory;
};
For call to this method:
class my_API {
public:
static my_API* Instance(struct confGlobal cGlobal,
struct confPartner cPartner);
... another methods ...
private:
virtual ~my_API();
struct confGlobal cGlobal;
struct confPertner cPartner;
};
I need to fill this structs and call my_API::Instance() from C but my attempts have been unsuccessful.
wrapper.h
#ifdef __cplusplus
extern "C" {
#endif
struct confGlobal_wpr; // An opaque type that we'll use as a handle
typedef struct confGlobal_wpr confGlobal_wpr;
confGlobal_wpr *confGlobal_create(unsigned int device,
char *name,
int number,
char *uid,
char *message,
unsigned char mode);
struct product_wpr{
char id[4];
char name[30];
};
typedef struct product_wpr product_wpr;
struct category_wpr{
unsigned char id;
char name[3];
char description[30];
};
typedef struct category_wpr category_wpr;
struct subcategory_wpr{
unsigned char id;
char name[3];
char description[30];
unsigned char idRoot;
};
typedef struct subCategory_wpr subCategory_wpr;
struct confPartner_wpr; // An opaque type that we'll use as a handle
typedef struct confPartner_wpr confPartner_wpr;
confPartner_wpr *confPartner_create(Product_wpr tableProducts[],
unsigned char numProducts,
Category_wpr tableCategories[],
unsigned char numCategories,
SubCategory_wpr tableSubCategories[],
unsigned char numSubCategories);
struct my_api_wpr;
typedef struct my_api_wpr my_api_wpr;
my_api_wpr *my_api_create(struct confGlobal_wpr cGlobal,
struct confPartner_wpr cPartner);
#ifdef __cplusplus
}
#endif
wrapper.cpp
confGlobal_wpr *confGlobal_create(unsigned int device,
char *name,
int number,
char *uid,
char *message,
unsigned char mode)
{
confGlobal_wpr *cg;
struct confGlobal confiGlobal;
confiGlobal.name = name;
confiGlobal.device = device;
confiGlobal.number = number;
confiGlobal.uid = uid;
confiGlobal.message = message;
if (mode == 0)
confiGlobal.mode = false;
else
confiGlobal.mode = true;
return cg;
}
void confGlobal_destroy(confGlobal_wpr *cg)
{
if (cg == NULL)
return;
delete static_cast<confGlobal_wpr *>(cg->instance); // ERROR: invalid static_cast from type ‘confGlobal’ to type ‘confGlobal_wpr*’
free(cg);
}
confPartner_wpr *confPartner_create(product_wpr tableProducts_wpr[],
unsigned char numProducts,
category_wpr tableCategories_wpr[],
unsigned char numCategories,
subCategory_wpr tableSubCategories_wpr[],
unsigned char numSubCategories)
{
unsigned char i=0;
confPartner_wpr *cc;
struct confPartner cPartner;
vector< struct product> tableProduct;
vector< struct category> tableCategory;
vector< struct subCategory> tableSubCategory;
for (i=0; i<numProducts; i++)
{
struct product p;
p.id = tableProducts_wpr[i].id;
p.name = tableProducts_wpr[i].name;
tableProduct.push_back(p);
}
cPartner.tableProduct = tableProducts;
for (i=0; i<numCategories; i++)
{
struct category c;
c.id = tableCategories_wpr[i].id;
c.nombre = tableCategories_wpr[i].name;
c.descripcion = tableCategories_wpr[i].description;
tableCategory.push_back(c);
}
cPartner.tableCategory = tableCategory;
for (i=0; i<numSubCategories; i++)
{
struct subZona sc;
sc.id = tableSubCategories_wpr[i].id;
sc.name = tableSubCategories_wpr[i].name;
sc.description = tableSubCategories_wpr[i].description;
sc.idRoot = tableSubCategories_wpr[i].idRoot;
tableSubCategory.push_back(sc);
}
cPartner.tableSubCategory = tableSubCategory;
return cc;
}
my_api_wpr *my_api_create(struct confGlobal_wpr confiGlobal_wpr,
struct confPartner_wpr confiPartner_wpr)
{
my_api_wpr *my_api;
my_API *obj;
my_api = (typeof(my_api))malloc(sizeof(*my_api));
obj = my_API::Instance(confiGlobal_wpr, confiConsorcio_wpr);
/* With this compile and linked OK
confGlobal cg;
confPartner cc;
obj = my_API::Instance(cg, cc);
*/
my_api->obj = obj;
return my_api;
}
void my_api_destroy(ct_api_wpr *my_api)
{
if (my_api == NULL)
return;
delete static_cast<my_API *>(my_api->ptr_api); // ERROR: ‘virtual my_API::~my_API()’ is private within this context
free(my_api);
}
The output error when compile and linked with:
g++ -shared -o libwrapper.so *.cpp wrapper.h -l:libthird-party.a -L. -ldl -lrt -Wl,-rpath /usr/local/lib -lc
In function ‘my_api_wpr* my_api_create(confGlobal_wpr, confPartner_wpr)’:
error: no matching function for call to ‘my_API::Instance(confGlobal_wpr&, confPartner_wpr&)’
obj = my_API::Instance(confiGlobal_wpr, confiConsorcio_wpr);
^
my_API.h:30:20: note: candidate: static my_API* my_API::Instance(confGlobal, confPartner)
static my_API* Instance(struct confGlobal cGlobal, struct confiPartner cPartner);
^~~~~~~~
my_API.h:30:20: note: no known conversion for argument 1 from ‘confGlobal_wpr’ to ‘confGlobal’
You're forgetting that CT_API::Instance doesn't understand the "handle" types that you have created in C to wrap the C++ structures. This is precisely what the error message is telling you, if you read it. You must translate those back to the appropriate C++ types.
Firstly, since you are using "create"-style routines to build the structures and return them as a pointer, you should consider making your my_api_create function accept pointers instead. Particularly because the resulting handle types are forward-declared structs with no definition visible in C and it will not be possible for your C client to dereference them.
That highlights another issue. You are also not using these handles correctly from C++.
So, one thing at a time...
Your creation routine in C should be declared as:
my_api_wpr *my_api_create(struct confGlobal_wpr* cGlobal, struct confPartner_wpr* cPartner);
On the C++ side, you need to actually define your handle types. Something like:
extern "C" struct confGlobal_wpr {
struct confGlobal instance;
};
extern "C" struct confPartner_wpr {
struct confPartner instance;
};
extern "C" struct my_api_wpr {
my_API *ptr;
};
Now, your creation:
confGlobal_wpr *confGlobal_create(unsigned int device,
char *name,
int number,
char *uid,
char *message,
unsigned char mode)
{
confGlobal_wpr *cg = new confGlobal_wpr;
struct confGlobal& cGlobal = cg->instance; //<-- note the reference
// TODO: populate cGlobal as usual
return cg;
}
confPartner_wpr *confPartner_create(product_wpr tableProducts_wpr[],
unsigned char numProducts,
category_wpr tableCategories_wpr[],
unsigned char numCategories,
subCategory_wpr tableSubCategories_wpr[],
unsigned char numSubCategories)
{
confPartner_wpr *cc = new confPartner_wpr;
struct confPartner& cPartner = cc->instance; //<-- note the reference
// TODO: populate cPartner as usual
return cc;
}
my_api_wpr *my_api_create(struct confGlobal_wpr* cGlobal, struct confPartner_wpr* cPartner)
{
my_api_wpr *my_api = new my_api_wpr;
my_api->ptr = CT_API::Instance(cGlobal->instance, cPartner->instance);
return my_api;
}
You should also add corresponding _destroy methods for all the above.
To use C++ code in C project you need define wrapper functions with C calling convention - extern "C"(turning off C++ name mangling/decoration) , and call them and only them in your C project. Inside those C functions you can use C++ code. Pass to C wrapper functions only types that C understands. You can create intermediate structures for passing data to C wrapper functions. Then you need copy data to types that C++ class expects. In you particular case you incorrectly pass confGlobal_wpr wrapper struct but C++ method requires confGlobal, and compiler complains on this directly.
Below is observable snippet how to use C++ code from C code:
Foo.h
#include <string>
class Bar
{
public:
Bar(std::string s) : s_(s)
{
}
std::string s_;
};
class Foo
{
public:
Foo(Bar) {}
};
CWrappers.h // include this header to C project
struct BarWrapper
{
char data[100] = "";
};
#ifdef __cplusplus
extern "C" {
#endif
BarWrapper createFoo(char *c);
#ifdef __cplusplus
}
#endif
Wrapper.cpp
#include <algorithm>
#include "Foo.h"
#include "CWrappers.h"
// input and output to this C function should be understandable for C
BarWrapper createFoo(char *c)
{
// inside you can use C++
std::string data(c);
Bar bar(data);
Foo * foo = new Foo(bar);
BarWrapper barWrapper;
std::copy(data.begin(), data.end(), barWrapper.data);
return barWrapper; // pack data to C struct wrapper (that has no C++ specific)
}
C Project
#include "CWrappers.h"
int main()
{
BarWrapper barWrapper = createFoo((char *)"test");
}
Structs that contain anything other than primitive data types common to C and C++ largely can't be wrapped in the way you want. In this particular case, you have ::std::strings in your struct. And they definitely can't be accessed reasonably from C at all.
Additionally, the structs contain bool and I don't know if newer versions of the C standard have bool or define it in such a way that it will result in layout compatible structs with C++ implementations on the same platform.
There are solutions to this problem. But they involve using opaque pointers to the struct in C, and always calling functions to access its methods. I will try to whip up an example of how this might work for a really simple struct.
Looking more carefully at your code, it looks like you need a sort of thunk layer that takes the C struct and (in C++) hand-converts it to the C++ struct and returns a pointer to a dynamically allocated C++ struct that you can then pass to other C++ functions that have been exposed to C.
I'm trying to compile a simple header with some structs but I'm getting this error:
ppal.h:25:34: error: typedef ‘string_proc_func’ is initialized (use decltype instead)
typedef void (*string_proc_func)(string_proc_key*);
There are a bunch of other errors but I think this one is the one causing the others. My header looks like this:
#include <stdint.h>
using namespace std;
typedef struct string_proc_list_t
{
char* name;
struct string_proc_node_t* first;
struct string_proc_node_t* last;
} __attribute__((__packed__)) string_proc_list;
typedef struct string_proc_node_t
{
struct string_proc_node_t* next;
struct string_proc_node_t* previous;
string_proc_func f;
string_proc_func g;
string_proc_func_type type;
} __attribute__((__packed__)) string_proc_node;
typedef void (*string_proc_func)(string_proc_key*);
typedef enum string_proc_func_type_t
{
REVERSIBLE = 0,
IRREVERSIBLE = 1
} __attribute__((__packed__)) string_proc_func_type;
typedef struct string_proc_key_t
{
uint32_t length;
char* value;
} __attribute__((__packed__)) string_proc_key;
I looked for similar questions but I can't find how to fix this.
You are trying to use string_proc_key before its declaration.
Move the line with the error underneath the typedef struct ... string_proc_key;
I want to put constants in a struct, but compiler generates error that ";" is missing at the "=".
struct {
int aaa=111; // error: "expected ; at end of declaration list"
} blah;
Did you try maybe:
int aaa{111};
And if you need int as a constant you should probably include the const keyword.
const int aaa{111};
You can't initialize at the time of defining the structure in Obj-C. Initialization is possible at the time of creating instance shown as below.
struct Employee {
int idNumber;
int age;
};
// create instance
struct Employee emp1;
emp1.idNumber=12345;
emp1.age = 25;
If you're using Objective-C, in an .h file add something like:
extern const struct MyStruct {
int aaa;
} MyStruct;
In the .m file:
const struct MyStruct MyStruct = {
.aaa = 1
};
#import the .h file and use the struct in your code like this:
if (someInteger == MyStruct.aaa) ...
I have a class like this:
class TType {
public:
...
enum binary_type {
bt_a = 0,
bt_xyz,
....
bt_ak = 10,
....
};
}
and I use it in several places, also the enum:
if(var12 == TType::bt_a ) { ....
Now I imported a C library which has exactly the same enum (same keys, same values, same size) inside one of it's headerfiles:
typedef enum data_types_e {
bt_a = 0,
bt_xyz,
....
} data_types;
How can I define the enum in the c++ class definition to use the declaration of the c headerfile?
I want to continue using the enum the same way as before (TType::bt_a), and avoid copying the whole enum. Furthermore I don't wont to modify the library (otherwise a preprocessor-macro would do the trick) and I want changes made in the library also be made to the enum in my class.
Neither a typedef inside the c++ class definition nor a type alias (c++11) seem to work in this situation.
"How can I define the enum in the c++ class definition to use the declaration of the c headerfile?"
You can simply reuse the values from the c-style enum:
#include "TheOtherEnum.h"
...
enum binary_type {
bt_a = ::bt_a,
bt_xyz = ::bt_xyz,
....
bt_ak = ::bt_ak,
....
};
"Neither a typedef inside the c++ class definition nor a type alias (c++11) seem to work in this situation."
Yes these would work to provide the correct enum type, but you'll still need to qualify the values from the global namespace and not for nested to your class.
You can make C++ enum dependant of C enum:
typedef enum data_types_e {
bt_a = 0,
bt_xyz,
....
} data_types;
// ...
class TType {
public:
...
enum binary_type {
bt_a = bt_a,
bt_xyz = bt_xyz,
....
};
}
If possible, try renaming your class TType say, class TType_1.
//=============CLibraryFileContainingEnum.h=================
typedef enum data_types_e
{
bt_a = 9999,
bt_xyz
} data_types;
//==========================================================
//==========================================================
class TType_1
{
public:
enum binary_type
{
bt_a = 8878,
bt_xyz
};
};
namespace TType
{
#include "CLibraryFileContainingEnum.h"
}
int main()
{
int a = TType::bt_a; //this prints 9999
cout << a << endl;
return 0;
}
//==========================================================
Here is my answer, this is an old topic, but better lately than never
class TType {
public :
public:
...
#undef __cplusplus
#include "yourheaderc.h"
#define __cplusplus
}
maybe with a typedef binary_type data_types to preserve your nominations
But this solution is available if your header don't contains syntax C forbidden in C++. I'm currently searching a new solution because there are prototypes in my header that contains something like :
void afunction( unsigned long id,
enum T_TYPE type,
enum T_CHAR characteristic,
unsigned short number,
unsigned short value);
because T_TYPE and T_CHAR are not typedef-ed but this syntax is non-sense in C++ because it's the declaration syntax. So my solution is not appropriate if you are in a similar case.
When I make my own struct, say:
struct myStruct{
int data1;
int data2;
string data3;
}
I can initialize an instance of type myStruct like this:
myStruct instance1;
So my question is, why am I often seeing "struct" written during the initialization of a struct?
Maybe that's an inaccurate statement so here is an example of what I mean:
/*This is a tiny program that checks
to see if a file exists in
the current working directory. */
#include <sys/stat.h>
#include <iostream>
using namespace std;
const string FILENAME = data.txt;
int main(){
struct stat fileStatus; //<-- HERE HERE HERE!
if (FileExists(FILENAME, fileStatus)){
cout << "File Does Exist" << endl;
}else{
cout << "File Does NOT Exist" << endl;
}
return 0;
}
bool FileExists(const string & fileName,struct stat & fileStatus){
bool fileDoesNotExist = stat (fileName.c_str(), &fileStatus);
return !fileDoesNotExist;
}
>
LINE 13: struct stat fileStatus;
Is this something that was done in C for some reason?
Something with a macro or a typedef?
I just don't understand why this is the way it is.
This is a C thing; there's no good reason to continue to do it in C++.1
In C, struct is part of the typename, e.g.:
struct foo { int x; };
defines a type called struct foo. In C++, it defines a type called foo. In C, you can usually hide this irritation behind a typedef:
typedef struct foo { int x; } foo;
1 At least, not in code that couldn't possibly also be compiled as C (such as the example in your question).
You can do what you want by instead calling it like this:
typedef struct mystruct
{
int itema;
int itemb;
Etc...
}* mystruct;
So that's whenever you make a mystruct item it creates a pointer to your struct, else you have to call your struct by
struct mystruct *object;