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

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

新規登録して質問してみよう
ただいま回答率
85.50%
Unity

Unityは、Unity Technologiesが開発・販売している、IDEを内蔵するゲームエンジンです。主にC#を用いたプログラミングでコンテンツの開発が可能です。

C++

C++はC言語をもとにしてつくられた最もよく使われるマルチパラダイムプログラミング言語の1つです。オブジェクト指向、ジェネリック、命令型など広く対応しており、多目的に使用されています。

Q&A

解決済

3回答

3852閲覧

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

Hiiragi3

総合スコア6

Unity

Unityは、Unity Technologiesが開発・販売している、IDEを内蔵するゲームエンジンです。主にC#を用いたプログラミングでコンテンツの開発が可能です。

C++

C++はC言語をもとにしてつくられた最もよく使われるマルチパラダイムプログラミング言語の1つです。オブジェクト指向、ジェネリック、命令型など広く対応しており、多目的に使用されています。

0グッド

0クリップ

投稿2018/03/19 02:55

前提・実現したいこと

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

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

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

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

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

guest

回答3

0

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 05:12

編集2018/03/19 05:42
catsforepaw

総合スコア5938

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

Hiiragi3

2018/03/19 10:09

ご回答ありがとうございます。 > Unityプラグイン(というかC#のDllImportで参照するDLL)の中でSTLを使うことには何の制限もありません。性能改善のためにC#アプリの処理の一部をC++で作るということは良くありますが、STLを使いまくっています。 なるほど、そうなのですか・・・。もろもろ勉強になります。STL の使用に何かしら制限があるのかと思ったのですが、であれば、作り方・使い方に問題がある気がしますね。 Unity からは、「C# の DllImport → プラグイン内で extern "C" で書いているインターフェイス → C++ のクラス」という風にアクセスしており、アクセス自体は問題ない模様なのですが、STL の部分でどうにも変な挙動をするので気になってた感じです。 > テストアプリというのはC#で作ったのでしょうか? なるべく利用環境に近い状態でテストする必要があります C++ で作っていますね・・・。VisualStudio での C# は全く経験がないのもあり、C++ で作った DLL の使い方が判らずに挫折しました・・・。確かに似た環境で作ったほうがよいですね。 > 実際のところ、どのように作ってどのように呼んでいるのかが判らないので、なんとも答えようがありません。可能であればソースコードを提示していただければ、もう少し適切に回答できるかもしれません。 うむー、そうですよね・・・。ちょっとタイミングを見つけてテストコードを作ってみようと思います。
guest

0

こんにちは。

すいません。最終的な回答ではないです。
リンク先の記事は「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 04:41

編集2018/03/19 07:38
Chironian

総合スコア23272

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

Hiiragi3

2018/03/19 09: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 さんが書かれている通り、ちょっとコードが提示されないと判らないですよね・・・すみません。
Chironian

2018/03/19 10: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を使う必要がある筈なのです。
guest

0

自己解決

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

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

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

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

投稿2018/03/22 03:24

Hiiragi3

総合スコア6

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

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

ただいまの回答率
85.50%

質問をまとめることで
思考を整理して素早く解決

テンプレート機能で
簡単に質問をまとめる

質問する

関連した質問