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.
Related
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
I need to build an executable which loads the dll # runtime and call the c++ function inside the dll with multiple parameters. I have seen plenty of examples which tells me about how to pass a parameter, but in my case it not exactly the same way. I need to pass 3 parameters.
Struct with 3 std::string elements
Struct pointer with std::map and std::string, and the map has std::string as key and structure as value
Struct with std::unordered_map which also has std::string as key and std::string as value, some bool, integer elements.
The dll class is derived from 2 base classes. ex: BaseClassA --> BaseClassB --> DerivedClassA
I have created the factory function and made all the __dllspec(dllexport) correctly but when I try to create DerivedClassA instance I got exceptions
Unhandled exception at 0x759FA6F2 in rte.exe: Microsoft C++ exception: std::length_error at memory location 0x016FF1C4
My Code Snippet (dllhelp.h):
#include <unordered_map>
#include <cinttypes>
typedef uint32_t ID;
typedef ID EVENT_UID;
typedef struct {
EVENT_UID m_unEventID;
uint8_t *m_ptrMemAddr;
size_t m_dataSize;
}stEvent_t;
typedef struct {
uint32_t m_unOffset;
uint32_t m_unArraySize;
std::string m_acTagName;
std::string m_acTagValue;
std::string m_acDataType;
} stTagElem_t;
typedef std::unordered_map<uint8_t, stTagElem_t> tagsList_t;
typedef struct {
EVENT_UID m_unBindedEvent;
uint16_t m_unMemSize;
tagsList_t m_stTagsList;
std::string m_acMapTypeName;
} stElem_t;
typedef std::unordered_map<std::string, stElem_t> ElemList_t;
typedef struct {
std::string m_acAppBlocName;
std::string m_acInstanceName;
std::string m_acDataMapInstanceName;
std::string m_acTypeName;
} stBlock_t;
typedef std::unordered_map<std::string, std::string > instanceTypeList_t;
typedef struct {
bool m_bIsItOutput;
uint8_t m_unIdxToEvtClient;
EVENT_UID m_acBindedEvent;
instanceTypeList_t m_acParamsList;
} stParams_t;
#ifdef EXPORTING_EXTN
#define RTE_EXTN __declspec(dllexport)
#else
#define RTE_EXTN __declspec(dllimport)
#endif
class RTE_EXTN BaseA {
public:
BaseA(){}
virtual ~BaseA(){}
void rxdEvent(stEvent_t pa_stEvent){ onRxdEvent(pa_stEvent);}
protected:
virtual void onRxdEvent(stEvent_t pa_stEvent);
};
class RTE_EXTN BaseB : public BaseA {
public:
BaseB(stParams_t pa_stParams){;}
virtual ~BaseB(){}
void sendOutput(size_t dataSize);
virtual int initialise() {return -1;}
virtual void onRxdEvent(stEvent_t pa_stEvent) {};
stBlock_t m_stBlock;
stElem_t *m_stElem;
protected:
stEvent_t m_stEvent;
stParams_t m_stParams;
};
class RTE_EXTN DerivedClassA : public BaseB {
public:
DerivedClassA(stBlock_t pa_stBlock, stElem_t *pa_stElem, stParams_t pa_stParams) : BaseB(pa_stParams){
m_stElem = pa_stElem;
m_stBlock = pa_stBlock;
}
int initialise(){return 0;}
void onRxdEvent(stEvent_t pa_stEvent);
};
extern "C" {
RTE_EXTN BaseB* CreateDerivedObj(stBlock_t pa_stBlock, stElem_t *pa_stElem, stParams_t pa_stParams);
};
typedef BaseB* (*CreateDrvObjType_t) (stBlock_t pa_stBlock, stElem_t *pa_stElem, stParams_t pa_stParams);
My dllmain.cpp:
#include "dllhelp.h"
BaseA* CreateDerivedObject(stBlock_t pa_stBlock, stElem_t *pa_stElem, stParams_t pa_stParams)
{
return new DerivedClassA(pa_stBlock, pa_stElem, pa_stParams);
}
My main executable:
stBlock_t m_siInfo;
stElem_t *pa_stElem;
stParams_t m_siParams;
//the above structures are initialized and updated with valid data..
HINSTANCE hGetProcIDDLL_derived;
typedef BaseB* (*CreateDrvObjType_t) (stBlock_t pa_stBlock, stElem_t *pa_stElem, stParams_t pa_stParams);
hGetProcIDDLL_derived = LoadLibrary(TEXT("dllhelp.dll"));
CreateDrvObjType_t oExtension = reinterpret_cast<CreateDrvObjType_t>(GetProcAddress(hGetProcIDDLL_derived, "CreateDerivedObj"));
BaseB* paDrvObj = oExtension(m_siInfo,pa_stElem,m_siParams);
When trying debug mode, I am able to see all the structures were filled with valid elements. But while debugging on the dllmain.cpp, I wasn't able to see any valid data on the structures. Some points to null address, and some to invalid data and many say "Error reading character string".
Note: I don't use dllhelp.h file in my main executable. Rather I have declared all the structures in main file itself.
How do I initialize the member variables in the following code?
#include <string>
#include <iostream>
using namespace std;
int main()
{
typedef struct Employee{
char firstName[56];
char lastName[56];
};
typedef struct Company {
int id;
char title[256];
char summary[2048];
int numberOfEmployees;
Employee *employees;
};
typedef struct Companies{
Company *arr;
int numberOfCompanies;
};
}
You can add a constructor like this one:
struct Employee{
char firstName[56];
char lastName[56];
Employee() //name the function as the struct name, no return value.
{
firstName[0] = '\0';
lastName[0] = '\0';
}
};
As you are already use std::string as indicated from the #include statement, you should change your classes declarations as follows (and do so outside of main() functions body):
struct Employee {
std::string firstName;
std::string lastName;
};
typedef struct Company {
int id;
std::string title;
std::string summary;
int numberOfEmployees;
Employee *employees;
};
typedef struct Companies{
Company *arr;
int numberOfCompanies;
};
first: u use typedef in a wrong way
typedef struct Employee{
char firstName[56];
char lastName[56];
};
you need the other name for typedef
typedef struct _Employee{
char firstName[56];
char lastName[56];
}Employee;
but since u're using c++ typedef is not needed.
Second: don't declare structs inside the function. Declare them outside of main.
Third: use constructors to initialize members to default value, example:
struct Employee{
Employee()
{
strcpy (firstName, ""); //empty string
strcpy (lastName, "asdasd"); // some value, every object now by default has this value
}
char firstName[56];
char lastName[56];
};
fourth: use std::string class (#include) for easier string handling
fifth: consider to type class instead struct, the only difference between those two, is that class declares the variables as private by default(you can change visibility of variable urself), while struct declares them as public by default.
I cannot figure the syntax to declare a function pointer as a static member.
#include <iostream>
using namespace std;
class A
{
static void (*cb)(int a, char c);
};
void A::*cb = NULL;
int main()
{
}
g++ outputs the error "cannot declare pointer to `void' member". I assume I need to do something with parentheses but void A::(*cb) = NULL does not work either.
I introduced a typedef, which made it somewhat clearer in my opinion:
class A
{
typedef void (*FPTR)(int a, char c);
static FPTR cb;
};
A::FPTR A::cb = NULL;
void (*A::cb)(int a, char c) = NULL;
I'm searching for the correct syntax to pass a struct array to an unmanaged C++ dll.
my dll imports are called like this
#define _DllImport [DllImport("Controller.dll", CallingConvention = CallingConvention::Cdecl)] static
_DllImport bool _Validation(/* array of struct somehow */);
In my client code I have
List<MyStruct^> list;
MyObject::_Validation(/* list*/);
I know System::Runtime::InteropServices::Marshal has a lot of useful methods for doing stuff like this but I'm not sure about which to use.
Create a managed version of the unmanaged struct using StructLayout.Sequential (make sure to put things in the same order). You should then be able to pass it like you'd pass it to any managed function (e.g., Validation(MyStruct[] pStructs).
For example, let's say our native function has this prototype:
extern "C" {
STRUCTINTEROPTEST_API int fnStructInteropTest(MYSTRUCT *pStructs, int nItems);
}
and the native MYSTRUCT is defined as follows:
struct MYSTRUCT
{
int a;
int b;
char c;
};
Then in C#, you define a managed version of the struct as follows:
[System.Runtime.InteropServices.StructLayoutAttribute(System.Runtime.InteropServices.LayoutKind.Sequential)]
public struct MYSTRUCT
{
public int a;
public int b;
public byte c;
}
And the managed prototype as follows:
[System.Runtime.InteropServices.DllImportAttribute("StructInteropTest.dll", EntryPoint = "fnStructInteropTest")]
public static extern int fnStructInteropTest(MYSTRUCT[] pStructs, int nItems);
You can then call the function passing it an array of MYSTRUCT structs as follows:
static void Main(string[] args)
{
MYSTRUCT[] structs = new MYSTRUCT[5];
for (int i = 0; i < structs.Length; i++)
{
structs[i].a = i;
structs[i].b = i + structs.Length;
structs[i].c = (byte)(60 + i);
}
NativeMethods.fnStructInteropTest(structs, structs.Length);
Console.ReadLine();
}
You can use Marshall.StructureToPtr to get an IntPtr which could be passed into a native MyStruct* array.
However, I'm not sure how to do this from a List directly. I believe you need to convert this to an array and use a pin_ptr (to prevent the GC from moving your memory) prior to passing it to the native code.