はじめてのエクステンション

付け焼き刃の C++ でエクステンション！
間違ってたら教えてくらはい。

## ファイル構成

ではでは、ログ出すだけのエクステンションを作っていきましょう。
最終的なファイル構成はこんな感じになります。

```
testextension
│  CMakeLists.txt
│  TestExtension.cpp
│
└─src
        CMakeLists.txt
        TestExtensionService.cpp
        TestExtensionService.h
```

## testextension ディレクトリ

ここにはファイルがふたつ。

- CMakeLists.txt
- TestExtension.cpp

### CMakeLists.txt ファイル

ビルドに含まれるよう追加する。

```cmake
cmake_minimum_required(VERSION 3.14)

catapult_define_extension(testextension)
```

### TestExtension.cpp ファイル

```cpp:TestExtension.cpp
#include "src/TestExtensionService.h"
#include "catapult/extensions/ProcessBootstrapper.h"

namespace catapult { namespace testextension {

	namespace {
		void RegisterExtension(extensions::ProcessBootstrapper& bootstrapper) {
			// register service(s)
			bootstrapper.extensionManager().addServiceRegistrar(CreateTestExtensionServiceRegistrar());
		}
	}

}}

extern "C" PLUGIN_API
void RegisterExtension(catapult::extensions::ProcessBootstrapper& bootstrapper) {
	catapult::testextension::RegisterExtension(bootstrapper);
}
```

#### こういう仕組み？

```cpp
extern "C" PLUGIN_API
void RegisterExtension(catapult::extensions::ProcessBootstrapper& bootstrapper) {
	catapult::testextension::RegisterExtension(bootstrapper);
}
```

ここからから始まる。
catapult::testextension の部分は変わるけれども。このブロックは固定。

```cpp
namespace catapult { namespace testextension {

	namespace {
		void RegisterExtension(extensions::ProcessBootstrapper& bootstrapper) {
			// register service(s)
			bootstrapper.extensionManager().addServiceRegistrar(CreateTestExtensionServiceRegistrar());
		}
	}

}}
```

エクステンションのメインメソッドと思って良いところ。

## src ディレクトリ

### CMakeLists.txt

```cmake
cmake_minimum_required(VERSION 3.14)

catapult_define_extension_src(testextension)
```

### TestExtensionService.h ファイル

```cpp:TestExtensionService.h
#pragma once
#include "catapult/extensions/ServiceRegistrar.h"

namespace catapult { namespace testextension {

	DECLARE_SERVICE_REGISTRAR(TestExtension)();

}}
```

### TestExtensionService.cpp ファイル

```cpp:TestExtensionService.cpp
#include "TestExtensionService.h"
#include "catapult/extensions/ServiceState.h"

namespace catapult { namespace testextension {

	namespace {
		class TestExtensionServiceRegistrar : public extensions::ServiceRegistrar {
		public:
			extensions::ServiceRegistrarInfo info() const override {
				CATAPULT_LOG(debug) << "TestExtensionServiceRegistrar class info start ";
				return { "TestExtension", extensions::ServiceRegistrarPhase::Post_Packet_Io_Pickers };
			}

			void registerServiceCounters(extensions::ServiceLocator& locator) override {
				CATAPULT_LOG(debug) << "TestExtensionServiceRegistrar class registerServiceCounters start ";
			}

			void registerServices(extensions::ServiceLocator&, extensions::ServiceState& state) override {
				// add packet handlers
				CATAPULT_LOG(debug) << "TestExtensionServiceRegistrar class registerServices start ";
			}
		};
	}

	DECLARE_SERVICE_REGISTRAR(TestExtension)() {
		return std::make_unique<TestExtensionServiceRegistrar>();
	}
}}
```

#### こういう仕組み？

##### info メソッド

```cpp
extensions::ServiceRegistrarInfo info() const override {
	CATAPULT_LOG(debug) << "TestExtensionServiceRegistrar class info start ";
	return { "TestExtension", extensions::ServiceRegistrarPhase::Post_Packet_Io_Pickers };
}
```

サービス登録に関する情報である ServiceRegistrarInfo 構造体を返す。
登録されるまで何回か呼ばれる？

ServiceRegistrarInfo 構造体は以下のような形をしている。

- 登録名
- 登録フェーズ

登録名は任意の文字列。他と衝突しなければ何でも良いかと思われる。
登録フェーズは Catapult が起動する時、どのタイミングで読み込まれるかを指定する（依存関係解決のため）。ServiceRegistrarPhase は下記の設定が出来る。

| 項目                            | 説明                                                                                                                                                                                                          |
| ------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| Initial_With_Modules            | 実行可能なモジュールを登録する最初のフェーズ(依存関係なし)。これにより、モジュールをロードするサービスは最後にアンロードされる。                                                                              |
| Initial                         | 第一段階（依存関係なし）。                                                                                                                                                                                    |
| Post_Transaction_Event_Handlers | トランザクション・イベント・ハンドラの登録が必要。                                                                                                                                                            |
| Post_Remote_Peers               | リモートピア（およびエンティティシンク）が登録されている必要がある。                                                                                                                                          |
| Post_Range_Consumers            | 基本レンジの消費者（および工場）に登録を義務付ける。                                                                                                                                                          |
| Post_Range_Consumers_Phase_Two  | 基本レンジの消費者（および工場）に登録を義務付ける（第 2 段階）。                                                                                                                                             |
| Post_Extended_Range_Consumers   | 拡張範囲コンシューマー(およびファクトリー)が登録されている必要があります。これらは通常エクステンションによって設定される。Post_Packet_Io_Pickers すべてのパケット io ピッカーが登録されている必要があります。 |
| Post_Packet_Handlers            | すべてのパケットハンドラが登録されている必要がある。                                                                                                                                                          |
| Post_Tasks                      | すべてのタスクの登録が必要。                                                                                                                                                                                  |

##### registerServiceCounters メソッド

```cpp
void registerServiceCounters(extensions::ServiceLocator& locator) override {
	CATAPULT_LOG(debug) << "TestExtensionServiceRegistrar class registerServiceCounters start ";
	// no additional counters
}
```

ソースコメントは「サービス依存カウンタをロケータに登録する。」となっている。
使用していないエクステンションもある。ちょっとまだよく分からない。

##### registerServices メソッド

```cpp
void registerServices(extensions::ServiceLocator&, extensions::ServiceState& state) override {
	// add packet handlers
	CATAPULT_LOG(debug) << "TestExtensionServiceRegistrar class registerServices start ";
}
```

ソースコメントは「ロケータが指定された状態で従属サービスを起動し、登録する。」
今回は登録してないけど、サービスを登録する部分のようだ。

## extensions ディレクトリ

### CMakeLists.txt

さらに上位ディレクトリである、extensions ディレクトリの CMakeLists.txt にも追加しておく必要がある。
ファイル末尾に`add_subdirectory(testextension)`を追加。

```cmake
cmake_minimum_required(VERSION 3.14)

include_directories(.)
include_directories(${PROJECT_SOURCE_DIR} ${PROJECT_SOURCE_DIR}/src)

function(catapult_define_extension_no_group EXTENSION_NAME)
	add_subdirectory(src)
	catapult_add_tests_subdirectory(tests)

	catapult_shared_library_target(extension.${EXTENSION_NAME})
	target_link_libraries(extension.${EXTENSION_NAME} catapult.${EXTENSION_NAME})
	set_property(TARGET extension.${EXTENSION_NAME} PROPERTY FOLDER "extensions")
endfunction()

function(catapult_define_extension EXTENSION_NAME)
	catapult_define_extension_no_group(${EXTENSION_NAME})
	add_dependencies(extensions extension.${EXTENSION_NAME})
endfunction()

function(catapult_define_extension_src EXTENSION_NAME)
	catapult_library_target(catapult.${EXTENSION_NAME} ${ARGN})
	target_link_libraries(catapult.${EXTENSION_NAME} catapult.extensions catapult.handlers catapult.net)
	set_property(TARGET catapult.${EXTENSION_NAME} PROPERTY FOLDER "extensions")
endfunction()

function(catapult_define_extension_test EXTENSION_NAME)
	catapult_test_executable_target(tests.catapult.${EXTENSION_NAME} local ${ARGN})
	set_property(TARGET tests.catapult.${EXTENSION_NAME} PROPERTY FOLDER "extensions")

	# need to promote custom test compiler flags to parent scope
	set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS}" PARENT_SCOPE)
endfunction()

add_subdirectory(addressextraction)
add_subdirectory(diagnostics)
add_subdirectory(filespooling)
add_subdirectory(finalization)
add_subdirectory(harvesting)
add_subdirectory(hashcache)
add_subdirectory(mongo)
add_subdirectory(networkheight)
add_subdirectory(nodediscovery)
add_subdirectory(packetserver)
add_subdirectory(partialtransaction)
add_subdirectory(pluginhandlers)
add_subdirectory(sync)
add_subdirectory(syncsource)
add_subdirectory(timesync)
add_subdirectory(transactionsink)
add_subdirectory(unbondedpruning)
add_subdirectory(zeromq)
add_subdirectory(testextension)
```

## ビルド

あとは Catapult をマニュアルビルドしてください。

参考

https://qiita.com/ccHarvestasya/items/b49825cabc6691778b42

## 実行の前に

実行する前に`config-extensions-server.properties`に作成したエクステンションを有効にする設定を記述する。
ファイル末尾に`extension.testextension = true`を追加。

```config-extensions-server.properties
[extensions]

# api extensions
extension.filespooling = false
extension.partialtransaction = false

# addressextraction must be first because mongo and zeromq depend on extracted addresses
extension.addressextraction = false
extension.mongo = false
extension.zeromq = false

# p2p extensions
extension.harvesting = true
extension.syncsource = true

# common extensions
extension.diagnostics = true
extension.finalization = true
extension.hashcache = true
extension.networkheight = false
extension.nodediscovery = true
extension.packetserver = true
extension.pluginhandlers = true
extension.sync = true
extension.timesync = true
extension.transactionsink = true
extension.unbondedpruning = true
extension.artificialidiocy = false
extension.testextension = true
```

## 実行する

参考

https://qiita.com/ccHarvestasya/items/99cf1dad7eb21ed6a86e