Calls when writing wrappers for C ++ functions so that they can be used from C code
I am now writing wrappers for C ++ functions so that they can be used from C code.
The idea is to compile cpp files with g ++ and c files with gcc and then link them together (!), But expose ONLY the functions needed by C programs by making them available in the header file 'test.h '(or maybe test.hpp?), for example:
(Note how I don't expose the 'vector Tokenize (const string & str, const string & delimiters)' function)
test.h:
/* Header can be read by both C+ and C compilers, just the way we want! */
#ifndef TEST_H
#define TEST_H
#ifdef __cplusplus
extern "C" {
#endif
#if defined(__STDC__) || defined(__cplusplus)
extern int TokenizeC(const char* text, const char* delim, char ***output); /* ANSI C prototypes */
extern void reclaim2D(char ***store, unsigned int itemCount);
#endif
#ifdef __cplusplus
}
#endif
#endif /* TEST_H */
test.cpp:
#include <string>
#include <iostream>
#include <vector>
#include <assert.h>
#include "test.h"
using namespace std;
vector<string> Tokenize(const string& str,const string& delimiters)
{
vector<string> tokens;
string::size_type delimPos = 0, tokenPos = 0, pos = 0;
if(str.length() < 1) return tokens;
while(1)
{
delimPos = str.find_first_of(delimiters, pos);
tokenPos = str.find_first_not_of(delimiters, pos);
if(string::npos != delimPos)
{
if(string::npos != tokenPos)
{
if(tokenPos < delimPos) tokens.push_back(str.substr(pos,delimPos-pos));
else tokens.push_back("");
}
else tokens.push_back("");
pos = delimPos + 1;
}
else
{
if(string::npos != tokenPos) tokens.push_back(str.substr(pos));
else tokens.push_back("");
break;
}
}
return tokens;
}
int TokenizeC(const char* text, const char* delim, char ***output)
{
if((*output) != NULL) return -1; /* I will allocate my own storage, and no one tells me how much. Free using reclaim2D */
vector<string> s = Tokenize(text, delim);
// There will always be a trailing element, that will be blank as we keep a trailing delimiter (correcting this issue would not be worth the time, so this is a quick workaround)
assert(s.back().length() == 0); // This will be nop'ed in release build
s.pop_back();
(*output) = (char **)malloc(s.size() * sizeof(char *));
for(vector <string>::size_type x = 0; x < s.size(); x++)
{
(*output)[x] = strdup(s[x].c_str());
if(NULL == (*output)[x])
{
// Woops! Undo all
// TODO : HOW to test this scenario?
for(--x; x >= 0; --x)
{
free((*output)[x]);
(*output)[x] = NULL;
}
return -2;
}
}
return x; /* Return the number of tokens if sucessful */
}
void reclaim2D(char ***store, unsigned int itemCount)
{
for (int x = 0; itemCount < itemCount; ++x)
{
free((*store)[x]);
(*store)[x] = NULL;
}
free((*store));
(*store) = NULL;
}
poc.c:
#include <stdio.h>
#include "test.h"
int main()
{
const char *text = "-2--4--6-7-8-9-10-11-", *delim = "-";
char **output = NULL;
int c = TokenizeC(text, delim, &output);
printf("[*]%d\n", c);
for (int x = 0; x < c; ++x)
{
printf("[*]%s\n", output[x]);
}
reclaim2D(&output, c);
return 0;
}
Have you noticed something wrong?
For starters, when I ran this program, I got "Unsatisfied character code" __gxx_personality_v0 '"
Luckily, there is something here: What is __gxx_personality_v0 for?
As soon as I run g ++ with "-fno-exceptions -fno-rtti" options, now the result fails with "Unsatisfied data symbol" _ZNSs4_Rep20_S_empty_rep_storageE "
Of course, the two environments (one to compile - HP-UX B.11.23 ia64 and one to run binary - HP-UX B.11.31 ia64) have different versions of the libraries (but the same architecture) and this should not be the reason mistakes.
I would also like to test the case marked as "// TODO: HOW to test this script?", But that might wait.
Any pointers?
a source to share
The easiest way to escape the undefined character when linking is to link with g ++ (not gcc). You can still compile your .c file with gcc.
Also use the system at the same time. The link error can go away if you run all your gcc and g ++ commands on the same system (no matter old or new).
a source to share
To call a C ++ function from C, you cannot have malformed names. Remove the conditional test for __cplusplus
where you are running extern "C"
. Even though your functions will be compiled by the C ++ compiler, using it extern "C"
will result in it avoiding name manipulation.
Here's an example:
File C.
/* a.c */
#include "test.h"
void call_cpp(void)
{
cpp_func();
}
int main(void)
{
call_cpp();
return 0;
}
Header file.
/* test.h */
#ifndef TEST_H
#define TEST_H
extern "C" void cpp_func(void);
#endif
CPP file.
// test.cpp
#include <iostream>
#include "test.h"
extern "C" void cpp_func(void)
{
std::cout << "cpp_func" << std::endl;
}
Compiler command line.
g++ a.c test.cpp
a source to share