我想为 C++ 框架编写一个包装器。这个框架有点错误,而且不是很好用 C++。所以我希望能够通过仅使用一个共享库从他们框架的外部(通过良好的旧 C 文件)调用他们的方法。这听起来像是需要一个包装器来封装所需的框架方法,以便与 C 而不是 C++ 一起使用。
到目前为止一切顺利....这是我已经做过的:
接口(interface) aldebaran.h (这是在我的包含文件夹中,超声波方法应该从框架外部调用):
#ifndef _ALDEBARAN_H
#define _ALDEBARAN_H
#ifdef __cplusplus
extern "C" {
#endif
void subscribe_ultrasound();
void unsubscribe_ultrasound();
float read_ultrasound();
#ifdef __cplusplus
}
#endif
#endif
现在是包装器:
cpp 文件 aldebaran.cpp:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "aldebaran.h"
#include "alproxy.h"
#include "../../include/aldebaran.h"
/*
* Ultrasound defines
*/
#define ULTRASOUND_RESERVATION_MAGIC "magic_foobar"
#define ULTRASOUND_POLL_TIME 250
#define ULTRASOUND_READ_ATTEMPTS 50
#define ULTRASOUND_SLEEP_TIME 20
using namespace std;
using namespace AL;
/*
* Framework proxies
*/
ALPtr<ALProxy> al_tts;
ALPtr<ALProxy> al_led;
ALPtr<ALProxy> al_motion;
ALPtr<ALProxy> al_mem;
ALPtr<ALProxy> al_us;
ALPtr<ALProxy> al_cam;
ALPtr<ALProxy> al_dcm;
/*
* Constructor
*/
Aldebaran::Aldebaran(ALPtr<ALBroker> pBroker, std::string pName): ALModule(pBroker, pName)
{
try {
al_tts = this->getParentBroker()->getProxy("ALTextToSpeech");
al_led = this->getParentBroker()->getProxy("ALLeds");
al_motion = this->getParentBroker()->getProxy("ALMotion");
al_mem = this->getParentBroker()->getProxy("ALMemory");
al_us = this->getParentBroker()->getProxy("ALUltraSound");
al_cam = this->getParentBroker()->getProxy("NaoCam");
al_dcm = this->getParentBroker()->getProxy("DCM");
}catch(ALError& err){
std::cout << "XXX: ERROR: " << err.toString() << std::endl;
return 1;
}
printf("XXX: module aldebaran initiated\n");
fflush(0);
}
/*
* Destructor
*/
Aldebaran::~Aldebaran()
{
printf("XXX: module aldebaran destructed\n");
fflush(0);
}
/*
* Subscribe to ultrasound module
*/
void subscribe_ultrasound()
{
ALValue param;
param.arrayPush(ULTRASOUND_POLL_TIME);
al_us->callVoid("subscribe", string(ULTRASOUND_RESERVATION_MAGIC), param);
printf("XXX: ultrasound subscribed: %s\n", ULTRASOUND_RESERVATION_MAGIC);
fflush(0);
}
/*
* Unsubscribe to ultrasound module
*/
void unsubscribe_ultrasound()
{
al_us->callVoid("unsubscribe", string(ULTRASOUND_RESERVATION_MAGIC));
printf("XXX: ultrasound unsubscribed: %s\n", ULTRASOUND_RESERVATION_MAGIC);
fflush(0);
}
/*
* Read from ultrasound module
*/
float read_ultrasound()
{
int i;
float val1, val2;
float val_sum;
ALValue distance;
val_sum = .0f;
for(i = 0; i < ULTRASOUND_READ_ATTEMPTS; ++i){
SleepMs(ULTRASOUND_SLEEP_TIME);
distance = al_mem->call<ALValue>("getData", string("extractors/alultrasound/distances"));
sscanf(distance.toString(AL::VerbosityMini).c_str(),"[%f, %f, \"object\"]", &val1, &val2);
val_sum += val1;
}
return val_sum / (1.f * ULTRASOUND_READ_ATTEMPTS);
}
aldebaran.cpp 的定义文件:
#ifndef ALDEBARAN_API_H
#define ALDEBARAN_API_H
#include <string>
#include "al_starter.h"
#include "alptr.h"
using namespace AL;
class Aldebaran : public AL::ALModule
{
public:
Aldebaran(ALPtr<ALBroker> pBroker, std::string pName);
virtual ~Aldebaran();
std::string version(){ return ALTOOLS_VERSION( ALDEBARAN ); };
bool innerTest(){ return true; };
};
#endif
所以这应该是我的包装器的一个简单示例,它可以很好地编译为 libaldebaran.so。
现在是我在 C 中的测试程序:
...现在我想从这样一个简单的 c 文件调用接口(interface) aldebaran.h 方法:
#include <stdio.h>
/*
* Begin your includes here...
*/
#include "../include/aldebaran.h"
/*
* End your includes here...
*/
#define TEST_OKAY 1
#define TEST_FAILED 0
#define TEST_NAME "test_libaldebaran"
unsigned int count_all = 0;
unsigned int count_ok = 0;
const char *__test_print(int x)
{
count_all++;
if(x == 1){
count_ok++;
return "ok";
}
return "failed";
}
/*
* Begin tests here...
*/
int test_subscribe_ultrasound()
{
subscribe_ultrasound();
return TEST_OKAY;
}
int test_unsubscribe_ultrasound()
{
unsubscribe_ultrasound();
return TEST_OKAY;
}
int test_read_ultrasound()
{
float i;
i = read_ultrasound();
return (i > .0f ? TEST_OKAY : TEST_FAILED);
}
/*
* Execute tests here...
*/
int main(int argc, char **argv)
{
printf("running test: %s\n\n", TEST_NAME);
printf("test_subscribe_ultrasound: \t %s\n", __test_print(test_subscribe_ultrasound()));
printf("test_read_ultrasound: \t %s\n", __test_print(test_read_ultrasound()));
printf("test_unsubscribe_ultrasound: \t %s\n", __test_print(test_unsubscribe_ultrasound()));
printf("test finished: %s has %u / %u tests passed\n\n", TEST_NAME, count_ok, count_all);
return (count_all - count_ok);
}
我怎样才能设法调用这些方法?我的意思是在我的 C 文件中我不可能创建这样一个对象实例(生成所有需要的 ALProxies),是吗?
帮助将不胜感激...谢谢
到目前为止非常感谢!
正如 xtofl 所说..我希望我的界面尽可能简单(最好不要使用另一个 C++ 对象):
#ifndef _ALDEBARAN_H
#define _ALDEBARAN_H
#ifdef __cplusplus
extern "C" {
#endif
void subscribe_ultrasound();
void unsubscribe_ultrasound();
float read_ultrasound();
#ifdef __cplusplus
}
#endif
#endif
这里的问题是像subscribe_ultrasound()这样的函数在没有所有代理实例化的情况下不能被调用...这是我们的先决条件:
...
al_tts = this->getParentBroker()->getProxy("ALTextToSpeech");
al_led = this->getParentBroker()->getProxy("ALLeds");
al_motion = this->getParentBroker()->getProxy("ALMotion");
al_mem = this->getParentBroker()->getProxy("ALMemory");
al_us = this->getParentBroker()->getProxy("ALUltraSound");
al_cam = this->getParentBroker()->getProxy("NaoCam");
al_dcm = this->getParentBroker()->getProxy("DCM");
...
如果我没有调用上面的代码,所有其他代码都会失败。
在他们的框架内,可以通过像这样调用的 python 脚本“自动加载”我的 libaldebaran.so:
myModule = ALProxy("Aldebaran", global_params.strRemoteIP, global_params.nRemotePort );
然后框架日志说:
May 10 15:02:44 Hunter user.notice root: XXX: module aldebaran initiated
May 10 15:02:46 Hunter user.notice root: INFO: Registering module : 'Aldebaran'
May 10 15:02:46 Hunter user.notice root: ______ End of loading libraries ______
这完全没问题...它调用了我的模块的构造函数(所以所有其他需要的代理也被实例化了)。
当然这个实例不属于我的C程序...
也许有可能将此共享给所有其他进程?
最佳答案
您可能想采用稍微不同的方法。为您的 C 接口(interface)考虑这样的事情:
#ifdef __cplusplus
extern "C" {
#endif
struct UltrasoundHandle;
UltrasoundHandle* ultrasound_Create();
void ultrasound_Destroy(UltrasoundHandle *self):
void ultrasound_Subscribe(UltrasoundHandle *self);
void ultrasound_Unsubscribe(UltrasoundHandle *self);
float ultrasound_Read(UltrasoundHandle *self);
#ifdef __cplusplus
}
#endif
UltrasoundHandle
结构是有意不透明的,因此您可以在实现中将其定义为您想要的任何形式。我所做的另一个修改是添加类似于构造函数和析构函数的显式创建和销毁方法。实现看起来像这样:
extern "C" {
struct UltrasoundHandle {
UltrasoundHandle() {
// do per instance initializations here
}
~UltrasoundHandle() {
// do per instance cleanup here
}
void subscribe() {
}
void unsubscribe() {
}
float read() {
}
};
static int HandleCounter = 0;
UltrasoundHandle* ultrasound_Create() {
try {
if (HandleCounter++ == 1) {
// perform global initializations here
}
return new UltrasoundHandle;
} catch (...) {
// log error
}
return NULL;
}
void ultrasound_Destroy(UltrasoundHandle *self) {
try {
delete self;
if (--HandleCounter == 0) {
// perform global teardown here
}
} catch (...) {
// log error
}
}
为 C 包装 C++ 接口(interface)的关键是通过自由函数公开 OO 概念,调用者显式地将对象指针 (this
) 传递给函数并显式公开构造函数和析构函数以相同的方式。包装器代码几乎可以从那里机械地生成。其他关键点是永远不要让异常向外传播并避开全局对象实例。我不确定后者是否会让您感到悲伤,但我会担心构建/销毁顺序问题。
关于c++ - 在 C 程序中调用 C++ 共享库...如何管理?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/845414/