在C程序中调用C++ Shared Lib ...如何管理?

1 c c++

我想为C++框架编写一个包装器.这个框架有点儿错误,在C++中并不是很好.所以我希望能够通过只使用一个共享库从外部(通过旧的C文件)调用他们的方法.这听起来像需要一个封装器,它封装了所需的框架方法,用于C而不是C++.

到目前为止一直很好......这就是我已经做过的事情:

接口aldebaran.h (这是在我的include文件夹中,超声方法应该从框架外部调用):

#ifndef _ALDEBARAN_H
#define _ALDEBARAN_H

#ifdef __cplusplus
 extern "C" {
#endif

void subscribe_ultrasound();
void unsubscribe_ultrasound();
float read_ultrasound();

#ifdef __cplusplus
}
#endif

#endif
Run Code Online (Sandbox Code Playgroud)

现在的包装器:

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);
}
Run Code Online (Sandbox Code Playgroud)

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
Run Code Online (Sandbox Code Playgroud)

所以这应该是我的包装器的一个简单示例,它可以很好地编译为libaldebaran.so.

现在我的C测试程序:

...现在我想从一个简单的c文件调用接口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);
}
Run Code Online (Sandbox Code Playgroud)

我该如何设法调用这些方法?我的意思是在我的C文件中我没有可能创建这样的对象实例(生成所有需要的ALProxies),有吗?

帮助真的很感激... thx


非常感谢你到目前为止!

正如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
Run Code Online (Sandbox Code Playgroud)

问题在于,如果没有所有代理的实例化,就无法调用像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");
...
Run Code Online (Sandbox Code Playgroud)

如果我没有调用上面的代码,那么其他所有代码都将失败.

在他们的框架内,可以通过像这个调用的python脚本"自动加载"我的libaldebaran.so:

myModule = ALProxy("Aldebaran",  global_params.strRemoteIP, global_params.nRemotePort );
Run Code Online (Sandbox Code Playgroud)

框架日志然后说:

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 ______
Run Code Online (Sandbox Code Playgroud)

这完全没问题......它调用了我的模块的构造函数(因此所有其他需要的代理也得到了实例化).

但当然这个实例不属于我的C程序......

也许有可能将此分享给所有其他流程?

D.S*_*ley 6

您可能希望采用略有不同的方法.考虑一下这样的C接口:

#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
Run Code Online (Sandbox Code Playgroud)

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
    }
}
Run Code Online (Sandbox Code Playgroud)

关键是为C包装C++接口是通过自由函数公开OO概念,其中调用者将对象指针(this)显式传递给函数,并以相同的方式显式地公开构造函数和析构函数.包装器代码几乎可以从那里机械地生成.其他关键点是,您永远不会让异常向外传播并避开全局对象实例.我不确定后者是否会让你感到悲伤,但我会担心施工/破坏订购问题.