プログラミング

nodejsとC言語の連携

nodejsとC言語との連携もffi-napiというライブラリを使うと容易に実現できます。このライブラリを導入することの方が時間がかかったくらいです。gccコンパイラが古かったため,コンパイラのアップデートからやる必要がありました。

#!/bin/bash

gcc --shared -fPIC -o libadd.so -xc - << EOS
int add(int x, int y ) { return x + y; }
EOS

cat << EOF > node2c.js
var ffi = require('ffi-napi');
var lib = ffi.Library("libadd", { add: ['int', ['int', 'int']]});
console.log("3 + 4 = "+  lib.add(3, 4));
EOF

node node2c.js
プログラミング

HaskelとC言語の連携

HaskelとC言語との連携もそれほど大変なものではありません。私はほとんどHaskelでの開発をしていないので,Haskelの記法には違和感があります。

#!/bin/bash
gcc --shared -fPIC -o libadd.so -xc - << EOS
int add(int x, int y) { return x + y; }
EOS

cat << EOF > Main.hs
module Main where
import Foreign.C.Types

foreign import ccall "add" c_add :: CInt -> CInt -> CInt
cAdd x y = fromIntegral $ c_add (fromIntegral x) (fromIntegral y)

main :: IO ()
main = do putStrLn "START"
          putStrLn ("100 + 200 = " ++ (show(cAdd 100 200)))
          putStrLn "END"
EOF

ghc -L. -ladd Main.hs
if [ $? -eq 0 ]; then
  ./Main
fi
プログラミング

LuaとC言語との連携

LuaとC言語との連携は癖があります。Cライブラリ側にLua専用のコードを記述する必要がありますので,既存のライブラリをそのまま使うという使い方は難しいかもしれません。特にデータの受け渡しは低レベルで,スタックにプッシュしたりポップしたりする手続きを記述する必要があります。

#!/bin/bash

gcc --shared -fPIC -o libadd.so -xc - << EOS
#include "lua.h"
#include "lualib.h"
#include "lauxlib.h"
static int c_add(lua_State *L)
{
    lua_pushnumber(L, (luaL_checknumber(L, 1) + luaL_checknumber(L, 2)));
    return 1;
}
int luaopen_libadd(lua_State *L)
{
    lua_register(L, "add", c_add);
    return 0;
}
EOS
if [ ! $? -eq 0 ]; then echo "ERROR" && exit 1; fi

cat << EOF > lua2c.lua
require "libadd"
for i = 1, 10 do
  print(tostring(i) .. " + " .. tostring(10 * i) .." = " .. tostring(add(i,i * 10)))
end
EOF

lua lua2c.lua
プログラミング

PHPとC言語の連携

PHPとC言語との連携は非常に簡便に実現できました。但し,PHPのFFIもジュールはバージョン7.4以降のものにしか対応していないようなので,古いPHPでは実行できないかもしれません。

#!/bin/bash
gcc --shared -fPIC -o libadd.so -xc - << EOS
int add(int x, int y) { return x + y; }
EOS

cat << EOF > php2c.php
<?php
  \$ffi = FFI::cdef("int add(int, int);", "libadd.so");
  print("30 + 40 = " . \$ffi->add(30, 40) . "\n");
?>
EOF

php80 php2c.php
プログラミング

C#とC言語(ネイティブ)の連携

C#とC言語の連携は.NET同士はもちろんのこと,ネイティブなC言語のライブラリでも容易に連携できます。C#のコードにDllImportなどいくつか記述を追加するのみです。Microsoftの以下のドキュメントに詳細に説明されています。

https://learn.microsoft.com/ja-jp/dotnet/framework/interop/creating-prototypes-in-managed-code

#!/bin/bash

rm -rf net2c
dotnet new console -o net2c

cat << EOF > net2c/Program.cs
using System;
using System.Runtime.InteropServices;
public class Net2C
{
    [DllImport("libadd.so")]
    private static extern int add(int x, int y);
    public static void Main(string[] args)
    {
         Console.WriteLine("START");
         Console.WriteLine(add(1,2));
         Console.WriteLine("END");
    }
}
EOF

gcc --shared -o net2c/libadd.so -xc - << EOS
int add(int x, int y) { return x + y; }
EOS

cd net2c
dotnet build
if [ $? -eq 0 ]; then
  dotnet run
fi

cd ..

プログラミング

LispとC言語の連携

Lispはあまり使われていないでしょうけど,一応C言語で作成したライブラリを呼び出せるようです。他と同様にシェルに全部記述しました。

#!/bin/bash

gcc -o libadd.so --shared -fPIC -xc - << EOS
int add(int x, int y) { return x + y; }
EOS

cat << EOF > add.lisp
(ql:quickload :cffi)
(cffi:load-foreign-library "libadd.so")

(defun main ()
  (write-line "Lisp START")
  (write-line (write-to-string (cffi:foreign-funcall "add" :int 100 :int 200 :int)))
  (write-line "Lisp END"))

(sb-ext:save-lisp-and-die "add" :toplevel #'main' :executable t)
EOF

sbcl --load add.lisp

if [ $? -eq 0 ]; then
  ./add
fi
プログラミング

FORTRANとC言語との連携

FORTRANとCとの連携は少々クセがあります。FORTRANからはポインタが渡されます。また,関数名はFORTRANで定義した関数名に”_”(アンダースコア)を付加したものとなります。私はFORTRAN77の知識しかないため,古い書式です。

#!/bin/bash
gcc -fPIC --shared -xc -o libadd.so - << EOS
#include <stdio.h>
int add_(int* x, int* y, int* z)
{
    printf("[C] %d + %d = %d\n", *x, *y, *x + *y);
    *z = *x + *y;
    return 0;
}
EOS

cat << EOF > fortran01.f
      PROGRAM fortran01
          IMPLICIT NONE
          INTEGER A,B,Z
          A = 10
          B = 20
          WRITE(*,*) 'START'
          CALL add(A, B, Z)
          WRITE(*,100) A, B, Z
          WRITE(*,*) 'END'
  100 FORMAT('[FORTRAN]', I5, ' + ', I5, ' = ', I5)
          STOP
      END
EOF

gfortran -o fortran01 fortran01.f libadd.so

./fortran01
プログラミング

PerlとC言語の連携

PerlからC言語のライブラリを呼び出すのは少々厄介でした。h2xsというツールを使ってテンプレートを生成した上で作成されたMakefileを使ってビルドするという手順になります。他の記事に倣ってシェル1本で書いたので,苦しい対処ですが,sedコマンドとcatコマンドでで生成されたテンプレートを編集しています。

#!/bin/bash

rm -rf Perl2C

h2xs -A -n Perl2C
source=Perl2C/Perl2C.xs

sed -i -e 's/^MODULE.*$/int add(int,int);\n\nMODULE = Perl2C    PACKAGE = Perl2C/g' $source 

cat << EOS >> $source
int
add(x, y) 
    int x
    int y
  CODE:
    RETVAL = x + y;
  OUTPUT:
    RETVAL
EOS

echo "========================================"
cat $source
echo "========================================"
cd Perl2C
perl Makefile.PL
make

cat << EOF > run.pl
use ExtUtils::testlib;
use Perl2C;
\$x = 1000;
\$y = 2000;
\$result = Perl2C::add(\$x, \$y);
print(\$x . " + "  . \$y . " = " . \$result . "\n\n");
EOF

perl run.pl
cd ..

プログラミング

Gnu COBOLとC言語との連携

Gnu COBOLとC言語との連携は比較的容易に実現できます。Gnu COBOL自体がCOBOLのソースコードをC言語に変換してからCコンパイラでコンパイルするというアーキテクチャで実現されていることも理由と思われます。

Cプログラムは普通にライブラリを作成する要領で作成できます。COBOLからはCALL文でサブルーチンを呼ぶのと同じです。文字列の扱いはCOBOL文字列をそのままCライブラリに渡しても処理ができないため,末尾に’\0’を付加して渡す必要があります。

#!/bin/bash
gcc -fPIC --shared -o add.so -xc - << EOF
int add(int x, int y) 
{ 
    return x + y; 
}
EOF

cat << EOS > cob01.cob
000100**************************************************************************
000200 IDENTIFICATION        DIVISION.
000300 PROGRAM-ID.           PROGRAM01.
000400 AUTHOR.               KUBO, Y.
000500**************************************************************************
000600 ENVIRONMENT           DIVISION.
000700**************************************************************************
000800 DATA                  DIVISION.
000900*-------------------------------------------------------------------------
001000 WORKING-STORAGE       SECTION.
001100*-------------------------------------------------------------------------
001200 01 WORK.
001300         03 WORK-ARG1             PIC 9(06)  VALUE  ZERO.
001400         03 WORK-ARG2             PIC 9(06)  VALUE  ZERO.
001500 01 RET                           PIC 9(04)  COMP-X.
001600**************************************************************************
001700 PROCEDURE             DIVISION.
001800**************************************************************************
001900 MAIN-RTN.
002000       DISPLAY   'START'.
002100       MOVE         100          TO       WORK-ARG1.
002220       MOVE         200          TO       WORK-ARG2.
002300       CALL 'add'   USING        BY VALUE   WORK-ARG1
002400                                 BY VALUE   WORK-ARG2
002500                    RETURNING    RET.
002600       DISPLAY    RET.
002700       DISPLAY   'END'.
002800       STOP       RUN.
002900**************************************************************************
EOS

cobc -x -o cob01 cob01.cob

./cob01

プログラミング

RustとC言語の連携

RustからC言語で作成したライブラリを呼び出すのは比較的簡単に実現できます。以下の1本のシェルでは,Cのライブラリを書き出して,Rustから呼び出すプログラムを作成します。

Rustのコード内にextern “C”でC言語の関数のプロトタイプ宣言に相当するインタフェース定義を記述した上で,呼び出す時にunsafeで囲んでやれば呼び出せます。rustcでコンパイルする際に,-lオプションでライブラリ名を指定して,-Lオプションでライブラリの場所を指定する必要があります。

#!/bin/bash

gcc --shared -o libadd.so -xc - << EOF
int add(int x, int y) { return x + y; }
EOF

cat << EOS > main.rs
extern "C" {
    fn add(x: i32, y: i32) -> i32;
}
fn main() {
    unsafe {
      println!("add(100,200) = {}", add(100, 200));
    };
}
EOS

rustc main.rs -l add -L .
if [ $? -eq 0 ]; then
  ./main
fi