こんにちは、Perl 6アドベントカレンダーの22日目の投稿になります。
昨日は、@magnolia_k_さんの、Perl6の中を覗いてみるでした。
今日はnativecastの使い方を紹介しようと思います。
イントロダクション
-
20日目で紹介したNativeCallを使ったプログラムの一部だけを最小構成で再現する方法のファイルを下記のように書き換えましょう
-
関数
load
はvoid*
型の与えられた引数をそのまま返す関数です
example.c
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include "example.h"
#ifdef _WIN32
#define DLLEXPORT __declspec(dllexport)
#else
#define DLLEXPORT extern
#endif
void* load(void* payload) {
return payload;
}
example.h
#if ! defined(HEADER_EXAMPLE_H)
#define HEADER_EXAMPLE_H
#ifdef __cplusplus
extern "C" {
#endif
void* load(void* payload);
#ifdef __cplusplus
} /* closing brace for extern "C" */
#endif
#endif /* HEADER_EXAMPLE_H */
01-basic.t
use v6;
use Test;
use MyNative;
use NativeCall;
use lib <lib t>;
use CompileTestLib;
compile_test_lib('example');
my sub load(Pointer[void] $payload) returns Pointer[void] is native('./example') { * }
my $a = CArray[int32].new;
$a[0] = 1;
$a[1] = 2;
$a[2] = 3;
my $from-c = load($a); # C言語側に$aを渡したい
done-testing;
- 下記コマンドで実行してみてください
実行
$ prove -e "perl6 -Ilib" -r -v t/01-basic.t
- 下記のようなエラーがでてしまったはずです
Native call expected return type with CPointer representation, but got a CArray (NativeCall::Types::CArray[int32])
in method CALL-ME at /home/itoyota/.rakudobrew/moar-nom/install/share/perl6/sources/24DD121B5B4774C04A7084827BFAD92199756E03 (NativeCall) line 333
in block <unit> at t/01-basic.t line 17
load
関数が要求している引数の型(i.e. Pointer[void]
)と違う型(i.e. CArray[int32]
)を渡しているのだから当然ですね。
nativecastを使ってキャストすれば、このようなエラーを回避することができます。
では実際に使っていきましょう!
Let's try!
- 01-basic.tを下記のように書き換えて再度実行してみましょう
01-basic.t
use v6;
use Test;
use MyNative;
use NativeCall;
use lib <lib t>;
use CompileTestLib;
compile_test_lib('example');
my sub load(Pointer[void] $payload) returns Pointer[void] is native('./example') { * }
my $a = CArray[int32].new;
$a[0] = 1;
$a[1] = 2;
$a[2] = 3;
my $from-c = load(nativecast(Pointer[void],$a)); # C言語側に$aを渡したい
done-testing;
実行
$ prove -e "perl6 -Ilib" -r -v t/01-basic.t
- 今度はエラーが出なくなりましたね。
- これは、
CArray[int32]
をPointer[void]
にきちんとキャストしてからC言語側に渡したからです。 - では、返ってきた値を使いたかったらどうしたらよいでしょうか。
- このときもnativecastを使うとよいです。
01-basic.t
use v6;
use Test;
use MyNative;
use NativeCall;
use lib <lib t>;
use CompileTestLib;
compile_test_lib('example');
my sub load(Pointer[void] $payload) returns Pointer[void] is native('./example') { * }
my $a = CArray[int32].new;
$a[0] = 1;
$a[1] = 2;
$a[2] = 3;
my $from-c = load(nativecast(Pointer[void], $a));
my $b = nativecast(CArray[int32], $from-c); # (#1)
is $b[0], 1;
is $b[1], 2;
is $b[2], 3;
done-testing;
- 上記の例では、
load
関数が返したPointer[void]
をCArray[int32]
にキャストして、Perl6側で使えるようにしています。 - では、実行してみましょう
実行
$ prove -e "perl6 -Ilib" -r -v t/01-basic.t
実行結果
t/01-basic.t ..
ok 1 -
ok 2 -
ok 3 -
1..3
ok
All tests successful.
- きちんと
$b
に値が入っていることが確認できましたね。
以上、Perl 6アドベントカレンダーの22日目の投稿でした。