2023年04月19日(水) [長年日記]
■ [windows][c++] VC++プロジェクトのビルドイベントでcmakeコマンドを呼び出す
Visual Studio 2019のC++プロジェクトの「ビルドイベント」でcmakeコマンドを呼び出したかった。
Visual StudioにはCMakeが付属しているので普通に呼び出せると思っていたが、コマンドが見つからないというエラーになってしまった。
Visual Studioの「Developer Command Prompt」からならcmakeコマンドを呼び出せる。調べると、Developer Command Promptとビルドイベントの環境とではPATH環境変数の値が違っていた。
Developer Command Promptの環境はVsDevCmd.batというバッチファイルから開けるようだ。幸い、ビルドイベントの環境はVsDevCmd.batへのパスが通っているので、ビルドイベントで実行するスクリプトに
call VsDevCmd.bat cmake --help
などと書けばcmakeコマンドを実行できた。
- call (Microsoft Learn)
2023年04月18日(火) [長年日記]
■ [windows][c++] VC++でコンパイル時の警告がエラーとみなされるのを解除する
deprecatedとマークされた関数などを使用していると、VC++のコンパイラはC4996という警告を報告する。
これは警告なのだけれど、Visual Studio上ではエラーとみなされてコンパイルが成功せず困った。
コンパイラの次のオプションが有効だと、一部の警告がエラー扱いになるようだ。
- /sdl (追加のセキュリティ チェックの有効化) (Microsoft Lean)
Visual Studio 2019が生成するプロジェクトではこのオプションが有効になっている模様。プロジェクトのプロパティの次の場所で設定を変更できる。
- 構成プロパティ → C/C++ → 全般 → SDLチェック
2023年04月07日(金) [長年日記]
■ [windows][c#] Ijwhost.dllが無いとSystem.BadImageFormatExceptionが投げられる
C++のコードをC#から呼び出したくて、C++/CLIを初めて試している。使うためには、Visual Studioに「C++/CLIサポート」コンポーネントを追加する必要があった。
で、C++/CLIのプロジェクトでDLLを作成し、C#のプロジェクトでそのDLLを直接参照する実行ファイルを作成してみたのだけれど、実行ファイルを実行すると
Unhandled exception. System.BadImageFormatException: Could not load file or assembly 'sample, Version=1.0.8497.30461, Culture=neutral, PublicKeyToken=null'. 間違ったフォーマットのプログラムを読み込もうとしました。
というエラーになってしまい困った。BadImageFormatExceptionという例外は64ビット用と32ビット用のバイナリが混在していると投げられるようなのだが、混在はしていない。
次の記事に解決方法が書かれていた。
C++/CLIのDLLを使う場合、DLLだけ持ってきてもダメで、同じ位置に出力されるIjwhost.dllもDLLと同じ位置に持っていかないとダメだそうです。
この通り、C++/CLIのプロジェクトのビルド結果に含まれていたIjwhost.dllを実行ファイルと同じフォルダにコピーしてみたところ、正しく動くようになった。
Ijwhost.dllについては情報があまり見つからない。
- C++/CLI プロジェクトを .NET Core または .NET 5 に移植する方法 (learn.microsoft.com)
の「MSBuild なしでビルドする」節で次のように言及されているくらい。
3. リンクするときに、.NET Core アプリのホスト ディレクトリを LibPath として指定します (ijwhost.lib が検出されるようにします)。
4. ijwhost.dll を (.NET Core アプリのホスト ディレクトリから) プロジェクトの出力ディレクトリにコピーします。
2023年02月21日(火) [長年日記]
■ [c++] OpenMPの処理結果をスレッド番号順に並べる
先日書いたOpenMPの例は、作られるvectorの要素の順序が毎回変わってしまう。毎回同じ結果を得るにはどうしたらいいか。
- openmp ordering critical sections (Stack Overflow)
の回答を見てなるほどと思った。スレッド数分だけループして、#pragma omp orderedで順番に処理させている。
先日の例に適用してみる。「schedule(static,1)」は指定してもしなくても同じだと思うので指定していない。
#include <iostream> #include <vector> #include <omp.h> std::vector<int> make_vector(int size) { std::vector<int> result; #pragma omp parallel { std::vector<int> local; #pragma omp for nowait for (int i = 0; i < size; ++i) { if (i % 2 == 0) local.push_back(i); } #pragma omp for ordered nowait for (int i = 0; i < omp_get_num_threads(); ++i) { #pragma omp ordered result.insert(result.end(), local.begin(), local.end()); } } return result; } int main() { const auto max_threads = omp_get_max_threads(); std::cout << "max_threads = " << max_threads << std::endl; const auto v = make_vector(max_threads * 10); std::cout << "v.size = " << v.size() << std::endl; for (auto e : v) { std::cout << e << ", "; } std::cout << std::endl; }
max_threads = 6 v.size = 30 0, 2, 4, 6, 8, 10, 12, 14, 16, 18, 20, 22, 24, 26, 28, 30, 32, 34, 36, 38, 40, 42, 44, 46, 48, 50, 52, 54, 56, 58,
2023年02月14日(火) [長年日記]
■ [dev] ROS2でノードに対するテストを書く
ROS2にlaunch_pytestというものがあることを知った。これを使えばノードを立ち上げて外部からその振る舞いをテストすることができる。
Writing a simple publisher and subscriber (Python)にあるMinimalPublisherをテストするコードをメモ。
まず、テスト対象ノードと通信するテスト用ノードを作成する。topicトピックを購読して、受信したメッセージをキューに保存するだけ。
from queue import Queue from rclpy.node import Node from std_msgs.msg import String class DummyNode(Node): def __init__(self): super().__init__("dummy_node") self.topic_queue = Queue() self.topic_subscription = self.create_subscription( String, "topic", self._receive_topic, 10 ) def _receive_topic(self, msg): self.topic_queue.put(msg)
テスト本体は次の通り。
import queue import launch import launch_pytest import launch_ros import pytest import rclpy from concurrent.futures import ThreadPoolExecutor from dummy_node import DummyNode @launch_pytest.fixture def talker_description(): return launch.LaunchDescription( [ launch_ros.actions.Node( executable="talker", package="py_pubsub", output="screen", ), ] ) @pytest.fixture(scope="session") def thread_pool(): pool = ThreadPoolExecutor() yield pool pool.shutdown() @pytest.fixture def dummy_node(thread_pool): rclpy.init() node = DummyNode() thread_pool.submit(rclpy.spin, node) yield node node.destroy_node() rclpy.shutdown() @pytest.mark.launch(fixture=talker_description) def test_talker(dummy_node): for i in range(10): try: msg = dummy_node.topic_queue.get(timeout=2.0 / (i + 1)) assert msg.data == f"Hello World: {i}", f"index={i}" except(queue.Empty): pytest.fail(f"no message. index={i}")
- @launch_pytest.fixture を指定して、launch.LaunchDescriptionの配列を返す関数を定義する。ここにテスト対象ノードの情報を記述することで、テスト時にそのノードが立ち上がる。配列なので複数のノードを立ち上げ可能。
- dummy_nodeというフィクスチャで先に定義したDummyNodeノードを生成して立ち上げている。ThreadPoolExecutorを使って別スレッドで動くようにしている。
- テストは @pytest.mark.launch で修飾し、先に定義したlaunch.LaunchDescription配列を指定する。メッセージを受信する間隔がだんだん短くなるようにしているので、ループの5回目くらいでテストが失敗する。