ROS 2のC++プログラムをVS Codeで自動フォーマットする方法

ROS

この記事の内容

ROS 2プログラムを書いているときに以下のようなことはないでしょうか。

  • 開発メンバーごとに、インデントやスペースの開け方が違い、統一感がない
  • コーディングスタイルを覚えるのに時間がかかる、忘れる
  • コーディングスタイルに沿って書かれているか確認するのに時間がかかる

上記のような問題を解決するために、本記事ではVS Codeで自動的にコード成形を行う方法を解説します。

ROS 2では基本的に、Google C++ Style Guideに沿った形でのコーディングが推奨されます。この形式でVS Codeで保存した場合に自動フォーマットを行う方法をここでは記載します。

Code style and language versions — ROS 2 Documentation: Rolling documentation

手順

フォーマットを行うためのツールとしてclang-formatを使用します。

clang-formatは、C, C++, Objective-C, Javaなどのプログラムを整形するツールです。一貫したコーディングスタイルを維持することでコードの読みやすさを向上させ、バグの発生を減らします。clang-formatは、設定ファイルを通じて整形ルールをカスタマイズ可能で、チームやプロジェクトのコーディング規約に合わせてコードを整形できます。これにより、プロジェクト全体で一貫したコードの見た目を維持することが可能になります。

以下のようなイメージです。

clang-formatを使用するために、以下のコマンドでインストールします。

sudo apt install clang-format

clang-formatを単体で使用するには以下の手順で使用します。

フォルダを作成し、その下にフォーマットしたいファイルを置きます。ここでは、publisher_member_function.cppという名前で以下の内容を保存しておきます。フォーマット結果がわかりやすいように、Hello, worldの後の部分に余分な空白を入れたり、 RCLCPP_INFO(this->get_logger(), “Publishing: ‘%s'”, message.data.c_str());の行の先頭に余分な空白をいれています。

#include <chrono>
#include <functional>
#include <memory>
#include <string>
#include "rclcpp/rclcpp.hpp"
#include "std_msgs/msg/string.hpp"

using namespace std::chrono_literals;

/* This example creates a subclass of Node and uses std::bind() to register a
* member function as a callback from the timer. */

class MinimalPublisher : public rclcpp::Node
{
public:
  MinimalPublisher() : Node("minimal_publisher"), count_(0)
  {
    publisher_ = this->create_publisher("topic", 10);
    timer_ = this->create_wall_timer(500ms, std::bind(&MinimalPublisher::timer_callback, this));
  }

private:
  void timer_callback()
  {
    auto message = std_msgs::msg::String();
    message.data = "Hello, world!"    +     std::to_string(count_++);
        RCLCPP_INFO(this->get_logger(), "Publishing: '%s'", message.data.c_str());
    publisher_->publish(message);
  }
  rclcpp::TimerBase::SharedPtr timer_;
  rclcpp::Publisher::SharedPtr publisher_;
  size_t count_;
};

int main(int argc, char * argv[])
{
  rclcpp::init(argc, argv);
  rclcpp::spin(std::make_shared());
  rclcpp::shutdown();
  return 0;
}

ROS 2のC++のフォーマットに成形するために、.clang-formatを作成します。ament_clang_format内の設定を使用させていただきます。以下の内容を.clang-formatというファイル名でフォルダに保存します。

---
Language: Cpp
BasedOnStyle: Google

AccessModifierOffset: -2
AlignAfterOpenBracket: AlwaysBreak
BraceWrapping:
  AfterClass: true
  AfterFunction: true
  AfterNamespace: true
  AfterStruct: true
BreakBeforeBraces: Custom
ColumnLimit: 100
ConstructorInitializerIndentWidth: 0
ContinuationIndentWidth: 2
DerivePointerAlignment: false
PointerAlignment: Middle
ReflowComments: false

参考

ament_lint/ament_clang_format/ament_clang_format/configuration/.clang-format at humble · ament/ament_lint
Contribute to ament/ament_lint development by creating an account on GitHub.

cppファイルがあるディレクトリにて、以下のコマンドを実行します。

clang-format -i publisher_member_function.cpp

以下のように成形されていれば成功です。

VS Codeでファイルを保存した際に、clang-formatで自動でフォーマットさせるために以下の設定を行います。

C/C++の拡張機能をインストールしていることが前提です。

VS Codeのコマンドパレットを開き(Ctrl+Shift+P), Preferences: Open User Settings (JSON)を選択します。

開かれたsettings.jsonに以下の設定を追加します。

"C_Cpp.formatting": "clangFormat",
"editor.formatOnSave": true

人によって設定が違うと思いますが、追加後のsettings.jsonは以下のようなイメージになります。

これで、Ctrl + Sなどで保存した際に、フォーマットされるようになるかと思います。.clang-formatが読み込まれていることを確認するには、ColumnLimitを100から50に変えてみるなどすると、行ごとの最大文字数が変わるのでわかりやすくてよいかもしれません。

clangは環境変数が通っているパスに配置されている場合はVS Codeでそれが使われます。インストールされていない安倍はVS CodeのC/C++の拡張機能にバンドルされているclangが使用されます。任意のclangを使用したい場合は、C_Cpp.Clang_format_pathを指定します。

.clang-formatのどれが使われるのかは、以下のような順で解決しているようです。(細かくは未確認)

  • 対象ファイルと同ディレクトリの.clang-formatがあれば使用する
  • なければ、上位のフォルダの.clang-formatを使用する
  • それでもなければ、デフォルトの.clang-formatが使用される。

運用としては、ソース管理するGitリポジトリのルートあたりに.clang-formatを配置しておけばよいのではと考えます。

コメント