
[code]

    public class Greeter {
        public static String greet(final String name) {
            final String message = String.format("こんにちは、%s！", name);
            return message;
        }
    
        public static void main(final String[] args) {
            final String message = greet("世界");
            System.out.println(message);
    
            // String.format() が確保した領域は(そのうち)GCが回収する
        }
    }
    
[/code]

上のJavaコードのうち、`greet`メソッドをCライブラリに置き換えようと考えています。  
上記のコードはJavaとしては合法だと考えていますが、同じようなノリのC実装はメモリリークするので問題があるかと思います。

[code]

    char* greet(char *name) {
        char *message = calloc(256, sizeof(char));
        sprintf(message, "こんにちは、%s！", name);
        return message;
    }
    
    int main(int argc, char **argv) {
        char *message = greet("世界");
        printf("%s\n", message);
        return 0;
        // calloc() が確保した領域が解放されないまま
    }
    
[/code]

そこで、後述のような、動的に確保した領域の解放方法(及び、対になる確保方法)を考えていました。  
が、そもそもこのような要求は別に特殊というわけでも無いと思うので、イディオム、パターン、ライブラリ実装などで一般化されているのでは、という思いも浮かんできました。

この件に関して、参考になりそうな(検索キーワードになりそうな)名前や実装などはあるでしょうか。

* * *

以下、自分が検討していた対応方法です。

1.  
解放処理を提供するパターン。  
実際には動的確保する関数としない関数が混在することになると思うので、呼び忘れとかが発生しそう。

[code]

    void greet_retval_dtor(char *retval) {
        free(retval);
    }
    
    // 呼び出し元
        char *message2 = greet("デストラクタ");
        printf("%s\n", message2);
        greet_retval_dtor(message2);
    
[/code]

2.  
呼び出し元が確保した領域に呼び出し先が書き込むパターン。  
あらかじめ必要なサイズの当たりがつくような処理でないと利用できない  
(あるいは、先に必要なサイズを教えてもらえるような関数が別途必要)。

[code]

    void greet_prepared_buffer(char *name, char *message) {
        sprintf(message, "こんにちは、%s！", name);
    }
    
    // 呼び出し元
        char *buff = calloc(256, sizeof(char));
        greet_prepared_buffer("バッファ", buff);
        printf("%s\n", buff);
        free(buff);
    
[/code]

3.  
コールバックで呼び出し元に返すパターン。  
関数のスコープと確保した領域のライフタイムが一致するという点では自然な気が。

[code]

    void greet_callback(char *name, void (*cb)(char *message)) {
        char *message = calloc(256, sizeof(char));
    
        sprintf(message, "こんにちは、%s！", name);
        cb(message);
    
        free(message);
    }
    
    // 呼び出し元
    void callback(char *message) {
        printf("%s\n", message);
    }
    
        greet_callback("コールバック", callback);
    
[/code]

* * *

(追記)  
実際には、呼び出し元はJava([Project
Panama](https://openjdk.java.net/projects/panama/)利用)、呼び出し先CライブラリはRustで実装しようと考えています。  
ただ、今回の件は実装言語に依存する話ではないのでは、と考えています(ので、C言語以外だと解法がある、というのであればそれも知りたいです)。

以下は前述のパターン2(呼び出し元が領域確保するパターン)を実装した…つもりのコードです。

[呼び出し元コード](https://github.com/yukihane/stackoverflow-
qa/blob/141e262c6b1d0b14193ee297558f357ca27637a2/so62868/mainjava/src/Main.java)(Java,
14-panama):

[code]

    public static void main(final String[] args) {
        final Scope scope = greeter_lib.scope();
    
        final Pointer<Byte> name = scope.allocateCString("錆びたお茶");
        final long size = 256;
        final Pointer<Byte> message = scope.allocateArray(NativeTypes.UINT8, size).elementPointer();
    
        greeter_lib.greet(name, message, size);
    
        final String retval = Pointer.toString(message);
        System.out.println(retval);
    
    }
    
[/code]

[呼び出し先コード](https://github.com/yukihane/stackoverflow-
qa/blob/141e262c6b1d0b14193ee297558f357ca27637a2/so62868/librust/src/lib.rs)(Rust,
1.41.0):

[code]

    #[no_mangle]
    pub unsafe extern "C" fn greet(name: *const c_char, message: *mut c_char, count: size_t) {
        let name = CStr::from_ptr(name);
        let name = name.to_str().unwrap();
        let text = format!("こんにちは、{}！", name);
        let text = CString::new(text).unwrap();
    
        message.copy_from(text.as_ptr(), count);
    }
    
[/code]


CC BY-SA 4.0
2020-02-08T05:16:34.377
62868
2020-02-22T08:34:23.133
2020-02-08T12:53:17.927
2808
2808
question
c api memory-leaks
動的確保したメモリー領域の解放戦略


[c++](/questions/tagged/c%2b%2b "'c++' のタグが付いた質問を表示") のクラスにしてデストラクタを使う  
[c](/questions/tagged/c "'c' のタグが付いた質問を表示") でなきゃならないのなら（自動変数を前提に） `alloca()`
を使うマクロにする  
あたりが現実的な解でしょうか・・・


CC BY-SA 4.0
2020-02-08T06:54:33.130
62872
2020-02-08T06:54:33.130
8589
62868
answer


呼び出し元が確保した領域に呼び出し先が書き込むパターン についてですが、

引数にバッファサイズを受け取り、戻り値で本来必要としたサイズを返す、というパターンを見たことがあります。

[code]

    size_t greet(char *name, char *message, size_t message_size) {
        size_t needed = 9+strlen(name);
        if (needed <= message_size) {
            sprintf(message, "hello, %s!", name);
        }
        return needed;
    }
    
    
    int main(void)
    {
        size_t size = greet("buffer", NULL, 0);
        char *buf = malloc(size);
        greet("buffer", buf, size);
        printf("%s\n", buf);
        free(buf);
    
        return 0;
    }
    
[/code]


CC BY-SA 4.0
2020-02-09T01:10:11.297
62891
2020-02-09T01:10:11.297
20885
62868
answer


Javaの文字列はUTF-16なので、相互運用するのであればネイティブコード側でもUTF-16を扱える言語のほうが効率的だし楽です。JNI (Java
Native Interface) のAPI関数には`char*`/`const char*`のインターフェイスも用意されていますが、これはModified
UTF-8と呼ばれる特殊形式であり、またUTF-16との余計な変換コストもかかります。

C/C++のような **ネイティブ言語** と、Java/C#のような **マネージ言語** を相互運用する際、 **ネイティブヒープ** と
**マネージヒープ**
ではメモリ管理の扱いが異なります。マネージ型を受け取って処理してマネージ型を返すような単純なケースなのか、それともネイティブヒープへのポインタをマネージコード側で保持しておきたいのか、によって方針も変わってきます。また、相互運用は言語やフレームワークごとに用意されている方法に従う必要があります。JavaであればJNIを使う、C#であればP/InvokeやCOM相互運用あるいはC++/CLIやC++/CXを利用する、などです。

Javaとネイティブ言語間のやりとりの場合、まずは素直にJNIを使って、C++ (C++11以降) でネイティブ側を実装する方法が良いと思います。  
（おそらく Project Panama はユーザーが少なく、まともな開発情報が得られるとは思えません）

`JNIEnv::NewString()`で返却される`jstring`はJavaの`String`型（マネージヒープ）に対応するJNIローカル参照で、nativeメソッド呼び出しの制御がJava側に返るとGC管理対象になり、解放をJVM任せにできます。

[code]

    // C++ 側:
    #include <jni.h>
    #include <string>
    
    extern "C" JNIEXPORT jstring JNICALL
    Java_com_example_Greeter_greet
    (JNIEnv* env, jclass /* sender */, jstring name) {
        if (!name) {
            return nullptr;
        }
        const jsize srcLength = env->GetStringLength(name);
        const jchar* nativeSrcBuffer = env->GetStringChars(name, nullptr);
    
        jstring resultString = nullptr;
        try {
            std::u16string str = u"こんにちは、";
            if (srcLength > 0 && nativeSrcBuffer) {
                str += std::u16string(reinterpret_cast<const char16_t*>(nativeSrcBuffer), static_cast<std::size_t>(srcLength));
            }
            str += u"！";
            resultString = env->NewString(reinterpret_cast<const jchar*>(str.c_str()), static_cast<jsize>(str.length()));
        } catch (...) {
        }
    
        if (nativeSrcBuffer) {
            env->ReleaseStringChars(name, nativeSrcBuffer);
            nativeSrcBuffer = nullptr;
        }
        return resultString;
    }
    
[/code]

[code]

    // Java 側:
    package com.example;
    
    public class Greeter {
        static {
            System.loadLibrary("xxx");
        }
        public static native String greet(String name);
        ...
    }
    
[/code]

ただし、計算量の多い処理の高速化や、既存の膨大なネイティブコード資産の活用など、よほどの理由がないかぎりJava/ネイティブコード相互運用には手を出すべきではありません。たいていの場合、Javaだけで組んだほうがコードのメンテナンス性の観点から好ましく、またヘタにネイティブコードを使うとかえってパフォーマンスが低下することもあります。

一般論で言うと、異なる言語間あるいは異なるABI間でのネイティブヒープメモリのやりとりは、通例バックエンド（ライブラリ）側にバッファの確保関数と解放関数を用意して、フロントエンド（アプリケーション）側で確保と解放を実行します。バッファサイズが不明な場合は別途必要バッファ長の問い合わせ関数を用意するケースもあります。C言語関数形式で書かれたAPI（例えばOpenCLやVulkanなど）の設計が参考になるはずです。

上位レベルの言語側では解放処理を自動化してメモリリークを防ぐため、例えばC++であればコンストラクタ・デストラクタによるRAII、Javaであれば`AutoCloseable`、C#であれば`IDisposable`などのリソース管理フレームワークにネイティブリソースの確保と解放を封じ込めるのが定石です。有名なC言語関数形式のAPIには、対応するC++/Java/C#向けのラッパー（バインディング）が公式または第三者によって開発されていることが多く、そういったものもまた参考になるはずです。

Windows APIやCOM (Component Object Model)
では`GlobalAlloc()`/`GlobalFree()`や`CoTaskMemAlloc()`/`CoTaskMemFree()`や`SysAllocString()`/`SysFreeString()`など、フレームワークやOSが用意したAPIでメモリ確保が行なわれるので、呼び出し側で不要になったときに対応APIを使って解放せよ、という設計になっているものもあります。ATL/MFCにはRAIIラッパーが用意されています。

なお、C++コードではポインタあるいは`intptr_t`/`uintptr_t`を、C#コードではポインタあるいは`IntPtr`/`UIntPtr`を使ってネイティブヒープのポインタやハンドルを保持することができますが、Javaコードではポインタやハンドルを直接保持することができないため、`byte[]`などを使う必要があります（`int`や`long`を使うと移植性が下がります）。


CC BY-SA 4.0
2020-02-09T15:36:25.827
62911
2020-02-15T15:37:07.330
2020-02-15T15:37:07.330
15413
15413
62868
answer


ヒープに確保したメモリの解放というお話であれば、C++の考えで敷衍すると`スマートポインタ`や`参照カウント`という単語にいきつくと思います。Rustは詳しくないのですが検索すると公式のドキュメントがでてきますので、このあたりをご参考にされてはいかがでしょうか?

<https://doc.rust-jp.rs/book/second-edition/ch15-00-smart-pointers.html>

基本的にはご推察の通り、何も考えずに用意されたものを使うのが通例ですし効率的でもあります。  
char*にヒープを確保して、文字列をいれて、デストラクタで自前にdelete ...
は行わずに標準ライブラリのstringを用いるのが適当でしょう(上のドキュメントを見る限りRustにも存在するようです)。

PureなC言語で実装することもできなくはないようです。

<https://stackoverflow.com/questions/799825/smart-pointers-safe-memory-
management-for-c>

JavaでC言語側の領域確保と解放は … 私なら避けると思います。JNIでJavaの型にして戻したほうが管理が楽ですしユーザーも使いやすいと思うので。


CC BY-SA 4.0
2020-02-15T02:48:58.060
63060
2020-02-15T03:05:55.580
2020-02-15T03:05:55.580
37777
37777
62868
answer


多くの言語でそのような機能は[Foreign Function
Interface(FFI)](https://ja.wikipedia.org/wiki/Foreign_function_interface)と呼ばれているので、FFI
+ design, principle, concept あたりをキーワードに検索することになると思います。

質問に対する直接的な回答として、具体的なイディオムとして見つけられたのは [opaque
pointer](https://en.wikipedia.org/wiki/Opaque_pointer) くらいでした。

* * *

他には、自分が疑問に思っていたことと同じ方向の記事として、次のようなものがありました。

  * [When & How to Use an FFI (Foreign Function Interface)](https://spin.atomicobject.com/2013/02/15/ffi-foreign-function-interfaces/)

加えて(、質問の当初の趣旨に反して実装言語依存のものになるのですが)、

  * [Crossing the Rust FFI frontier with Protocol Buffers - Mozilla Hacks - the Web developer blog](https://hacks.mozilla.org/2019/04/crossing-the-rust-ffi-frontier-with-protocol-buffers/)

という記事内容は、自分が問題としている領域とかなり近く、ここで説明されているライブラリのリポジトリは詳細を見てみる価値があるのではないかな、と考えています。

現時点の FFI の話題は、既存のプログラム資産を利用するために行う、というものが比較的多い(FFI API をデザインする余地が無く、既存の API
に合わせることができるかが関心の対象になる)印象を受けています。  
それとは異なり、Mozilla が Rust で行おうとしているのは、マルチプラットフォームのために新規作成機能も FFI
を利用して実装していくというものなので、自分にとって参考になりそうな情報が多そうだ、と感じました。

* * *

(以下、概ね出典のない[独自研究](https://ja.wikipedia.org/wiki/Wikipedia:%E7%8B%AC%E8%87%AA%E7%A0%94%E7%A9%B6%E3%81%AF%E8%BC%89%E3%81%9B%E3%81%AA%E3%81%84)です。)

今回、「呼び出し元」「呼び出し先」という 2 つの立場のモジュールが登場しています。  
これら 2 つのモジュールが協調動作するとき、2 つの類型に分類できるかなと考えます。

  * 呼び出し元が生成したコンテンツを呼び出し先が受け取り、処理する
  * 呼び出し先が生成したコンテンツを呼び出し元が受け取り、処理する

[![画像の説明をここに入力](https://i.stack.imgur.com/3khvT.png)](https://i.stack.imgur.com/3khvT.png)

ここからコンテンツを格納するメモリ領域(以降、作図の都合上容器と呼称します)の確保と破棄について考えていきます。

まず、容器を作成するのは誰が適任なのかを考えてみると、コンテンツ生産者が必要なサイズの容器を用意する、というのがどう考えても適切に思われます(以下、
_基本形_ と呼称)。

[![画像の説明をここに入力](https://i.stack.imgur.com/n5ZxA.png)](https://i.stack.imgur.com/n5ZxA.png)

コンテンツ消費者は一般的にコンテンツの大きさがわからないので適切な容器を作成するのが困難で、小さすぎるとバグ(バッファオーバラン)になり、大きすぎると効率が低下します。

それではなぜ、呼び出し先がコンテンツ生産するような C 言語の API
(例えば[`scanf`](https://linuxjm.osdn.jp/html/LDP_man-
pages/man3/scanf.3.html)や[`strcpy`](https://linuxjm.osdn.jp/html/LDP_man-
pages/man3/strcpy.3.html))はコンテンツ消費者である呼び出し元が容器を用意する前提(以降、 _容器先渡し方式_ と呼称)なのか。  
これは容器としてスタック領域を利用したいという C 言語特有の要求によるものではないかと思われます。  
FFI について考えると、スタック領域を意識しない(意識できない)言語も多く、これについてさほど強い要求は無いのではと考えます。

ただし、スタック領域利用のことを考えないとしても、呼び出し先のコンテンツ生産者に容器作成を任せたくない状況はあり得るでしょう。  
呼び出し先のコンテンツ生産者に対してコンテンツ生成を高頻度で依頼するような状況で、容器作成を都度行わなくとも前回のものを使い回せる(必要な容器の大きさが固定で、現在の容器の中身は無くなっても良い)、というような場合です。  
この場合には、あらかじめ容器を作成して、コンテンツ要求の関数で _容器先渡し_ を行い容器作成処理を省略したくなるのでは無いかと考えます。  
(グラフィック処理なんかが該当すのでは、思っていますが、確認できていません)

パフォーマンス上の要請で行うことになるので、自然にこの形になった、という状況は無く、意図を持って _基本形_ から _容器先渡し方式_
に変形することになるのではないでしょうか。

[![画像の説明をここに入力](https://i.stack.imgur.com/6GyyA.png)](https://i.stack.imgur.com/6GyyA.png)

次に、容器を破棄するのが適切のは誰かと言うと、これもあまり考える余地がなくコンテンツ消費者がコンテンツを消費し不要になった時点で行うのが自然で、つまりコンテンツ消費者が行う、ということになるかと思います。  
ただし、容器を破棄する処理の実装と容器を作成する処理の実装は同じ側が行っているはずで、API 境界をまたぐ呼び出しが発生する場合もあります。

[![画像の説明をここに入力](https://i.stack.imgur.com/IQI7D.png)](https://i.stack.imgur.com/IQI7D.png)

質問文には容器の破棄を忘れる(リーク)という懸念もありました。  
これについては、調べた限りでは、たしかにそのとおりだが、言語に備わっているデストラクタ相当機能(Java の `try-finally`(Project
Panama では
[`Scope`](https://download.java.net/java/early_access/panama/docs/api/java.base/java/foreign/Scope.html)の`try-
with-resources`) や Go だと [`defer`](https://blog.golang.org/c-go-
cgo))でリークを防いでいくことで対応可能、というのが一般的な考え方のようです。

コールバック方式について。  
必ずしも利用を避けるべきとは言えないが、前述の _基本形_ でなくコールバック方式を用いるのであればちゃんとした意図が必要( _容器先渡し方式_
と同じように)なのかな、という感想を持ちました。  
言語や FFI ライブラリ実装によって考慮すべき事項が異なり、机上で思い浮かべられることより実際の難易度は高そうに感じられます。

なお、(既存資産の再利用文脈以外での)コールバック方式についての言及は、調べた限りでは、冒頭にリンクしたものともうひとつの合計 2 つだけでした。

  * [When & How to Use an FFI (Foreign Function Interface)](https://spin.atomicobject.com/2013/02/15/ffi-foreign-function-interfaces/)

> Making a C Library FFI Friendly
>
>   4. Keep callback signatures simple, shallow, and well-documented. This is
> especially important if callbacks are necessary to use the library’s core
> functionality.
>

  * YouTube: [Nikita Baksalyar — All you need to know about the Rust FFI (Rust Hungary #2, 2017-10-24), 28:15 より](https://youtu.be/ctffjzdgGvc?t=28m15s) , summary は [34:40 より](https://youtu.be/ctffjzdgGvc?t=34m40s)

>   * Callbacks ar not ideal, but they work
>

まとめると、 _基本形_ がベースになり、 _容器先渡し方式_ , _コールバック方式_ は明確な意図を持って _基本形_
から変形すべき、ということになります。


CC BY-SA 4.0
2020-02-22T08:34:23.133
63269
2020-02-22T08:34:23.133
2808
62868
answer