Hello guys,
Ok here is the issue/roadblock. I am trying to call a method that is a member of a c++ class. I know I need to have a wrapper function to give C a pointer to the member function. I have created a shared header file <header.h> and 2 source files. <cSource.c> and <cppSource.cpp>. Then of course the main file written in c. <cMain.c>
If anyone could help me get this small example working so that I can experiment with the actual problem I'm trying to overcome it would be absolutely terrific.
Here is the code I have:
(the commands to compile and errors located at the end of post)
header.h -
/*Start CPP header information */
-
#ifndef HEADER_H
-
#define HEADER_H
-
-
#ifdef __cplusplus
-
class cClass
-
{
-
public:
-
-
void sayHello();
-
cClass (){};
-
};
-
extern "C" cClass* cSayHello(cClass* cclass);
-
#else
-
/*End CPP header information */
-
///////////////////////////////////
-
/*Start C Header Information */
-
typedef struct cClass {}cClass;
-
#endif
-
-
#ifdef __cplusplus
-
extern "C"
-
{
-
#endif
-
-
-
void cSpeak(struct cClass* cclass);
-
-
#ifdef __cplusplus
-
}
-
#endif
-
/* End C Header Information */
-
-
-
#endif //For Header.h define check.
-
-
cSource.c -
#include <stdio.h>
-
#include "header.h"
-
-
-
void cSpeak(cClass* cclass)
-
{
-
cSayHello(cclass);
-
}
-
-
cppSource.cpp -
#include <stdio.h>
-
#include "header.h"
-
-
void cClass::sayHello() //This is the function we are trying to run from C
-
{
-
printf ("This is how you say hello, using C that calls a C++ function");
-
}
-
-
extern "C" cClass* cSayHello(cClass* cclass) //This returns a pointer for the C function!
-
{
-
cclass->sayHello();
-
return cclass;
-
}
-
-
cMain.c -
//Includes
-
#include <stdio.h>
-
#include "header.h"
-
-
int main()
-
{
-
cClass cclass;
-
printf ("void cSpeak(cClass* cclass);");
-
cSpeak(&cclass);
-
return 0;
-
}
-
-
$>gcc -c cMain.c -o main.o
$>gcc -c cSource.c -o csource.o
$>g++ -c cppSource.cpp -o cppsource.o
$>gcc -o main.o csource.o cppsource.o //This gives the following error.
csource.o: In function `cSpeak':cSource.c:(.text+0xd): undefined reference to `cSayHello'
collect2: ld returned 1 exit status
Thanks for all the help in advance!
13 19961
First, cClass is a C++ class and there are no classes in C so you can't create a cClass object in main().
You can call any C++ functions from C but you will have to be sure all of the C++ functions are in C++ source files and compiled as C++.
Any C++ functions called from C have to be extern "C" to surn off the C++ mangler. Therefore, you lose function oveloading, too.
So, to call cClass::sayHello from C you would in the C main():
1) call cSpeak(). No arguments.
This would be an extern "C" C++ function.
2) cSpeak would create a cClass object on the heap and call sayHello on that object.
3) cSpeak would delete the cClass object and return
@weaknessforcats
Oh i guess I should have mentioned about the wrapper thing I was looking to do. From my internet journey I have discovered a way to do it, by creating a wrapper function that returns a pointer to the class method. Here is the link i found. Its about the third sentence down "Naturally...." I somewhat understand what is going on, however I'm confused about where certain pieces go. http://www.research.att.com/~bs/bs_faq2.html#callCpp
You want to be careful here.
This is from your referenced article:
extern "C" double call_C_f(C* p, int i) // wrapper function
{
return p->f(i);
}
This function takes a C* and the member function is called using that pointer.
This is exactly what I said to do in my earlier post except without the C* argument:
Now C::f() can be used like this:
/* C code: */
double call_C_f(struct C* p, int i);
void ccc(struct C* p, int i)
{
double d = call_C_f(p,i);
/* ... */
}
Here an assumption is made: Namely the struct C has the same address as a class C. It's based on the fact that classes in C++ are really implemented as structs. Personally, I would remove the assumption by using a struct C* in the C function and on the C++ side using a struct C rather than a class C.
If you specify public/private/protected for every struct member, then a struct and a class are identical.
I so not know if this assumption is part of the C++ standard or whether it is compiler-dependent. Every time I have done this the class and the struct work the same in C++.
But were I coding this, ccc() would look like this: -
void ccc(int arg)
-
{
-
wrapperfunction(arg); //a C++ function
-
}
-
where -
void wrapperfunction(int arg)
-
{
-
C* ptr = new C;
-
ptr->f(i);
-
delete C;
-
}
-
There's no need to pass a C* around onb the C side. You just stay in C++ until you can delete the C object.
That is, your C main() itself is just a wrapper for C++ and you should put all of your code in C++. Here's the C main(): -
int main()
-
{
-
cplusplusmain();
-
}
-
where cplusplusmain() wraps the entire C++ application: -
extern "C" int cplusplusmain()
-
{
-
the ActualApplication();
-
}
-
theActualApplication is your C++ main().
This is exactly what I said to do in my earlier post except without the C* argument:
My apologies, I must have overlooked that small detail. I am having more trouble with the layout of where things go than with the theory behind it all. This is the most in depth I've ever gone with C or C++ for that matter.
If I have a common header, I get very confused where to add the extern "C" and where not to.
Lets see if I can sum this up properly:
If I am careful I can use a c++ class and a C struct (only if it's assumed the address are the same and the class is designed to mimic a struct), or I have a c++ struct and a c struct.
Then in cMain I call a function that is an extern "C" cppMain(), which then does all the work such as creating a class accessing its members and doing what needs to be done.
Where i get confused is what files go where in this case. If i have cMain.c, csource.c, cppsource.cpp, and header.h
Sorry for being so clumsy at this, but I've been trying to get my head around this (syntactically) most of this week, and I've confused myself pretty bad. Sadly it wouldn't be so bad if the examples from the sites I've been looking at would show how to link all these files together, and where the corresponding functions go.
The only two sites I have are : http://www.parashift.com/c++-faq-lit....html#faq-32.8
and the one above.
Do you know of any other good resources for examples or information on what I'm trying to do?
I appreciate all your help weaknessforcats
Do not use a common header. C ande C++ are different languages and you will have problems on the C side tripping over C++ syntax and troubles on the C++ side because C syntax can work differently (or be an outright error) on the C++ side.
Instead, have a C header for the C side and a C++ header for the C++ side.
The extern "C" goes into the C++ header since that is C++-only syntax.
Then your C code is in a .c file and that code includes your C header.
Your C++ code is in a .cpp file and that code includes your C++ header.
You compile each source file and then link the object files together to create your final executable.
Usually, this consists of having the .c and the .cpp files members of the same project. Then when you build, the .c files are compiled as C code and the .cpp files are compiled as C++ code. The link step takes care of linking the object files together. (this is how Visual Studio.NET does it).
Lastly, remember, that at the object level everything is binary and all notion od C or C++ is lost. All you need to do is get your source code to compile using the correct language.
Thanks so much weaknessforcats. I'm going to try this code and I'll let you know how everything works out and if i need more help. I'll post the results (the files and possibly a make file) so that anyone who has this question can have it answered with a good example.
I believe it worked. Can you check this to make sure it is indeed C calling C++? It would be greatly appreciated.
cMain.c -
/* This is the C main file. This should also be compiled with a c compiler */
-
#include <stdio.h> // This is included because printf is used.
-
#include "cheader.h" // We are calling functions from the C source here. so we
-
//need the header for it.
-
-
int main()
-
{
-
printf ("About to run the c function.\n");
-
cSpeak(); // Call the C function to start everything.
-
return 0;
-
}
-
-
cheader.h -
// This is the header file for all the C functions.
-
//
-
-
//Function to initiate the C++ function.
-
-
void cSpeak();
-
-
cppheader.h -
//C++ Header, this is where all the C++ functions are prototyped.
-
//
-
-
// C++ class which holds the member function sayHello();
-
-
class myTest
-
{
-
public:
-
void sayHello();
-
myTest(){};
-
};
-
-
//Purpose of this function is to call sayHello
-
//This function is also, C compatible. (because we used extern "C")
-
extern "C" void sayHelloWrapper();
-
-
cppSource.cpp -
/* This is the c++ source file.*/
-
#include "cppheader.h" // Include the cppheader.h file
-
#include <stdio.h> // We need to include this in order to use printf.
-
-
//This is the function we are trying to run from C (it needs a wrapper)
-
void myTest::sayHello()
-
{
-
printf ("This is how you say hello, using C that calls a C++ function\n");
-
}
-
-
//This function is meant to call the class function. This is the wrapper.
-
extern "C" void sayHelloWrapper()
-
{
-
printf("inside wrapper about to run sayHello\n");
-
myTest* speak; // Create instance of class type
-
speak->sayHello(); // call sayHello()
-
-
}
-
-
/* This file needs to be compiled by the c++ compiler. ie: g++ */
-
cSource.c -
/* This file is small for readibility. */
-
#include "cheader.h" //Include the cheader.h file. It contains the cSpeak() protoype
-
#include <stdio.h> // We have to include this to use printf.
-
-
// C function designed to call a C++ function.
-
void cSpeak()
-
{
-
printf("Inside cspeak.. running wrapper.\n");
-
sayHelloWrapper();
-
}
-
-
/* This file needs to be compiled by the C compiler. ie gcc -c */
-
Makefile -
#This simple make file must be executed from within the directory that contains
-
#the source files, header files, and main files.
-
-
#linking of all the main .o files
-
run : cMain.o cppSource.o cSource.o
-
g++ -o run cMain.o cppSource.o cSource.o
-
-
# c files need to be compiles with gcc
-
cMain.o : cMain.c
-
gcc -c cMain.c
-
-
cSource.o : cSource.c
-
gcc -c cSource.c
-
-
# cpp files need to be compiled with g++
-
cppSource.o : cppSource.cpp
-
g++ -c cppSource.cpp
-
# this allows you to remove all the .o files from the directory in which you compiled
-
# simply issue the command <make clean> and it will perform this action.
-
clean:
-
rm -f *.o
-
Hopefully this helps everyone. Thanks again weaknessforcats, i appreciate it very much.
Plop all the files in a directory, and then run the make file. That should do it.
OK, so i got the variable passing down. But I decided that I need to be able to pass multiple objects back and forth, so I decided to use a structure.
In my CHeader file I have this: -
typedef struct returnData
-
{
-
/* This is the structure that gets changed for passing
-
* data back and forth from C and C++
-
*/
-
char* txtResult;
-
int intResult;
-
int intMultiResult[50];
-
int resultToGet;
-
};
-
struct returnData* cData;
-
-
void getMachineInfoC();
-
-
in my CppHeader.h file i have this: -
typedef struct returnData
-
{
-
/* This is the structure that gets changed for passing
-
* data back and forth from C and C++
-
*/
-
char* txtResult;
-
int intResult;
-
int intMultiResult[50];
-
int resultToGet;
-
};
-
struct returnData* cppData;
-
-
extern "C" void getMachineInfoCpp(returnData* cppData, char* machineName);
-
-
Now, what i want to do is essentially pass the address from the C function to the C++ function. Do what I gotta do in C++ then output all the stuff using C
C Function Description -
void getMachineInfoC()
-
{
-
-
char machineName[50];
-
printf ("Enter the name of the machine: ");
-
fgets(machineName, 50, stdin);
-
rmNewline(machineName, 50);
-
(*cData).intResult = 10; //This causes a segfault
-
-
getMachineInfoCpp(&cData, &machineName[0]);
-
-
//These print statements don't work either
-
printf ("Error Status: %d\n", cData->intMultiResult[0]);
-
printf ("Machine ID: %d\n", cData->intMultiResult[1]);
-
printf ("MiltID: %d\n", cData->intMultiResult[2]);
-
}
-
-
CPP Function Description: -
extern "C" void getMachineInfoCpp(returnData* cppData , char* machineName)
-
{
-
-
string query;
-
string query1 = "select * from sp_getmachineid('";
-
string query2 = (string)machineName;
-
string query3 = "');";
-
query = query1 + query2 + query3;
-
cout << query << endl;
-
startHandling(1);
-
-
initConnection();
-
-
icConnection.dbStatement->Execute(query);
-
-
while (icConnection.dbStatement->Fetch())
-
{
-
(icConnection.dbStatement)->Get(1, cppData->intMultiResult[0]);
-
(icConnection.dbStatement)->Get(2, cppData->intMultiResult[1]);
-
(icConnection.dbStatement)->Get(3, cppData->intMultiResult[2]);
-
}
-
startHandling(0);
-
printf ("intResult %d", cppData->intResult);
-
printf("qResult 1: %d\n 2: %d\n 3: %d\n",
-
cppData->intMultiResult[0],
-
cppData->intMultiResult[1],
-
cppData->intMultiResult[2]);
-
}
-
Thank you all for your help. I know I'm probably just doing my pointer stuff incorrectly.
This code: - (*cData).intResult = 10; //This causes a segfault
should be written as:
That's a matter of style but is not your problem.
Your problem is that cData is a returnData* that does not point to a returnData variable. That is, it an uninitialized pointer.
Ahh, got it. THX!!!
But with pointers I have another concept. I tried a bunch of diferent methods but nothing i seem to try works to convert the value of qresult to qresultptr -
char qResult[25];
-
char* qResultptr;
-
-
//get a value for qResult[]
-
//lets say qResult="superman"
-
-
qResultptr=qResult; // this doesn't work. and strcpy segfaults.
-
It works for me: - int main()
-
{
-
char qResult[25];
-
char* qResultptr;
-
-
//get a value for qResult[]
-
//lets say qResult="superman"
-
-
qResultptr=qResult; // this doesn't work. and strcpy segfaults
-
strcpy(qResultptr, "superman");
-
cout << qResultptr << '\n'
-
<< qResult << endl;
-
}
Of course, this can't work:
because the name of an array is the address of element 0 and this code attempts to change the address of qResult[0] (a local variable) and this you cannot do.
Ahh, you know I'm beginning to get the hang of all this complex (complex to me anyway) pointer logic. Thank you so much weaknessforcats.
I've got another question yet again. A tad more complex for me this time probably not for you.
OK so i call getDBVersionCpp from C and give it the pointer to an array of structures. Everything works fabulous. Here's the catch though. Once the aryResults is returned by the getDBVersionCpp, and I go to do the printf's, I get the correct value of gobble gobble from myResults[1].textResults[1] however, instead of getting 5.8.10 for the db version i get a strange symbol.
Everything on the CPP side prints out just as it should. The comments will show what it prints out.
Can anyone explain to me why this is happening?
cheader.h -
typedef struct
-
{
-
char* textResults[1024];
-
int intResults[1024];
-
int exitStatus;
-
}results;
-
results cResults[10];
-
results* ptrResults;
-
void rmNewline(char*, int); // Function to remove the newline from the user's input.
-
void getDBVersionC(); // Function to show the database version.
-
cSource.c -
void getDBVersionC()
-
{
-
ptrResults = cResults;
-
results* myResults;
-
myResults = getDBVersionCpp(ptrResults);
-
printf("\n");
-
printf("Database Version: %s\n", myResults[1].textResults[2]);
-
//prints ----- Database Version: <some symbol> (wtf?)
-
printf("What is gibberish: %s\n", myResults[1].textResults[1]);
-
//prints ----- What is gibberish: gobble gobble
-
getchar();
-
}
-
cppheader.h -
#include "cheader.h"
-
extern "C"
-
{
-
results* getDBVersionCpp(results* aryResults);
-
}
-
-
cppSource.cpp -
extern "C"
-
{
-
results* getDBVersionCpp(results* aryResults)
-
{ /* Function for getting the version of the database */
-
char qResult[25];
-
char* ptrTextResult;
-
char* gobble = "gobblegobble";
-
std::string query = "select cast(versionstring as varchar(25)) from version;";
-
-
//startHandling(1);
-
initConnection();
-
icConnection.dbStatement->Execute(query);
-
-
while (icConnection.dbStatement->Fetch())
-
{
-
(icConnection.dbStatement)->Get(1, qResult);
-
}
-
//startHandling(0);
-
cout << "*****************************************************"<<endl;
-
cout << "qResult: " << qResult << endl;
-
//prints ----- qResult: 5.8.10 (works great)
-
ptrTextResult = qResult;
-
cout << "ptrResult: " << ptrTextResult << endl;
-
//prints ------ ptrResult: 5.8.10 (works great)
-
cout << "*****************************************************"<<endl;
-
aryResults[1].textResults[1] = gobble;
-
aryResults[1].textResults[2] = ptrTextResult;
-
cout << "CPP: cResults[1].textResults[2]: " << aryResults[1].textResults[2] << endl;
-
// prints CPP cResults[1].textResults[2]: 5.8.10 (works)
-
cout << "CPP: cResults[1].textResults[1]: " << aryResults[1].textResults[1] << endl;
-
// prints CPP: cResults[1].textResults[1]: gobble gobble (works)
-
cout << "*****************************************************"<<endl;
-
closeConnection();
-
-
return (aryResults);
-
}
-
}// End Extern "C"
-
typedef struct
{
char* textResults[1024];
int intResults[1024];
int exitStatus;
}results;
results cResults[10];
results* ptrResults;
This code says results is a type and that type is another name for that struct.
That's OK.
Next you create an array of results banking on that typedef.
That's OK.
Next you create a results* banking on that typedef.
That's OK.
Now to include this header again on the C++ side thereby creating ANOTHER cResults array and ptrResults pointer.
That's NOT OK.
You never put code in a header file that allocates memory. When you do you get new variables every time you include that header.
So, remove - results cResults[10];
-
results* ptrResults;
from your header file and make them variables in your C main().
Change your function prototypes to have a result* argument. You call the function using the name of the array, cResults.
You don't need ptrResults.
Your function does not need to return a results* since you now pass it in.
Your C main() should look like: - int main()
-
{
-
results cResults[10];
-
getDBVersionC(cResults);
-
}
Post your reply Sign in to post your reply or Sign up for a free account.
Similar topics
reply
views
Thread by George Vodpik |
last post: by
|
2 posts
views
Thread by Jan |
last post: by
|
7 posts
views
Thread by JJ |
last post: by
|
5 posts
views
Thread by Nick Flandry |
last post: by
|
5 posts
views
Thread by huzz |
last post: by
|
1 post
views
Thread by Rik |
last post: by
|
4 posts
views
Thread by ssg31415926 |
last post: by
|
6 posts
views
Thread by Sagari |
last post: by
| | | | | | | | | | |