C++ 参照渡しの勉強 ④ ROS2　その1 cmakeができるようにする。 subscriberのsub001.cpp

　ROS2のビルド・ツールcolconがうまく動かないので、cmakeでビルドできるようにします。

 ### 環境

- Raspberry Pi 5 8GB
- 追加ボード；NVMe Base for Raspberry Pi 5 (NVMe Base by Pimoroni)
- Crucial クルーシャル P2シリーズ 500GB 3D NAND NVMe PCIe M.2 SSD CT500P2SSD8
- Ubuntu Desktop ***24.04LTS***(64-bit)
- ROS2 **Jazzy**

 ### 準備
 　Windows10のコマンドプロンプトからsshで入ります。
```
ssh yoshi.local
$ cd works
$ nano sub001.cpp
```
　前に使ったsubscriberのサンプル・プログラムです。

 https://qiita.com/jamjam/items/50f3bc352e4cd0a7959d
 
```sub001.cpp
#include "rclcpp/rclcpp.hpp"
#include "std_msgs/msg/string.hpp"
#include <iostream>

rclcpp::Node::SharedPtr g_node = nullptr;

void callback(const std_msgs::msg::String::SharedPtr msg)
{
  RCLCPP_INFO(g_node->get_logger(), "%s", msg->data.c_str());
}

int main(int argc, char **argv)
{
  std::cout << "start subscribe test\n");

  rclcpp::init(argc, argv);
  g_node = rclcpp::Node::make_shared("my_subscriber");
  auto subscriber = g_node->create_subscription<std_msgs::msg::String>("greeting", 1, callback);

  rclcpp::spin(g_node);

  g_node = nullptr;
  rclcpp::shutdown();
  return 0;
}
```

 プログラム最初の記述です。
```
 rclcpp::Node::SharedPtr g_node = nullptr;
```

 　C++11から **nullptr**  は、ヌルポインタ値を表すキーワードで、ポインタが何も指していないことを示すために使われるようです。それまでの古いプログラムでは **0** を代入していました。何か不都合が起こるようになったので、定義されたようです。null＝ヌルは日本語読みですが、日本では一般的です。英語ではナルと発音するるらしいですが、聞いたことがないので...

https://jp-seemore.com/iot/26206/

　つぎは、callback関数のところです。わかる限り説明を試みます。string型のメッセージを受け取るときの定番の書き方です。
```
void callback(const std_msgs::msg::String::SharedPtr msg)
{
  RCLCPP_INFO(g_node->get_logger(), "%s", msg->data.c_str());
}
```

　SharedPtr エイリアスは、ROS1 時代からの遺産だそうです。std::shared_ptr<>と書くより短い？から使われている？その当時のC++には存在していなかったから、とか。


　**shared_ptr**はスマートポインタ(C++11以上）と呼ばれます。リソースへのポインタの所有権を共有するものがshared_ptrと記述され、占有するものがunique_ptrだそうです。unique_ptrは見かけません。
 　スマートポインタとは、メモリの解放忘れや、不適切な対象への deleteの実行をなくすために、確保したメモリを自動的に解放してくれるクラスだそうです。

 https://www.youtalk.jp/2017/06/12/ros2-cpp11-14.html

 　C++ においては, 安全のため積極的に const を用いるべきという解説を見ます。const型は内容を書き換えられないらしいが、どこを修飾しているの？
  この記述は、ポインタ渡し？　参照渡し？ ＆や　＊がない。
  関数内で、->を使ってメンバに代入しているので、ポインタ渡しの書き方のようなので、このcallback関数はポインタ渡しだろう。

  いってみれば、
```
void callback(std::string *msg) 
```
と同じなのかな？ポインタ渡しの勉強が足らん。
　とすると、ROS2のcallback関数を理解しようとして、「C++ 参照渡しの勉強」というタイトルをつけてたけど、うそになる、「C++ ポインタ渡しの勉強」が正しい！


``RCLCPP_INFO``はROSで使われるprintfみたいなマクロのようです。ログレベルを指定してログを出力します。
　``msg->data.c_str())``　dataはrclcpp.hppで公開されている変数名で、string型なので、c_str()で印字？できる型に変換しているという解釈であっているかな。

　main()は、ものすごく一般的なので、スルーします。もっと勉強が進めば、それぞれの部分の解説ができるようになるかもしれません。
 　でも、一言。
```
  rclcpp::spin(g_node);
```
　waitしているんだから、割り込みのcallback関数なんか書かずに、ポーリングすればいいじゃないかと思う。と、こんなサンプルのような短いプログラムでは思ってしまった。

```
$ nano CMakeLists.txt
```

```CMakeLists.txt
cmake_minimum_required(VERSION 3.8)

project(worksfirst)

set(CMAKE_CXX_STANDARD 11)
set(CMAKE_CXX_STANDARD_REQUIRED True)

if(CMAKE_COMPILER_IS_GNUCXX OR CMAKE_CXX_COMPILER_ID MATCHES "Clang")
 add_compile_options(-Wall -Wextra -Wpedantic)
endif()

# find dependencies
find_package(ament_cmake REQUIRED)
find_package(rclcpp REQUIRED)
find_package(rclcpp_components REQUIRED)
find_package(std_msgs REQUIRED)

add_executable(sub001 sub001.cpp)

ament_target_dependencies(sub001 rclcpp std_msgs)

install(
 TARGETS sub001
 DESTINATION lib/${PROJECT_NAME}
)

ament_package()
```

```
$ nano package.xml
```
　CMakeLists.txtの``project(worksfirst)``に合わせて、``<name>worksfirst</name>``に修正しただけです。
```package.xml
<?xml version="1.0"?>
<?xml-model href="http://download.ros.org/schema/package_format3.xsd" schematypens="http://www.w3.org/2001/XMLSchema"?>
<package format="3">
  <name>worksfirst</name>
  <version>0.0.0</version>
  <description>TODO: Package description</description>
  <maintainer email="yoshi@todo.todo">yoshi</maintainer>
  <license>TODO: License declaration</license>

  <buildtool_depend>ament_cmake</buildtool_depend>

  <test_depend>ament_lint_auto</test_depend>
  <test_depend>ament_lint_common</test_depend>

  <export>
    <build_type>ament_cmake</build_type>
  </export>
</package>
```
 　cmakeとmakeで実行ファイルsub001をビルドします。
  このsubscriberを起動し、``ros2 topic pub``コマンドで強制的にメッセージをpublishします。subscribeが2回おこっていて変ですが、とりあえず動くことが確認できました。
```
$ source /opt/ros/jazzy/setup.bash
$ cmake -S . -B build
$ cd build
$ make
$ ./sub001 &
start subscribe test
$ ros2 topic pub /greeting std_msgs/msg/String "data: 'abc'"
publisher: beginning loop
publishing #1: std_msgs.msg.String(data='abc')

[INFO] [1724574852.371661583] [my_subscriber]: abc
[INFO] [1724574852.371670046] [my_subscriber]: abc
publishing #2: std_msgs.msg.String(data='abc')

[INFO] [1724574853.372361308] [my_subscriber]: abc
[INFO] [1724574853.372361512] [my_subscriber]: abc
publishing #3: std_msgs.msg.String(data='abc')

[INFO] [1724574854.372281871] [my_subscriber]: abc
[INFO] [1724574854.372281760] [my_subscriber]: abc
publishing #4: std_msgs.msg.String(data='abc')

[INFO] [1724574855.372286804] [my_subscriber]: abc
[INFO] [1724574855.372286915] [my_subscriber]: abc
publishing #5: std_msgs.msg.String(data='abc')
```

　関係する部分だけ。　
```

$ tree
.
├── auto
├── build
　　。。。
│   ├── Makefile
│   └── sub001
　　。。。

├── CMakeLists.txt
├── package.xml
├── sub001.cpp
　　。。。
```

:::note info
（※）**ros2 topic pub**コマンドの2番目の引数では型を指定します。

```
std_msgs/msg/String
```
と記述していますが、実行時には、
```
std_msgs.msg.String
```
と表示が変化しています。元のソースでは、
```
std_msgs::msg::String
```
だし、記述の方法がわかるまでX時間もかかった。
:::


　ここで作ったsubscriber（sub001）を起動し、topicのリストをとると、/greetingが表示されたので、ros2に認識されているようです。
　デフォルトでは、標準エラー出力と、~/.rosディレクトリ配下のファイル、/rosoutトピックにログを出力するそうです。
```
yoshi@yoshi:~/works/build$ ./sub001 &
[1] 41384
yoshi@yoshi:~/works/build$ start subscribe test
ros2 topic list
/greeting
/parameter_events
/rosout
```

 ------

 https://qiita.com/jamjam/items/d9dfb1819b8ca513ac03

https://qiita.com/jamjam/items/cca4f181397daeac1ac2

https://qiita.com/jamjam/items/e7a734f85f4554ba6273

https://qiita.com/jamjam/items/28cb7fcd678b39378090  
  
https://qiita.com/jamjam/items/6c0b48747fd405989917