好友
阅读权限10
听众
最后登录1970-1-1
|
今天蹲茅草坑没有玩手机,后悔怎么就那么急,怎么就忘记拿手机了,,,,
看着一堆杂草胡思乱想,突发奇想:C++能使用抽象类访问动态库里里面的对象,那C怎么使用C++的类.
emmmmm
回到房间,瞎倒腾了一番,发现C++的类方法指针长度跟C的函数指针不一样长!!!而且还各种不兼容~~
恩??!!这闹哪样,我孤陋寡闻了,然后就去网上各种搜索,然后发现可以通过汇编的方式暴力获取地址但是兼容性不行,可读性也不行!
当然,如果你是大神.....请首小弟一拜....
经过胡乱倒腾了一番,终于,ok了!
下面简单讲一下思路吧。
当然,这个获取类方法指针的方法目前行不通,那么还有一个方法,用友元函数。
众所周知,友元函数有权限访问类的所有成员,而且它的指针长度跟C的一样,经过测试,传值,调用并且获取返回值没问题,因为这只是个普通的函数。
然后就可以通过定义一个结构体把所有要提供给C的函数已函数指针的定义在结构体里面。(这个表述感觉没说错哈哈哈)
因为C和C++的结构体时的结构是对齐的。
结构体如下:
typedef void * thisobj;
//声明
typedef struct myobj *obj;
typedef struct {
//类方法
void (*setNumA)(obj ob, double a);
void (*setNumB)(obj ob, double b);
double (*getNumA)(obj ob);
double (*getNumB)(obj ob);
double (*getSum)(obj ob);
void (*outputNumber)(obj ob, double *p, double *q);
void (*destory)(obj ob);
} __methods;
还差属性没弄进去,那么的话再定义一个结构用来存放属性(attribute)和方法(method)。
最终结构如下:
typedef void * thisobj;
//声明
typedef struct myobj *obj;
typedef struct {
//类方法
void (*setNumA)(obj ob, double a);
void (*setNumB)(obj ob, double b);
double (*getNumA)(obj ob);
double (*getNumB)(obj ob);
double (*getSum)(obj ob);
void (*outputNumber)(obj ob, double *p, double *q);
void (*destory)(obj ob);
} __methods;
//定义
typedef struct myobj {
struct {
/*属性指针*/
double *a, *b;
} attributes;
//友元函数地址,由动态库提供静态地址
const __methods *methods;
//
thisobj self;
} myobj, *obj;
我们把上面的结构扔进一个头文件里面,供C和C++使用,不能语法有差异,兼容性第一。
我们还有一个得注意的是:C++代码编译时由隐式的改名行为,所以我们得导出C风格的函数名字。
extern "C" return-type function-name (...) {}
但是又有新的问题出现了,就是C中没有extern这个关键字,那么我们可以通过宏__cplusplus来判断当前是否为C++编译。
所以我们的头文件最终长下面这个样子:
/*
* author: lovingxiaobing | 小冰哟
*/
//filename: cppobj.h
#ifndef _CPPOBJ_H_
#define _CPPOBJ_H_
typedef void * thisobj;
//声明
typedef struct myobj *obj;
typedef struct {
//类方法
void (*setNumA)(obj ob, double a);
void (*setNumB)(obj ob, double b);
double (*getNumA)(obj ob);
double (*getNumB)(obj ob);
double (*getSum)(obj ob);
void (*outputNumber)(obj ob, double *p, double *q);
void (*destory)(obj ob);
} __methods;
//定义
typedef struct myobj {
struct {
/*属性指针*/
double *a, *b;
} attributes;
//友元函数地址,由动态库提供静态地址
const __methods *methods;
//
thisobj self;
} myobj, *obj;
#ifdef __cplusplus
extern "C" {
#endif
obj createSimpleObject(double a, double b);
#ifdef __cplusplus
}
#endif
#endif
我们下面会写一个C++代码,最后会编译成动态链接库的形式。
我们把友元函数的地址跟一个结构体绑定,这样子就可以访问了。
我们把关于某个类的的方法(友元函数)写进一个命名空间,这样子就不会混淆了。
我们通过malloc函数动态申请一个结构体,然后释放结构和释放对象写进一个destory函数里。
我们会发现,不能单纯的调用,得传一个实例指针进去,不然函数不知道要处理哪个实例对象,就类似与windows编程中的各种句柄,小冰猜这些个句柄应该是某个对象实例或者某个结构体的指针。
我们也借鉴这种方法。
我们对于对象属性的操作,可以通过指针操作,真没白学,,,亲测可用。
我们....
由destory那么肯定得有createXXX函数,因为C没有对象。。。。
我们在createXXX函数里面处理结构的动态申请,还有对象实例化。
下面就把带有少量注释的C++代码贴上来吧。
/*
* author: lovingxiaobing | 小冰哟
*/
//filename: cppobj.cpp
#include <iostream>
#include <cstdlib>
#include "cppobj.h"
//隐藏
#define __SECRET__ __attribute__((visibility("hidden")))
//可见
#define __CANSEE__ __attribute__((visibility("default")))
namespace MYOPS
{
class myops {
public:
myops(obj ob, double a, double b);
~myops(void);
/* 友元函数有权限访问类所有的成员 */
friend void setNumA(obj ob, double a); //设置a
friend void setNumB(obj ob, double b); //设置b
friend double getNumA(obj ob); //获取a
friend double getNumB(obj ob); //获取b
friend double getSum(obj ob); //求 a + b 的值
//通过指针导出a 和 b 的值
friend void outputNumber(obj ob, double *p, double *q);
private:
double a, b;
protected:
obj ob;
};
myops::myops(obj ob, double a, double b):
ob(ob), a(a), b(b)
{
//利用构造函数的权限获取a和b的指针
ob->attributes.a = &(this->a);
ob->attributes.b = &(this->b);
}
myops::~myops(void)
{
//释放结构
free(this->ob);
std::cout << "(In C++ obj): struct myobj was free!" << std::endl;
}
//隐藏这些函数符号
#pragma GCC visibility push(hidden)
///////////////////////////////////////
void setNumA(obj ob, double a)
{
((myops *)(ob->self))->a = a;
}
//////////////////////////////////////
void setNumB(obj ob, double b)
{
((myops *)(ob->self))->b = b;
}
//////////////////////////////////////
double getNumA(obj ob)
{
return ((myops *)(ob->self))->a;
}
//////////////////////////////////////
double getNumB(obj ob)
{
return ((myops *)(ob->self))->b;
}
//////////////////////////////////////
double getSum(obj ob)
{
myops *o = (myops *)(ob->self);
return o->a + o->b;
}
///////////////////////////////////////////////////
void outputNumber(obj ob, double *p, double *q)
{
myops *o = (myops *)(ob->self);
*p = o->a;
*q = o->b;
}
#pragma GCC visibility pop
//end,效果同__SECRET__
///////////////////////////////////////////////////
__SECRET__ void destory(obj ob)
{
delete (myops *)(ob->self);
std::cout << "(In C++ func. destory): object myops was deleted!" << std::endl;
}
//GCC 扩展的结构体初始化 (c++ style)
static const __methods staticFriendAddressTable = {
setNumA : &setNumA,
setNumB : &setNumB,
getNumA : &getNumA,
getNumB : &getNumB,
getSum : &getSum,
outputNumber : &outputNumber,
destory : &destory
};
//c style
/*
struct-type struct-name = {
.name1 = value1,
.name2 = value2,
......
}
*/
} //end of namespace MYOPS
__CANSEE__ obj createSimpleObject(double a, double b)
{
obj p = NULL;
MYOPS::myops *o = NULL;
if (!(p = (obj)malloc(sizeof(myobj))))
return NULL;
try {
o = new MYOPS::myops(p, a, b);
} catch (void *e) {
free(p);
return NULL;
}
p->methods = &MYOPS::staticFriendAddressTable;
p->self = (thisobj)o;
//返回实例结构
return p;
}
考虑到隐私问题,我们给要隐藏的函数名用宏的形式告诉编译器不要写进符号表。做法在代码注释中。
ok,咱们编译他们:
g++ cppobj.cpp -o cppobj.so -shared -fPIC -Os
回车,OK没有编译错误,因为我这里用的是Linux系统,所以动态链接库的后缀名是 .so
最后我们会得到一个cppobj.so的动态链接库。windows下是cppobj.dll,把上名的命令中的-o后面的改成cppobj.dll就行。
看来快要成功啦!!!!
赶快写一个C程序测试一下吧,下面是测试代码:
/*
* author: lovingxiaobing | 小冰哟
*/
//filename: callobj.c
#include <stdio.h>
#include <stdlib.h>
#include <errno.h>
#include "cppobj.h"
int main(void)
{
obj o = NULL;
double a, b;
printf("输入 a,b: ");
scanf("%lf,%lf", &a, &b);
//获取实例化对象
if ((o = createSimpleObject(a, b)) == NULL) {
perror("Create Error");
exit(-1);
}
///////////////////////////////////////////
puts("测试函数调用并返回值:");
printf("sum(a: %lf, b: %lf) = %lf\n", \
o->methods->getNumA(o), \
o->methods->getNumB(o), \
o->methods->getSum(o));
///////////////////////////////////////////
puts("通过函数设置对象属性:");
printf("输入 a,b: ");
scanf("%lf,%lf", &a, &b);
o->methods->setNumA(o, a);
o->methods->setNumB(o, b);
printf("sum(a: %lf, b: %lf) = %lf\n", \
o->methods->getNumA(o), \
o->methods->getNumB(o), \
o->methods->getSum(o));
///////////////////////////////////////////
puts("通过指针获取或改变对象的属性:");
printf("输入 a,b: ");
scanf("%lf,%lf", &a, &b);
*(o->attributes.a) = a;
*(o->attributes.b) = b;
printf("sum(a: %lf, b: %lf) = %lf\n", \
*(o->attributes.a), \
*(o->attributes.a), \
o->methods->getSum(o));
puts("-------------- c++动态库内访问c指针 --------------");
//先清零
b = a = 0;
o->methods->outputNumber(o, &a, &b);
printf("此时对象的属性:a = %lf\tb = %lf\n", a, b);
printf("结果: a = %lf\tb = %lf\n", a, b);
puts("----------------- free & delete ------------------");
o->methods->destory(o); //释放结构和对象
puts("(In C): 正常释放!");
return 0;
}
编译再链接然后就可以得到最终的程序啦!!
gcc callobj.c cppobj.so -o callobj -Wl,-rpath=. -O2
上面的-Wl,-rpath=.告诉编译器那个动态链接库在执行文件的当前路径中,加载动态库时就在这查找。
然后运行callobj就得到如下结果:
xiaobing@xiaobing-ThinkPad-T60p:~/mycode/dong$ ./callobj
输入 a,b: 3,6
测试函数调用并返回值:
sum(a: 3.000000, b: 6.000000) = 9.000000
通过函数设置对象属性:
输入 a,b: 8,5
sum(a: 8.000000, b: 5.000000) = 13.000000
通过指针获取或改变对象的属性:
输入 a,b: 11,22
sum(a: 11.000000, b: 11.000000) = 33.000000
-------------- c++动态库内访问c指针 --------------
此时对象的属性:a = 11.000000 22.000000
结果: a = 11.000000 22.000000
----------------- free & delete ------------------
(In C++ obj): struct myobj was free!
(In C++ func. destory): object myops was deleted!
(In C): 正常释放!
通过这个结果就知道,我们成功啦!
指针操作类属性,调用友元函数,以及对象的正常释放。。。
下会把源代码上传到这,还写了一个makefile。
好像浏览器耍性子了,附件不能上传。那就网盘链接吧,这个源代码还有编译过程都已经全部贴出了,无关紧要,更重要的是你们学到了点东西。
网盘链接:https://pan.baidu.com/s/1a45nwfi8QoL_Ajt66JPLIQ
小甲鱼:change world by programming.
|
免费评分
-
查看全部评分
|