質問をすることでしか得られない、回答やアドバイスがある。

15分調べてもわからないことは、質問しよう!

ただいまの
回答率

87.77%

Unity のネイティブプラグイン(C++)では std::map や std::for_each の挙動がおかしい?

解決済

回答 3

投稿

  • 評価
  • クリップ 0
  • VIEW 2,766

score 6

 前提・実現したいこと

C++ 初心者で、Unity 用 NativePlugin を制作しています。
対象プラットフォームは、Windows/Mac/Android/iOS であり、プラグインは C++ を利用して作っています。

一旦、Windows のものを作ろうと思い、Visual Studio で制作しています。コレクション関連を自前で実装するのは厳しいので、std::map や std::for_each を利用しています。

Visual Studio 上でテストアプリを作り、このプラグインの動作は確認済みです。

いざ、作成した dll を Unity 上で利用しようとすると、「std::map の count() で、そのキーが存在しているにもかかわらずカウントされない」とか、「std::for_each の処理に差し掛かったら落ちる」などあり、正常に動作しません。前述しました通り、Visual Studio 上でのテストアプリではこの処理は問題なく実行されています。

調べてみたところ、下記記事において「STL は使用してはならない」とあります。
ネイティブプラグイン開発のススメ (Unity/Cocos2dx)
しかしながらコレクションを自前実装するのはスキル的に難しいので、なんとか使えればと思っています。

記事内には「ちょっと凝ったプラグインを作る場合は素直に STL を使って幾つかの STL バージョン用のバイナリを生成してます。」とあるので、何か回避策はあるのだろうと思うのですが、調べてみてもそういった内容の記事は見つからず、皆目見当がつきません。

ですので、下記の点をお尋ねしたいです。

  • Windows/Mac/Android/iOS で扱う Unity プラグインにおいて、std::map や std::for_each を問題なく利用できる方法
  • 難しい場合は、何か代替となるようなライブラリがあるか
  • そもそもそういう問題ではない場合、何が問題か

以上、よろしくお願いいたします。

 補足情報(FW/ツールのバージョンなど)

Unity 2017.3.1p3
VisualStudio 2017

  • 気になる質問をクリップする

    クリップした質問は、後からいつでもマイページで確認できます。

    またクリップした質問に回答があった際、通知やメールを受け取ることができます。

    クリップを取り消します

  • 良い質問の評価を上げる

    以下のような質問は評価を上げましょう

    • 質問内容が明確
    • 自分も答えを知りたい
    • 質問者以外のユーザにも役立つ

    評価が高い質問は、TOPページの「注目」タブのフィードに表示されやすくなります。

    質問の評価を上げたことを取り消します

  • 評価を下げられる数の上限に達しました

    評価を下げることができません

    • 1日5回まで評価を下げられます
    • 1日に1ユーザに対して2回まで評価を下げられます

    質問の評価を下げる

    teratailでは下記のような質問を「具体的に困っていることがない質問」、「サイトポリシーに違反する質問」と定義し、推奨していません。

    • プログラミングに関係のない質問
    • やってほしいことだけを記載した丸投げの質問
    • 問題・課題が含まれていない質問
    • 意図的に内容が抹消された質問
    • 過去に投稿した質問と同じ内容の質問
    • 広告と受け取られるような投稿

    評価が下がると、TOPページの「アクティブ」「注目」タブのフィードに表示されにくくなります。

    質問の評価を下げたことを取り消します

    この機能は開放されていません

    評価を下げる条件を満たしてません

    評価を下げる理由を選択してください

    詳細な説明はこちら

    上記に当てはまらず、質問内容が明確になっていない質問には「情報の追加・修正依頼」機能からコメントをしてください。

    質問の評価を下げる機能の利用条件

    この機能を利用するためには、以下の事項を行う必要があります。

回答 3

+2

こんにちは。

すいません。最終的な回答ではないです。
リンク先の記事は「STL のバージョンによりバイナリ互換性がなくなってしまう」からSTLは使わない方がいい。しかし、使いたい場合は、「STL バージョン用のバイナリを生成」しているということです。

あるUnityアプリのネイティブ・プラグインを開発する際に、例えばVisual Studio 2013を使ったとします。そして、ある時、Visual Studio 2017へバージョンアップしたとします。
ネイティブ・プラグインとアプリのバージョンを常に同期してアップデートすれば問題ないのですが、恐らく同期しないで(アプリのみ、もしくは、ネイティブ・プラグインのみ)アップデートする場合に問題がでます。
Unityのネイティブ・プラグインの配布戦略を把握していませんが、可能であれば両者同時にアップデートすれば、使って問題ない筈です。
といいますが、C++ DLLを使う場合は、DLLを使う側とDLL側のバージョンを同期させることを強くお勧めします。

現在Hiiragi3さんは恐らくVisual Studioのバージョン変更による問題にハマっている様子ではありませんので、リンク先の「STL は使わない」ケースには該当しておらず、リンク先の対策「バージョン毎にバイナリを作成する」は適用できません。

そもそもそういう問題ではない場合、何が問題か

Unityのマニュアルによると「プラグインの関数は C 言語に基づいたコール·インタフェース(C-based call interface)を使用するので、C++や Objective-C を使用した場合はネーム・マングル(name mangling)の問題を避けなければなりません。」ということですので、直接STLを呼び出すことは事実上出来ません。
使っているSTLの関数全てについて、マングリングを手で解決する必要があります。これは現実的ではないと思います。


もし、既にネイティブ・プラグイン自体をC言語I/Fで開発されていて、Unityから呼び出した時に正常に動作しないのであれば、マーシャリングに失敗している可能性が高いです。特に文字列型についてはUnityは恐らくUTF16ではないでしょうか? C++側もそれに合わせたコードになっていないと正常には動作しないと思います。

投稿

編集

  • 回答の評価を上げる

    以下のような回答は評価を上げましょう

    • 正しい回答
    • わかりやすい回答
    • ためになる回答

    評価が高い回答ほどページの上位に表示されます。

  • 回答の評価を下げる

    下記のような回答は推奨されていません。

    • 間違っている回答
    • 質問の回答になっていない投稿
    • スパムや攻撃的な表現を用いた投稿

    評価を下げる際はその理由を明確に伝え、適切な回答に修正してもらいましょう。

  • 2018/03/19 18:55

    ご回答ありがとうございます。

    > (中略) 現在Hiiragi3さんは恐らくVisual Studioのバージョン変更による問題にハマっている様子ではありませんので、リンク先の「STL は使わない」ケースには該当しておらず、リンク先の対策「バージョン毎にバイナリを作成する」は適用できません。

    丁寧なご説明ありがとうございます。ふむー、難しいものですね。Unity と Visual Studio とのバージョンで何かしら関係があるのかと考えたりしたのですが。

    > Unityのマニュアルによると「プラグインの関数は C 言語に基づいたコール·インタフェース(C-based call interface)を使用するので、C++や Objective-C を使用した場合はネーム・マングル(name mangling)の問題を避けなければなりません。」ということですので、直接STLを呼び出すことは事実上出来ません。
    > (中略)
    > もし、既にネイティブ・プラグイン自体をC言語I/Fで開発されていて、Unityから呼び出した時に正常に動作しないのであれば、マーシャリングに失敗している可能性が高いです。

    そうですね、C 言語でのネームマングルは回避して、そこから C++ のクラスを呼び出したりする処理をしています。
    実際に Unity から C++ のコードにアクセスすること自体は可能なのですが、その中の処理で std::map や std:for_each が不審な挙動を取るので、STL は使ってはいけないのではないか、という危機感を抱いた感じです。

    > 特に文字列型についてはUnityは恐らくUTF16ではないでしょうか? C++側もそれに合わせたコードになっていないと正常には動作しないと思います。
    浅学ゆえに文字コードがどうだったかは判らないのですが、map のキーに文字列を使っていたりするので、もしかしたらそこらへんが関係しているような気がします・・・。

    上のほうで catsforepaw さんが書かれている通り、ちょっとコードが提示されないと判らないですよね・・・すみません。

    キャンセル

  • 2018/03/19 19:19

    > Unity と Visual Studio とのバージョンで何かしら関係があるのかと考えたりしたのですが。

    C#とdll間のI/FがC言語形式であれば、catsforepawさんも書かれているようにdll側でSTLを使うことに問題はありません。また当然ですがC#との間でSTLに関するバージョン不整合は発生しません。

    > map のキーに文字列

    C++側はstd::wstring、もしくは、std::u16stringを使う必要がある可能性が高いです。
    Unityには詳しくないのですが、C#標準はUTF16ですので恐らくUnityもUTF16だろうと思います。
    ですので、どこかで文字コード変換していない限り、C++側でもUTF16を使う必要がある筈なのです。

    キャンセル

+2

Unityプラグイン(というかC#のDllImportで参照するDLL)の中でSTLを使うことには何の制限もありません。性能改善のためにC#アプリの処理の一部をC++で作るということは良くありますが、STLを使いまくっています。

調べてみたところ、下記記事において「STL は使用してはならない」とあります。

それは「Cocos2d-x」の文脈で語られていることですよね。Cocos2d-xはいじったことがないのでよく判りませんが、プラグインはスタティックライブラリー形式なのでしょうか。だとしたら、バージョン不整合は問題になります。

一方、Unity(というかC#)に関しては、DLLを動的リンクする方式なので、インターフェイス部分(関数プロトタイプ)さえきっちり決められていれば、中身をどう書こうと自由です。STLを使っても良いですし、boostのような外部ライブラリーを使っても何の問題もありません。たとえコンパイラーのバージョンが違っていても(基本的には)問題にはなりません。

ただし、C#のDllInportの場合「C形式の関数」しか対応していないので、引数にSTLコンテナを使うようなことはできません。それが制限といえば制限かもしれませんね。そのため、中身はクラスで実装するけど、C#向けにはC形式のラッパー関数を作ってそれをエクスポートする、ということをよくやります。


補足

↑に書いた内容はWindowsのDLLの場合ですね。元記事をもう少し読み進めていったら、.soファイルとか書いてあるので、LinuxかMacということでしょう。となると、バージョン不整合は問題になるかもしれません。
まぁ、そうだとしても、コンパイラーのバージョンが変わったらリビルドすれば良いだけなので、STLを使ってはいけないという縛りをあえて設ける必要なないと思います。


追記

肝心なことを忘れていました。

いざ、作成した dll を Unity 上で利用しようとすると、「std::map の count() で、そのキーが存在しているにもかかわらずカウントされない」とか、「std::for_each の処理に差し掛かったら落ちる」などあり、正常に動作しません。

テストアプリというのはC#で作ったのでしょうか? なるべく利用環境に近い状態でテストする必要があります
エクスポート関数の引数などにコンテナが含まれていませんか? 前述の通り、C#のDllImportで呼べるのはC形式の関数のみです。

実際のところ、どのように作ってどのように呼んでいるのかが判らないので、なんとも答えようがありません。可能であればソースコードを提示していただければ、もう少し適切に回答できるかもしれません。

投稿

編集

  • 回答の評価を上げる

    以下のような回答は評価を上げましょう

    • 正しい回答
    • わかりやすい回答
    • ためになる回答

    評価が高い回答ほどページの上位に表示されます。

  • 回答の評価を下げる

    下記のような回答は推奨されていません。

    • 間違っている回答
    • 質問の回答になっていない投稿
    • スパムや攻撃的な表現を用いた投稿

    評価を下げる際はその理由を明確に伝え、適切な回答に修正してもらいましょう。

  • 2018/03/19 19:09

    ご回答ありがとうございます。

    > Unityプラグイン(というかC#のDllImportで参照するDLL)の中でSTLを使うことには何の制限もありません。性能改善のためにC#アプリの処理の一部をC++で作るということは良くありますが、STLを使いまくっています。

    なるほど、そうなのですか・・・。もろもろ勉強になります。STL の使用に何かしら制限があるのかと思ったのですが、であれば、作り方・使い方に問題がある気がしますね。

    Unity からは、「C# の DllImport → プラグイン内で extern "C" で書いているインターフェイス → C++ のクラス」という風にアクセスしており、アクセス自体は問題ない模様なのですが、STL の部分でどうにも変な挙動をするので気になってた感じです。

    > テストアプリというのはC#で作ったのでしょうか? なるべく利用環境に近い状態でテストする必要があります
    C++ で作っていますね・・・。VisualStudio での C# は全く経験がないのもあり、C++ で作った DLL の使い方が判らずに挫折しました・・・。確かに似た環境で作ったほうがよいですね。

    > 実際のところ、どのように作ってどのように呼んでいるのかが判らないので、なんとも答えようがありません。可能であればソースコードを提示していただければ、もう少し適切に回答できるかもしれません。
    うむー、そうですよね・・・。ちょっとタイミングを見つけてテストコードを作ってみようと思います。

    キャンセル

check解決した方法

0

お騒がせしましたが、なんとか自己解決しました。
結論としては、STL が全く関係ない、恥ずかしながら、C++ に対する無知故の問題でした。

おそらく文字列を const char* でやり取りしていて、std::map のキーに指定していたのですが、それのメモリ保持がうまくできてなかったためにキーがおかしくなり、std::map や std::for_each で支障をきたしていたようです。std::string を使うことで問題解決しました。

今回の解決に至れたのは、「STL の使用は全然問題ない」ということによって迷いがなくなったのと、「文字列を疑ってみる」という方向性を、皆さんのご回答で見出すことができたからです。

ありがとうございました。

投稿

  • 回答の評価を上げる

    以下のような回答は評価を上げましょう

    • 正しい回答
    • わかりやすい回答
    • ためになる回答

    評価が高い回答ほどページの上位に表示されます。

  • 回答の評価を下げる

    下記のような回答は推奨されていません。

    • 間違っている回答
    • 質問の回答になっていない投稿
    • スパムや攻撃的な表現を用いた投稿

    評価を下げる際はその理由を明確に伝え、適切な回答に修正してもらいましょう。

15分調べてもわからないことは、teratailで質問しよう!

  • ただいまの回答率 87.77%
  • 質問をまとめることで、思考を整理して素早く解決
  • テンプレート機能で、簡単に質問をまとめられる

関連した質問

同じタグがついた質問を見る