ふるお〜と!- FullAuto

AI・ロボットが普及しBI(ベーシックインカム)が早急に実現されることを願う元ニートのブログ

ふるお〜と!-FullAuto

C++コード読解

Ignition Gazeboに以下のようなコードがある。

void Model::SetWorldPoseCmd(EntityComponentManager &_ecm,
    const math::Pose3d &_pose)
{
  auto poseCmdComp = _ecm.Component<components::WorldPoseCmd>(
      this->dataPtr->id);
  if (!poseCmdComp)
  {
    _ecm.CreateComponent(this->dataPtr->id, components::WorldPoseCmd(_pose));
  }
  else
  {
    poseCmdComp->SetData(_pose,
        [](const math::Pose3d &, const math::Pose3d &){return false;});
    _ecm.SetChanged(this->dataPtr->id,
        components::WorldPoseCmd::typeId, ComponentState::OneTimeChange);
  }
}

イミフメイ。

ひとつわかることは、これはC++上級者が書いたコードだ。

(·∀·)つ【改訂版】「組込みソフトウェア開発向けコーディング作法ガイド ESCR [C++言語版] Ver. 2.0」の発行:IPA 独立行政法人 情報処理推進機構
なになにC++のコーディング規約とな。

プログラムはシンプルに書く

ぜんっぜんシンプルじゃない!

やりたいことはシンプル

やりたいことは簡単。モデルを動かす。

上記の実装によれば、
モデルクラスはSetWorldPoseCmdメソッドに対し、 EntityComponentManagerとPose3dを引数として渡す必要がある。

(·∀·)まぁ、それはヘッダかドキュメント見ればわかるよ。

GazeboではSetWorldPoseにマネジャークラスを渡す必要はなかったのだが、
Ignitionでは渡す実装になっている...。
複雑になってるよ...。

Next step

auto poseCmdComp = _ecm.Component<components::WorldPoseCmd>(this->dataPtr->id);

auto? 安心しろ...静的型付言語なので絶対に何らかの型はある!!
ちなみにJavascript界隈では大規模開発に不向きということで、Typescriptが流行っているようだ。

とりあえず、メソッドをVS CodeでPeek!! f:id:nullpo24:20210107184743p:plain

出てこない!?
なるほど、実装がトリッキーすぎてVS Codeでも追えないということか。

EntityComponentManager.hh

      /// \brief Get a mutable component based on a key.
      /// \param[in] _key A key that uniquely identifies a component.
      /// \return The component associated with the key, or nullptr if the
      /// component could not be found.
      public: template<typename ComponentTypeT>
              ComponentTypeT *Component(const ComponentKey &_key);

components名前空間にあるcomponentの型をテンプレート戻り値として返す。
したがって、戻り値の型はautoはcomponents::WorldPoseCmdとなる。

Next step

poseCmdComp->SetData(_pose, [](const math::Pose3d &, const math::Pose3d &){return false;});

すげぇ、ぱっと見いくつのパラメータがあるのかさえわからない。
SetDataをPeek!!

  template <typename DataType, typename Identifier, typename Serializer>
  bool Component<DataType, Identifier, Serializer>::SetData(
      const DataType &_data,
      const std::function<bool(const DataType &, const DataType &)> &_eql)
  {
    bool result = !_eql(_data, this->data);
    this->data = _data;
    return result;
  }

どうやら2つのパラメータがある。
そして、C++11から導入されたstd::functionを使っている。
また、C++

[]{};

のような記法が出てきたらラムダ式となる。 std::functionの使い方 in C++ - Qiita を参考に書き換えると、

std::function<bool(const DataType &, const DataType &)> _eql  = [](const math::Pose3d &, const math::Pose3d &){return false;};

となり、ラムダ式を代入した_eqlを使用して評価する。
つまり、SetDataしても同じだったらfalseを返すよ。
ということか。

今度はtemplate3つ使用している。
・DataType
・Identifier
・Serializer

Datatype:今回はPose3dとして暗黙的される
Identifier,SerializerはSetDataメソッドのために書かれているわけではない。
Identifierはエイリアステンプレートで明示される。
PoseCmd.hh

using WorldPoseCmd = Component<math::Pose3d, class WorldPoseCmdTag>;

Serializerはデフォルトテンプレート引数として以下のように指定されている。

typename Serializer = serializers::DefaultSerializer<DataType>

Next step

_ecm.SetChanged(this->dataPtr->id,
        components::WorldPoseCmd::typeId, ComponentState::OneTimeChange);

変更されたコンポーネントを_ecmへセットする。
typIdはInline Staticのため上記のような表記となる。

それでも理解できない場合

ign-gazebo/test/integrationにテスト用コードが用意されている。
CMakeでビルド後、
ign-gazebo/build/binの中に実行バイナリが生成されているので、
それを動かせばオッケー。