LinuxSir.cn,穿越时空的Linuxsir!

 找回密码
 注册
搜索
热搜: shell linux mysql
查看: 1666|回复: 10

如何直接调用C++共享库(.so)的成员函数?

[复制链接]
发表于 2008-7-1 09:38:02 | 显示全部楼层 |阅读模式
比如有个类:
//头文件:myclass.h
class myclass
{
public :
       int classFun(int) ;
};
//cpp文件:myclass.cpp
#include "myclass.h"
int myclass::classFun(int param)
{
      return 1 ;
}

问题是:
我想把类myclass做成.so库,然后再在其他的类或函数里面
直接调用类mayclass的成员函数。
请问:怎样做?
我在网上搜索到的都是关于C的。
发表于 2008-7-1 12:22:12 | 显示全部楼层
试试这样?

在 myclass.cpp 中添加
  1. extern "C" void * get_interface(void)
  2. {
  3.     return new myclass;
  4. }
复制代码

然后当外部动态加载了这个库后, 只要导出 get_interface 接口, 执行一次即可得到一个类的实例, 然后访问它的成员即可了. 最后别忘了 delete 掉
回复 支持 反对

使用道具 举报

发表于 2008-7-1 14:57:12 | 显示全部楼层
加入extern 'C'  是為了name mangling??

如果沒有跨編譯器執行的話 應該是不會有name mangling的問題

我自己也不是很清楚 @@

但我的做法是 做成.so後 丟到/usr/lib資料夾內

並於主程式的makefile 加入 -lxxx.so
回复 支持 反对

使用道具 举报

 楼主| 发表于 2008-7-1 14:59:27 | 显示全部楼层
按照fish兄的方法,可以成功调用函数“void * get_interface(void)
”。但是有个问题:
当调用myclass的成员函数classFun时,
会出现编译错误:
/tmp/cc8tQ9Ta.o: In function `main':
testlib.cpp.text+0x24e): undefined reference to `libmy::getMyName()'
collect2: ld returned 1 exit status
=====================
不知道是哪里出错了……
回复 支持 反对

使用道具 举报

发表于 2008-7-1 16:52:21 | 显示全部楼层
原来楼主不是想要通过 dlopen 来动态加载 .so 啊.... sorry, 误解了, 而且把问题复杂化了.

为表歉意, 提供这个例子


  1. /* hello.h */
  2. #ifndef __HELLO_H__
  3. #define __HELLO_H__

  4. #include <iostream>

  5. using std::cout;
  6. using std::endl;

  7. class Hello {
  8.         public:
  9.                 void fn(void) { cout << "hello" << endl; };
  10. };

  11. #endif//__HELLO_H__
复制代码


  1. /* hello.cpp */
  2. #include "hello.h"

  3. #if 1
  4. /* 只有当打算动态加载时才需要这一部分, 参见 bar.c */
  5. extern "C" void * get_interface(void)
  6. {
  7.         return new Hello;
  8. }
  9. #endif
复制代码


为了把以上内容编译成库采用以下命令.

  1. # 编译成动态库
  2. g++ -Wl,-soname,libhello.so -shared -o libhello.so hello.o

  3. # 编译成静态库
  4. ar cru libhello.a hello.o
  5. ranlib libhello.a
复制代码


这样我们就得到了 libhello.so. 下面我们在 foo.c 中引用它


  1. /* foo.c */
  2. #include "hello.h"

  3. int main(void)
  4. {
  5.         Hello * hello = new Hello;
  6.         hello->fn();

  7.         delete(hello);

  8.         return 0;
  9. }
复制代码


编译时只要指定需要引用 libhello 即可, 与普通的库没有区别

  1. # 可以用绝对路径引用库
  2. g++ -o foo foo.o libhello.so

  3. # 如果 libhello.so 放在 /usr/lib 这样的系统路径中, 那么用下面的形式更好
  4. g++ -o foo foo.o -lhello
复制代码


另外既然已经提到了, 就再说一下动态加载的方法吧.

  1. /* bar.c */
  2. #include "hello.h"

  3. #include <dlfcn.h>

  4. int main(void)
  5. {
  6.         void * handle = dlopen("./libhello.so", RTLD_LAZY);
  7.         if (handle == NULL) {
  8.                 printf("dlopen(): %s\n", dlerror());
  9.                 return -1;
  10.         }
  11.         void * (* interface) (void) =
  12.                 (void * (*)(void)) dlsym(handle, "get_interface");

  13.         Hello * hello = (Hello *) interface();
  14.         hello->fn();

  15.         delete(hello);

  16.         return 0;
  17. }
复制代码


这次编译时不需要连接 libhello.so, 但是需要连接 libdl

  1. g++ -o bar bar.o -ldl
复制代码


foo 与 bar 的区别在于, 如果系统的库查找路径中找不到 libhello.so 的话, foo 是无法载入运行的, 而 bar 可以.

另外关于 extern "C", 这是为了使得接口编译后按照 C 语言形式, 因此我们才可能通过 get_interface 这个名字来对其进行查找, 如果按照 C++ 形式进行存储的话, 我们就无法直观地知道它编译后叫做什么名字了.
回复 支持 反对

使用道具 举报

发表于 2008-7-1 17:53:25 | 显示全部楼层
good~~~~~~   
回复 支持 反对

使用道具 举报

 楼主| 发表于 2008-7-1 21:19:58 | 显示全部楼层
fish兄,我照了你的方法做了,都成功。谢谢!
不过有个问题很奇怪:
如果我把“hello.h”中“fn”函数的定义放到hello.cpp文件里面去,
在编译bar.c或者foo.cpp文件时,就出现错误:
/tmp/cchDsdhg.o: In function `main':
foo.cpp.text+0x1a3): undefined reference to `Hello::fn()'
collect2: ld returned 1 exit status
这是为什么呢???
回复 支持 反对

使用道具 举报

发表于 2008-7-1 23:06:10 | 显示全部楼层
具体是怎么改的, 还是贴代码吧
回复 支持 反对

使用道具 举报

 楼主| 发表于 2008-7-2 08:26:59 | 显示全部楼层
Post by remote fish;1870025
具体是怎么改的, 还是贴代码吧

/* hello.h */
#ifndef __HELLO_H__
#define __HELLO_H__

#include <iostream>

using std::cout;
using std::endl;

class Hello {
        public:
                void fn(void);
};

#endif//__HELLO_H__
===============================
/* hello.cpp */
#include "hello.h"
void Hello::fn(void)
{
    cout << "hello" << endl;
}
#if 1
/* 只有当打算动态加载时才需要这一部分, 参见 bar.c */
extern "C" void * get_interface(void)
{
        return new Hello;
}
#endif

我只是把“hello.h”中“fn”函数的定义放到hello.cpp文件里面去,
其他的都没变!
回复 支持 反对

使用道具 举报

发表于 2008-7-2 09:55:53 | 显示全部楼层
我自己试了一下, 对于 bar 来说编译时是有这个问题. 至于楼主提到 foo 运行有问题, 应该是运行库查找路径的问题, 参见后面的测试.

尝试了一下, 给接口声明为 virtual 就可以解决 foo 的编译问题, 至于为什么还不清楚, 原谅我能力有限.

附上代码

  1. $ make dump
  2. **** Makefile ****
  3. CXX     := g++
  4. AR      := ar
  5. RANLIB  := ranlib

  6. all: hello.so hello.a foo bar

  7. clean:
  8.         -rm -f hello.{a,so} foo bar *.o

  9. dump:
  10.         @for i in Makefile *.cpp *.h; do                \
  11.                 echo "**** $$i ****";                   \
  12.                 cat $$i;                                \
  13.         done

  14. test: all
  15.         LD_LIBRARY_PATH=. ./foo
  16.         ./bar

  17. hello.so: hello.o
  18.         $(CXX) -Wl,-soname,$@ -shared -o $@ $^

  19. hello.a: hello.o
  20.         $(AR) cru $@ $^
  21.         $(RANLIB) $@

  22. foo: foo.o hello.so
  23.         $(CXX) -o $@ $^

  24. bar: bar.o
  25.         $(CXX) -o $@ $^ -ldl

  26. %.o: %.cpp
  27.         $(CXX) -o $@ $< -c
  28. **** bar.cpp ****
  29. #include "hello.h"

  30. #include <stdio.h>
  31. #include <dlfcn.h>

  32. int main(void)
  33. {
  34.         void * handle = dlopen("./hello.so", RTLD_NOW);
  35.         if (handle == NULL) {
  36.                 printf("dlopen(): %s\n", dlerror());
  37.                 return -1;
  38.         }

  39.         void * (* interface) (void) =
  40.                 ( void * (*) (void) )
  41.                 dlsym(handle, "interface");

  42.         Hello * hello = (Hello *) interface();
  43.         hello->fn();

  44.         delete hello;

  45.         return 0;
  46. }
  47. **** foo.cpp ****
  48. #include "hello.h"

  49. int main(void)
  50. {
  51.         Hello * hello = new Hello();

  52.         hello->fn();

  53.         delete hello;

  54.         return 0;
  55. }
  56. **** hello.cpp ****
  57. #include "hello.h"

  58. #include <iostream>

  59. void Hello::fn(void)
  60. {
  61.         std::cout << "hello" << std::endl;
  62. }

  63. #if 1
  64. extern "C" void * interface(void)
  65. {
  66.         return new Hello();
  67. }
  68. #endif
  69. **** hello.h ****
  70. #ifndef __HELLO_H__
  71. #define __HELLO_H__

  72. class Hello {
  73. public:
  74.         virtual void fn(void);
  75. };

  76. #endif//__HELLO_H__
复制代码


编译情况

  1. $ make
  2. g++ -o hello.o hello.cpp -c
  3. g++ -Wl,-soname,hello.so -shared -o hello.so hello.o
  4. ar cru hello.a hello.o
  5. ranlib hello.a
  6. g++ -o foo.o foo.cpp -c
  7. g++ -o foo foo.o hello.so
  8. g++ -o bar.o bar.cpp -c
  9. g++ -o bar bar.o -ldl
复制代码


运行情况

  1. $ make test
  2. LD_LIBRARY_PATH=. ./foo
  3. hello
  4. ./bar
  5. hello
复制代码
回复 支持 反对

使用道具 举报

您需要登录后才可以回帖 登录 | 注册

本版积分规则

快速回复 返回顶部 返回列表