I am now writing wrappers for C++ functions, such that they can be used from C code.
我现在正在为C ++函数编写包装器,以便可以从C代码中使用它们。
The idea is to compile the cpp files using g++ and the c files using gcc, then link them together (!), but exposing ONLY those functions that are needed, to the C programs, by making them available in a header file 'test.h' (or maybe test.hpp?), like so:
我们的想法是使用g ++编译cpp文件和使用gcc编译c文件,然后将它们链接在一起(!),但只将那些需要的函数暴露给C程序,使它们在头文件'test中可用。 h'(或者可能是test.hpp?),如下:
(Note how I do not expose function 'vector Tokenize(const string& str,const string& delimiters)')
(注意我不公开函数'vector Tokenize(const string&str,const string&delimiters)')
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;
}
Do you notice something wrong?
你注意到了什么问题吗?
For starters, when I ran this program, I got "Unsatisfied code symbol '__gxx_personality_v0'"
对于初学者来说,当我运行这个程序时,我得到了“不满意的代码符号'__gxx_personality_v0'”
Thankfully, there is something here : What is __gxx_personality_v0 for?
值得庆幸的是,这里有一些东西:__gxx_personality_v0是什么?
Once I ran g++ with options " -fno-exceptions -fno-rtti", the output now fails with "Unsatisfied data symbol '_ZNSs4_Rep20_S_empty_rep_storageE'"
一旦我用选项“-fno-exceptions -fno-rtti”运行g ++,输出现在失败并显示“不满意的数据符号'_ZNSs4_Rep20_S_empty_rep_storageE'”
Ofcourse, the two environments (the one to compile - HP-UX B.11.23 ia64 and the one to run the binary on - HP-UX B.11.31 ia64) have different library versions (but same architecture), and this should not be a reason for the errors.
当然,两个环境(编译的那个 - HP-UX B.11.23 ia64和运行二进制文件的环境 - HP-UX B.11.31 ia64)具有不同的库版本(但是相同的架构),这不应该是错误的原因。
I would also like to test out the case marked by "// TODO : HOW to test this scenario?", but that can wait for now.
我还要测试标记为“// TODO:如何测试这种情况?”的案例,但现在可以等待了。
Any pointers?
3 个解决方案
#1
The easiest way to avoid undefined symbols while linking is to link with g++ (not gcc). You can still compile your .c file with gcc, though.
链接时避免未定义符号的最简单方法是链接g ++(而不是gcc)。不过,您仍然可以使用gcc编译.c文件。
Also please use system at a time. The link error may go away if you run all your gcc and g++ commands on the same system (no matter the old or the new one).
另外请一次使用系统。如果在同一系统上运行所有gcc和g ++命令(无论旧的还是新的),链接错误可能会消失。
#2
To call a C++ function from C, you can't have mangled names. Remove the conditional test for __cplusplus
where you do the extern "C"
. Even though your functions will be compiled by a C++ compiler, using extern "C"
will cause it to avoid name-mangling.
要从C调用C ++函数,您不能使用损坏的名称。删除__cplusplus的条件测试,执行extern“C”。即使您的函数将由C ++编译器编译,使用extern“C”也会导致它避免名称错误。
Here is an example:
这是一个例子:
The C file.
C文件。
/* a.c */
#include "test.h"
void call_cpp(void)
{
cpp_func();
}
int main(void)
{
call_cpp();
return 0;
}
The header file.
头文件。
/* test.h */
#ifndef TEST_H
#define TEST_H
extern "C" void cpp_func(void);
#endif
The CPP file.
CPP文件。
// test.cpp
#include <iostream>
#include "test.h"
extern "C" void cpp_func(void)
{
std::cout << "cpp_func" << std::endl;
}
The compiler command line.
编译器命令行。
g++ a.c test.cpp
#3
Is there a reason why you haven't considered modifying Swig to do just this? I seem to remember that there is a developmental branch of Swig to do just this...
有没有理由为什么你没有考虑修改Swig这样做呢?我似乎记得Swig有一个发展分支来做这个...
#1
The easiest way to avoid undefined symbols while linking is to link with g++ (not gcc). You can still compile your .c file with gcc, though.
链接时避免未定义符号的最简单方法是链接g ++(而不是gcc)。不过,您仍然可以使用gcc编译.c文件。
Also please use system at a time. The link error may go away if you run all your gcc and g++ commands on the same system (no matter the old or the new one).
另外请一次使用系统。如果在同一系统上运行所有gcc和g ++命令(无论旧的还是新的),链接错误可能会消失。
#2
To call a C++ function from C, you can't have mangled names. Remove the conditional test for __cplusplus
where you do the extern "C"
. Even though your functions will be compiled by a C++ compiler, using extern "C"
will cause it to avoid name-mangling.
要从C调用C ++函数,您不能使用损坏的名称。删除__cplusplus的条件测试,执行extern“C”。即使您的函数将由C ++编译器编译,使用extern“C”也会导致它避免名称错误。
Here is an example:
这是一个例子:
The C file.
C文件。
/* a.c */
#include "test.h"
void call_cpp(void)
{
cpp_func();
}
int main(void)
{
call_cpp();
return 0;
}
The header file.
头文件。
/* test.h */
#ifndef TEST_H
#define TEST_H
extern "C" void cpp_func(void);
#endif
The CPP file.
CPP文件。
// test.cpp
#include <iostream>
#include "test.h"
extern "C" void cpp_func(void)
{
std::cout << "cpp_func" << std::endl;
}
The compiler command line.
编译器命令行。
g++ a.c test.cpp
#3
Is there a reason why you haven't considered modifying Swig to do just this? I seem to remember that there is a developmental branch of Swig to do just this...
有没有理由为什么你没有考虑修改Swig这样做呢?我似乎记得Swig有一个发展分支来做这个...