2013年5月15日水曜日

[c言語] Windows と Linux の共有ライブラリ

分けあってWindows向けの共有ライブラリ(dll) と Linux向けの共有ライブラリ(so) を
一つのC言語のソースで用意する必要が生じだ。

後にも先にもこれ一回限りな気がするけど一応メモ
検証環境は、

Windows 7 64bit版 C Compiler: gcc ライブラリを使う言語とコンパイラ Delphi XE2
Linux (RHEL) C Compiler: gcc ライブラリを使う言語とコンパイラ Fortran 90  pgfortran



まずは、ヘッダファイルとソース

add.h

#ifndef ADD_H
#define ADD_H

int add_(int *a, int *b)

int devide_(float *a, float *b, float *c)
#endif


add.c
#include<stdio.h>


int add_(int *a, int *b){
    int c;

  c = *a + *b;
  return c;
}

int devide_(float *a, float *b, float *c){

    *c = *a / *b;

    return 0;
}

ポイントは、引数がすべて参照渡しになっている点です。 Fortranが参照渡しがデフォルトなためです。 また、関数名がアンダースコアで終わっているのは、また後で。。。

お次は、コンパイル
まずは、Windowsで

gcc -shared -m32 -o add.dll add.c

-m32 は、32bi tPEバイナリを出力するためのオプションでdllを使う、アプリケーション(exe)が32bitであるため。

次に、Linuxで
gcc -fPIC -shared -o libadd.so add.c

-fPICは、位置独立コード(Position Independent Code)というメモリのどのアドレにもロード可能にするためのオプションです。


そして、使う側。
まずは、Delphi

unit use;

interface

uses
  Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
  Dialogs, StdCtrls;

type
  TForm6 = class(TForm)
    Button1: TButton;
    procedure Button1Click(Sender: TObject);
  private
    { Private 宣言 }
  public
    { Public 宣言 }
  end;

  TAdd = function(a : PInteger; b : PInteger) : integer ; stdcall;
  TDevide = function(a : PSingle; b : PSingle; c : PSingle) : integer ; stdcall;

var
  Form6: TForm6;

implementation

{$R *.dfm}

procedure TForm6.Button1Click(Sender: TObject);
var
 ret : integer;
 a,b : integer;
 c,d,e   : single;
 Handle : THandle;
 Add    : TAdd;
 Devide : TDevide;
begin
 a := 4;
 b := 2;
 c := 4.0;
 d := 2.0;
 Handle := LoadLibrary('add.dll');
 if Handle <> 0 then
 begin
   @Add := GetProcAddress(Handle, 'add_');
   if @Add <> nil then
   begin
     ret := add(@a, @b);
     ShowMessage(IntToStr(ret));
   end;
   @Devide := GetProcAddress(Handle, 'devide_');
   if @Devide <> nil then
   begin
     ret := devide(@c, @d, @e);
     ShowMessage(FloatToStr(e));
   end;
   FreeLibrary(Handle);
 end;
end;

end.

関数をtypedefして、GetProcAddress にて関数のあるアドレスを取得する。

次は、Linux上のFortran
interface節を記述したインクルードファイルを作成

addf.h
      interface
        integer function add(a,b)
          integer a,b
        end function add

        integer function devide(a,b,c)
          real  a,b,c
        end function devide

      end interface

次にFortranのソース本体
       program test
       include 'addf.h'
       integer c,d
       real e,f,g
       c=4
       d=2
       e=4.0 
       f=2.0
       d = add(c,d)
       i = devide(e,f,g)
       write(*,*) d,g,i
       end program test

そしてコンパイル
pgfortran -I./include -L./lib -ladd src/use.f

-l で使用するライブラリを指定します。
また、Fortranの場合、関数名は、アンダースコアがついたシンボルを探すのでCの関数名には、アンダースコアをつける必要があります。


0 件のコメント:

コメントを投稿