[USD/C++] CMake USD 用語集

# 用語集

こちらは [[USD/C++] ArPathmapResolverを作ろう [Github]](https://qiita.com/yu_kita/items/a95137829aa5a27c857d) の記事の補助として用語の説明をまとめた記事になります
CMakeやUSDでのマクロや、オプションについてできるだけ説明しています

## Cmake
https://qiita.com/shohirose/items/45fb49c6b429e8b204ac

### Environment

#### CMAKE_GENERATOR

:::note
プロジェクトのビルドに使用するジェネレータを指定

コマンドラインの `-G` とほぼ同等
```
// コマンドライン
cmake -G "Visual Studio 16 2019"
cmake -G "Unix Makefiles"
//環境変数の場合
set CMAKE_GENERATOR="Visual Studio 16 2019"
export CMAKE_GENERATOR="Unix Makefiles"
```
:::

:::note warn
変数の方にも `CMAKE_GENERATOR` はあるがそちらは __非推奨__
:::

https://cmake.org/cmake/help/latest/envvar/CMAKE_GENERATOR.html

https://cmake.org/cmake/help/latest/manual/cmake-generators.7.html

### Variables

#### CMAKE_\<LANG>\_COMPILER

:::note
\<LANG\> 用のコンパイラをフルパスで指定します

```cmake
set(CMAKE_CXX_COMPILER "/usr/local/bin/gcc")
set(CMAKE_CXX_COMPILER "C:\MinGW\bin\gcc.exe")
set(CMAKE_CXX_COMPILER "C:\Program Files (x86)\Microsoft Visual Studio\2019\Community\VC\Tools\MSVC\14.29.30133\bin\Hostx64\x64\cl.exe")
```
:::

https://cmake.org/cmake/help/latest/variable/CMAKE_LANG_COMPILER.html

#### CMAKE_BUILD_TYPE

:::note
ジェネレーターのビルドタイプを指定します

```cmake:CMakeLists.txt
set(CMAKE_BUILD_TYPE Debug)  # または Release
```

```text:BUILD_TYPE
Debug          : 最適化なし、デバッグ情報あり
Release        : 最適化あり、デバッグ情報なし
RelWithDebInfo : 最適化あり、デバッグ情報あり
MinSizeRel     : 最小サイズで最適化、デバッグ情報なし
```
:::

:::note warn
`CMAKE_BUILD_TYPE` は `project()` よりも前に記述する必要があります
:::

https://cmake.org/cmake/help/latest/variable/CMAKE_BUILD_TYPE.html

#### CMAKE_VERBOSE_MAKEFILE

:::note
ビルドプロセス中に実行されるコマンドを詳細に表示させる変数

```cmake
set(CMAKE_VERBOSE_MAKEFILE ON)
```
:::

https://cmake.org/cmake/help/latest/variable/CMAKE_VERBOSE_MAKEFILE.html

#### CMAKE_BINARY_DIR / CMAKE_CURRENT_BINARY_DIR
:::note
CMakeでビルドしているディレクトリを返す変数

`CMAKE_BINARY_DIR` はトップレベルのディレクトのパスを返します

[`add_subdirectory()`](#add_subdirectory) によって追加されたディレクトリを取得する場合は `CMAKE_CURRENT_BINARY_DIR`を使用します
:::

https://cmake.org/cmake/help/latest/variable/CMAKE_BINARY_DIR.html

https://cmake.org/cmake/help/latest/variable/CMAKE_CURRENT_BINARY_DIR.html

#### CMAKE_INSTALL_PREFIX
:::note
[`install()`](#install)コマンドを使用した時のインストールディレクトリを指定

```cmake
set(CMAKE_INSTALL_PREFIX "/path/to/install")
```

:::

https://cmake.org/cmake/help/latest/variable/CMAKE_INSTALL_PREFIX.html

#### CMAKE_EXPORT_COMPILE_COMMANDS
:::note
コンパイルコマンドを `compile_commands.json` というファイルにエクスポートするためのオプションです

```cmake
set(CMAKE_EXPORT_COMPILE_COMMANDS ON)
```

今回はVisual Studio Codeでのコード補完やインテリセンスを使用したいのでONにしています
:::

https://cmake.org/cmake/help/latest/variable/CMAKE_EXPORT_COMPILE_COMMANDS.html

#### CMAKE_MESSAGE_LOG_LEVEL

:::note
[`message()`](#message) コマンドで使用されるロギング・レベルを指定します
`STATUS` がデフォルトとして設定されています

```cmake
set(CMAKE_MESSAGE_LOG_LEVEL "DEBUG")
```

```cmake:TYPE
ERROR
WARNING
NOTICE
STATUS
VERBOSE
DEBUG
TRACE
```
:::

https://cmake.org/cmake/help/latest/variable/CMAKE_MESSAGE_LOG_LEVEL.html

#### CMAKE_CXX_STANDARD

:::note
CMakeで使用するC++コンパイラの標準を指定するための変数
```cmake
# C++17を使用する設定
set(CMAKE_CXX_STANDARD 17)
```
:::

https://cmake.org/cmake/help/latest/variable/CMAKE_CXX_STANDARD.html

#### CMAKE_CXX_STANDARD_REQUIRED

:::note
指定した C++ がサポートされていない場合に、ビルドを継続するか、エラーを発生させるかを決定します。
`True` に設定すると C++ がサポートしていない場合にエラーが発生します

```cmake
set(CMAKE_CXX_STANDARD_REQUIRED TRUE)
```

:::

https://cmake.org/cmake/help/latest/variable/CMAKE_CXX_STANDARD_REQUIRED.html

#### WIN32 / LINUX

:::note
OSを識別するための変数
他にもあるので、[こちら](https://cmake.org/cmake/help/latest/manual/cmake-variables.7.html#variables-that-describe-the-system)も確認

`LINUX` はCMake 3.25から追加されました

```cmake
if(WIN32)
    # Windows向けの設定
    message("Building on Windows")
elseif(LINUX)
    # Linux向けの設定
    message("Building on Linux")
else()
    # その他のプラットフォーム向けの設定
    message("Building on an unknown platform")
endif()
```
:::

https://cmake.org/cmake/help/latest/manual/cmake-variables.7.html#variables-that-describe-the-system

https://cmake.org/cmake/help/latest/command/if.html

#### BUILD_SHARED_LIBS
:::note
共有ライブラリ（.dll .soなど）をビルドするか、静的ライブラリ（.aや.libなど）をビルドするかを制御するためのオプション

```cmake
set(BUILD_SHARED_LIBS ON)
```

:::

https://cmake.org/cmake/help/latest/variable/BUILD_SHARED_LIBS.html

### Commnads

#### cmake_minimum_required

:::note
プロジェクトに必要な cmake の最小バージョンを設定します

`FATAL_ERROR` オプションを使用すると、指定したバージョン未満の場合はビルドを中止します
```cmake
cmake_minimum_required(VERSION 3.25 FATAL_ERROR)
```
:::

:::note warn
`cmake_minimum_required` は `project()` よりも前に記述する必要があります
:::

https://cmake.org/cmake/help/latest/command/cmake_minimum_required.html

#### project

:::note
プロジェクトの名前を設定します
プロジェクト名や言語設定を明確にするためにも必ず設定

```cmake: Option
# LANGUAGES    : 言語を明示的に指定
# VERSION      : プロジェクトにバージョン情報を付ける
# DESCRIPTION  : プロジェクトの簡単な説明を指定
# HOMEPAGE_URL : プロジェクトのホームページやリポジトリのURLを指定

# e.g.
project(Hoge VERSION 1.0.0 LANGUAGE C++)
```
:::

https://cmake.org/cmake/help/latest/command/project.html

#### message

:::note
指定されたメッセージテキストをログに表示します

`DEBUG` などを表示する場合は `CMAKE_MESSAGE_LOG_LEVEL` を設定します
```cmake
# STATUS         : 通常の情報を表示
# NOTICE         : STATUSよりも強調したい場合に使用
# VERBOSE        : より詳細な情報を表示するために使用
# DEBUG          : 開発者向けのメッセージを表示
# TRACE          : 細かいメッセージ
# WARNING        : 警告を表示
# AUTHOR_WARNING : 開発者向けの警告メッセージを表示
# SEND_ERROR     : エラーメッセージを表示。処理を続行
# FATAL_ERROR    : エラーメッセージを表示。処理を停止
# DEPRECATED     : 非推奨の機能や方法を使っている場合に警告を表示

message(DEBUG "This is a debug message.")
```
:::

https://cmake.org/cmake/help/latest/command/message.html

#### set

:::note
変数に値を設定したり、リストや環境変数なども設定できます
```cmake
# 通常の変数
set(MY_VAR "Hello!")

# リストの設定
set(MY_LIST "list1" "list2" "list3")

# 環境変数の設定
set(ENV{MY_ENV_VAR} "env")

# 子階層から上位のスコープ（親階層など）に変数を設定
set(MY_VAR "value" PARENT_SCOPE)

# CMakeキャッシュに値を設定する
# これで設定した場合はコマンドラインなどから上書きできる
set(MY_VAR "default_value" CACHE STRING "custom variable")
# cmake -D MY_VAR="new_value"
```
:::

https://cmake.org/cmake/help/latest/command/set.html

https://cmake.org/cmake/help/latest/command/list.html

#### add_executable

:::note
指定されたソースファイルを使ってプロジェクトに実行ファイルを追加するコマンド

```cmake
add_executable(my_program main.cpp)
```

:::

https://cmake.org/cmake/help/latest/command/add_executable.html

#### add_library

:::note
指定されたソースファイルを使ってプロジェクトにライブラリを追加するコマンド
下記オプションの指定ができます

```
add_library(mylibrary SHARED src/hoge.cpp src/fuga.cpp)
```

```
STATIC : 静的ライブラリ(.a や.lib)
SHARED : 動的ライブラリ(.soや.dll)
MODULE : 動的ライブラリに近いが、dlopenのような機能を使ってロードされる事を前提としたもの
```
:::

https://cmake.org/cmake/help/latest/command/add_executable.html

#### add_subdirectory

:::note
CMakeでサブディレクトリを追加するためのコマンド

このコマンドを使うことで、サブディレクトリ内で定義された `CMakeLists.txt` をメインのビルドに組み込むことができます

```cmake:CmakeLists.txt #1
# hoge
# ├── CMakeLists.txt #1
# └── src
#     └── CMakeLists.txt #2
add_subdirectory("src") #2を#1に組み込む事ができる
```
:::

https://cmake.org/cmake/help/latest/command/add_subdirectory.html

#### add_dependencies

:::note
ターゲット間の依存関係を設定するために使用するコマンド
設定をする事で、別のターゲットのビルドが完了するまで自身のビルドを行わないようにする事ができます
```cmake
add_executable(MyApp main.cpp)
add_library(MyLibrary STATIC my_library.cpp)

# MyApp に MyLibrary をリンク
target_link_libraries(MyApp MyLibrary)

# 依存関係の追加
# MyLibraryに依存しているので、MyAppをビルドする前にMyLibraryが先にビルドされます
add_dependencies(MyApp MyLibrary)
```
:::

https://cmake.org/cmake/help/latest/command/add_dependencies.html

#### add_compile_options / target_compile_options

:::note
CMakeでコンパイラのオプションを追加するためのコマンド

特定のターゲットに対して設定する場合は
`target_compile_options` を使用する
:::

https://cmake.org/cmake/help/latest/command/add_compile_options.html

https://cmake.org/cmake/help/latest/command/target_compile_options.html

#### add_compile_definitions / target_compile_definitions

:::note
CMakeでコンパイラのマクロ定義を設定するためのコマンド

特定のターゲットに対して設定する場合は
`target_compile_definitions` を使用する
:::

https://cmake.org/cmake/help/latest/command/add_compile_definitions.html

https://cmake.org/cmake/help/latest/command/target_compile_definitions.html

#### link_directories / target_link_directories

:::note
CMakeでリンクするライブラリの検索パスを指定するためのコマンド

特定のターゲットに対して設定する場合は
`target_link_directories` を使用する
:::

:::note warn
`link_directories` は指定されたディレクトリ内のすべてのライブラリをリンクする可能性があるので __非推奨__
:::

#### link_libraries / target_link_libraries

:::note
CMakeでリンクするライブラリを指定するためのコマンド

特定のターゲットに対して設定する場合は
`target_link_libraries` を使用する

`target_link_directories` でディレクトリを指定している場合は `lib` と 拡張子を除いた名前で記述できます、フルパスの場合はそのまま記述
```cmake
# libhoge.so (target_link_directoriesでディレクトリを指定済み)
# libfuga.so
target_link_libraries(<TARGET>
    PRIVATE
    hoge
    /path/to/lib/libfuga.so
)
```
:::

:::note warn
`link_libraries` は全てのターゲットに影響するので __非推奨__
:::

https://cmake.org/cmake/help/latest/command/link_libraries.html

https://cmake.org/cmake/help/latest/command/target_link_libraries.html

#### include_directories / target_include_directories

:::note
CMakeでヘッダーファイルの検索パスを指定するためのコマンド

特定のターゲットに対して設定する場合は
`target_include_directories` を使用する

:::

:::note warn
`include_directories` は全てのターゲットに影響するので __非推奨__
:::

https://cmake.org/cmake/help/latest/command/include_directories.html

https://cmake.org/cmake/help/latest/command/target_include_directories.html

#### target_〜の影響範囲

:::note
```
- PRIVATE   : ターゲット内部でのみ有効
- PUBLIC    : ターゲットとそのリンク先のターゲットに有効
- INTERFACE : ターゲット自体には影響しないが、リンクした別のターゲットに影響
```
:::

https://qiita.com/shohirose/items/45fb49c6b429e8b204ac

#### set_target_properties

:::note
特定のターゲットに対して、プロパティを指定するために使用するコマンド

例えば、ライブラリのプリフィックスを上書きする場合は下記で指定できる
```
set (MY_LIB "hoge")
add_library(${MY_LIB} SHARED fuga.cpp)
set_target_properties(${MY_LIB} PROPERTIES PREFIX "")
# libhoge.so -> hoge.so
```

プロパティの種類は他にもあるので、[こちら](https://cmake.org/cmake/help/latest/manual/cmake-properties.7.html#target-properties)を確認
:::

https://cmake.org/cmake/help/latest/command/set_target_properties.html

https://cmake.org/cmake/help/latest/manual/cmake-properties.7.html#target-properties

#### configure_file

:::note
入力ファイルの内容を変換しながら `input` ファイルを `output` ファイルにコピーするコマンド
```json:config.json.in
{
    "var1" : "@FOO@",
    "var2" : "@BAR@"
}
```
```cmake:CMakeLists.txt
cmake_minimum_required(VERSION 3.10)
project(MyProject VERSION 1.0)

# 変数のセット
set(FOO "hoge")
set(BAR "fuga" )

# configure_file を使って config.json.in を処理
configure_file(
  ${CMAKE_SOURCE_DIR}/config.json.in # 入力ファイル（テンプレート）
  ${CMAKE_BINARY_DIR}/config.json    # 出力ファイル
  @ONLY                              # 変数の置換を @VAR@ 形式の参照に制限する
)
```
```json:config.json
{
    "var1" : "hoge",
    "var2" : "fuga"
}
```

今回は `plugInfo.json.in` を `CMakeLists.txt` でセットした変数へと置き換えるために使用しています
:::

#### install

:::note
ビルド後に生成されたファイル等を、指定したディレクトリへとインストールするために使用するコマンド

```
install(TARGETS <target> DESTINATION <dir>)
```

```
TARGETS     : ビルドしたターゲットをインストール
FILES       : 単一または複数のファイルをインストール
DERECTORY   : ディレクトリ内のファイルとサブディレクトリを再帰的にインストール
DESTINATION : インストール先のディレクトリパス（絶対パスや相対パス
```
オプションは他にもあるので、[こちら](https://cmake.org/cmake/help/latest/command/install.html)を確認
:::

https://cmake.org/cmake/help/latest/command/install.html#directory

***

### Definitions

#### NOMINMAX
:::note
`windef.h` や`windows.h` では、`min` と `max` のマクロが定義されており、
それらのマクロが `std::min` や `std::max` というC++の標準ライブラリ関数などと衝突してしまう可能性があるため

`#define NOMINMAX` を使ってマクロを無効化しています
:::

#### WIN32_LEAN_AND_MEAN
:::note
不要なWindows APIからヘッダーを除外することで、ビルド時間が短縮されます
また[C++でUSDのコードを書こう(準備編)](https://fereria.github.io/reincarnation_tech/usd/cpp/start_usdcpp)でも記載されているように、それらのヘッダーで定義されたマクロとの衝突を避けるためにも

`#define WIN32_LEAN_AND_MEAN` を定義しています
:::

https://fereria.github.io/reincarnation_tech/usd/cpp/start_usdcpp

https://yohhoy.hatenadiary.jp/entry/20120115/p1

https://mntmanblog.com/win32_lean_and_mean/

https://github.com/PixarAnimationStudios/OpenUSD/tree/release/cmake/defaults

#### _GLIBCXX_USE_CXX11_ABI
:::note
C++11 ABIを使用するライブラリと古いABIを使用するライブラリ間でバイナリの互換性がなくなるため、古いライブラリと新しいライブラリが混在していると、実行時に問題が発生することもあるので、DCCで使用されているABIにあわせて定義しています

`#define _GLIBCXX_USE_CXX11_ABI 1`
:::

https://www.sidefx.com/docs/houdini/news/20/platforms.html

https://rinatz.github.io/cpp-book/ch11-05-library-updating-and-compatibility/

#### BOOST_ALL_NO_LIB
:::note
Boostの自動リンクの機能を無効にします

これを指定しない場合、Boostはリンクの必要がないヘッダーオンリーライブラリを使う場合でも、ライブラリを自動的にリンクしようとします。

不必要なライブラリのリンクを行わない為にも
`#define BOOST_ALL_NO_LIB` を定義しています
:::

:::note warn
自動リンクを有効にするためにはコンパイラが `#pragma comment(lib, ...)` をサポートしている必要があるので、主にMSVCでのコンパイル時に定義します
:::

##### HBOOST_ALL_NO_LIB
:::note warn
Houdiniで展開されているboostはプリフィックスに h が付くので
Houdiniからboostを使用する場合はマクロも H を付ける必要があります
:::

https://www.boost.org/doc/libs/1_86_0/libs/config/doc/html/index.html

https://lists.preview.boost.org/archives/list/boost@lists.preview.boost.org/thread/RNRKHIGGWKQEZT5LM3V3IOQLHMIGQNZC/#RNRKHIGGWKQEZT5LM3V3IOQLHMIGQNZC

https://stackoverflow.com/questions/1685206/pragma-commentlib-xxx-lib-equivalent-under-linux

https://github.com/boostorg/config/blob/master/include/boost/config/auto_link.hpp

***

## Compiler Options

#### /Zc:inline

:::note
このオプションを有効にすると、参照されていない変数や関数などのシンボルを
コンパイラは「未使用」と見なし、最適化によって削除します

MSVC 2019 以降、このオプションがデフォルトで有効になっています
:::

##### なぜ無効(/Zc:inline-)にするのか
:::note
[attributes.h](https://github.com/PixarAnimationStudios/OpenUSD/blob/release/pxr/base/arch/attributes.h)で定義されている `ARCH_CONSTRUCTOR` `ARCH_DESTRUCTOR` というマクロで使われているシンボルが`/Zc:inline`によって未使用と判断されてしまうため、
そのシンボルが削除されないように `/Zc:inline-` で無効にする必要があります

[具体的](https://developercommunity.visualstudio.com/t/zcinline-removes-extern-symbols-inside-anonymous-n/914943)には
```cpp:attributes.h: #290
#   define ARCH_CONSTRUCTOR(_name, _priority, ...)                             \
    static void _name(__VA_ARGS__);                                            \
    namespace {                                                                \
    __declspec(allocate(".pxrctor"))                                           \
    extern const Arch_ConstructorEntry                                         \
    _ARCH_CAT_NOEXPAND(arch_ctor_, _name) = {                                  \
        reinterpret_cast<Arch_ConstructorEntry::Type>(&_name),                 \
        static_cast<unsigned>(PXR_VERSION),                                    \
        _priority                                                              \
    };                                                                         \
    }                                                                          \
    _ARCH_ENSURE_PER_LIB_INIT(Arch_ConstructorInit, _archCtorInit);            \
    static void _name(__VA_ARGS__)
```
この部分の `namespace{}` で囲まれている `_ARCH_CAT_NOEXPAND(arch_ctor_, _name)` というシンボルが削除されるようです

VS2017では回避できていたみたいですが、VS2019以降では`extern`を付けてもエラーになるとの事
:::

##### /OPT:NOREF
:::note
`/OPT:NOREF` を有効にする事でも、参照されない関数とデータが保持されるので
`/Zc:inline-` の代わりとして指定する事もできます
:::

https://learn.microsoft.com/ja-jp/cpp/build/reference/zc-inline-remove-unreferenced-comdat?view=msvc-170

https://developercommunity.visualstudio.com/t/zcinline-removes-extern-symbols-inside-anonymous-n/914943

https://github.com/PixarAnimationStudios/OpenUSD/issues/1279

https://fereria.github.io/reincarnation_tech/usd/cpp/start_usdcpp

https://qiita.com/takahito-tejima/items/01ab2abe2f4c0d12eeed

#### /w
:::note
コンパイル時に警告メッセージを表示しないようにする
:::

https://learn.microsoft.com/ja-jp/cpp/build/reference/compiler-option-warning-level?view=msvc-170

#### -fPIC
:::note
動的リンクライブラリ（.so）を作成する際に主に使用されるオプション
:::

http://0xcc.net/blog/archives/000107.html

https://kaworu.jpn.org/c/gcc%E3%81%AE%E3%82%B3%E3%83%B3%E3%83%91%E3%82%A4%E3%83%AB%E3%82%AA%E3%83%97%E3%82%B7%E3%83%A7%E3%83%B3-fPIC%E3%81%A8-fpic%E3%81%AE%E9%81%95%E3%81%84%E3%81%AF%EF%BC%9F

#### -Wno-deprecated
:::note
非推奨機能の警告を無効にする
:::

#### -Wno-deprecated-declarations
:::note
非推奨宣言の警告を無効にする
:::

#### -Wno-changes-meaning
:::note
意味が変わる可能性のある変更に関する警告を無効にする
:::

***

## USD

### Macros

#### PXR_NAMESPACE_OPEN_SCOPE / PXR_NAMESPACE_CLOSE_SCOPE

:::note
名前空間を開閉するためのマクロです。ビルドによって変わりますが、[pxr.h](https://openusd.org/release/api/pxr_8h_source.html) では 
`pxrInternal_v0_24_11__pxrReserved__` という名前空間で展開されています

```c++:pxr.h #22
#define PXR_NS pxr
#define PXR_INTERNAL_NS pxrInternal_v0_24_11__pxrReserved__
#define PXR_NS_GLOBAL ::PXR_NS
 
namespace PXR_INTERNAL_NS { }
 
// The root level namespace for all source in the USD distribution.
namespace PXR_NS {
    using namespace PXR_INTERNAL_NS;
}
 
#define PXR_NAMESPACE_OPEN_SCOPE   namespace PXR_INTERNAL_NS {
#define PXR_NAMESPACE_CLOSE_SCOPE  }  
#define PXR_NAMESPACE_USING_DIRECTIVE using namespace PXR_NS;
```
のように定義されており `PXR_INTERNAL_NS` は
`PXR_NS` の名前空間ではusing ディレクティブが使用されています
:::

#### PXR_NAMESPACE_USING_DIRECTIVE

:::note
`using namespace PXR_NS` というディレクティブを挿入します

これによって名前空間のメンバーをコード内で簡単に使用する事ができます

```cpp
#include <pxr/base/tf/token.h>

PXR_NS::TfToken("hoge");
```

```cpp
#include <pxr/base/tf/token.h>

PXR_NAMESPACE_USING_DIRECTIVE

TfToken("hoge");
```
:::

https://openusd.org/release/api/pxr_8h_source.html

https://learn.microsoft.com/ja-jp/cpp/cpp/namespaces-cpp?view=msvc-170#using_directives

#### AR_API / AR_LOCAL / ARCH_IMPORT / ARCH_EXPORT / ARCH_HIDDEN
:::note
ライブラリのシンボルをエクスポートしたり、インポートする時の挙動を制御するためのマクロ

[具体的](https://github.com/PixarAnimationStudios/OpenUSD/blob/release/pxr/base/arch/export.h)にはコンパイラやOSに応じて
```c++:MSVC
__declspec(dllexport) // Export
__declspec(dllimport) // Import
```
```c++:GCC(4+)/Clang
// Windows
__attribute__((dllexport)) // Export
__attribute__((dllimport)) // Import
// Unix
__attribute__((visibility("default"))) //Export
__attribute__((visibility("hidden")))  //Hide
```
などのアトリビュートを設定します
:::

:::note warn
Windowsではシンボルのエクスポートとインポートを明示的に記述する必要があり、Unix系では可視性でシンボルを管理するので、外部から使用されたくない場合は `__attribute__((visibility("hidden")))` を設定します
:::

https://github.com/PixarAnimationStudios/OpenUSD/blob/release/pxr/usd/ar/api.h

https://github.com/PixarAnimationStudios/OpenUSD/blob/release/pxr/base/arch/export.h

https://learn.microsoft.com/ja-jp/cpp/build/importing-and-exporting?view=msvc-170

https://www.ibm.com/docs/ja/xl-c-and-cpp-linux/16.1.0?topic=compatibility-visibility-type-attribute

#### AR_DEFINE_RESOLVER(`ResolverClass`, `BaseClass...`)

::: note
指定したリゾルバーを登録するためのマクロ

第一引数に登録するリゾルバー、それ以降の引数に基底クラスを指定します

[defineResolver.h](https://openusd.org/release/api/define_resolver_8h.html)をみてみると、
`TF_REGISTRY_FUNCTION(TfType)` マクロ内で、`TfType::Define` を使って `TfType` を定義、その後 `SetFactory` でリゾルバーを登録しているみたいです
:::

::: note warn
- ソース内で使う事が想定されています
- `pluginfo.json` の基底クラスも合わせないと、読み込み時にエラーとなる可能性があります
:::

https://openusd.org/release/api/define_resolver_8h.html

https://openusd.org/release/api/class_tf_type.html

https://github.com/PixarAnimationStudios/OpenUSD/blob/release/pxr/usd/ar/defineResolver.h
#### AR_DECLARE_RESOLVER_CONTEXT(`ContextObject`)

::: note
指定された `ContextObject` が `ArResolverContext` のコンテキストオブジェクトとして使用できることを宣言するマクロ

マクロが展開されると`ArIsContextObject<ContextObject>`の構造体が定義され、コンテキストとして使用できることを示すために `value` メンバを `true` に設定しています

この構造体はResolverの変更を通知するための `ArNotice` クラスで使用されています

```cpp
ArIsContextObject<ContextObject>::value //true
```
:::

::: note warn
`ContextObject` が宣言されているヘッダー内で使用します
:::

https://openusd.org/release/api/class_ar_resolver_context.html

https://openusd.org/release/api/define_resolver_context_8h.html#aff8e5cb4e3aa8c2eb52692b3c31567cc

https://openusd.org/release/api/class_ar_notice_1_1_resolver_changed.html

https://github.com/PixarAnimationStudios/OpenUSD/blob/release/pxr/usd/ar/defineResolverContext.h

https://github.com/PixarAnimationStudios/OpenUSD/blob/release/pxr/usd/ar/notice.h

#### TF_WRAP(`x`)

:::note
Boost.Pythonライブラリを使用して、C++のクラスや関数を
Pythonで使用できるようにラップするための関数を宣言、実行するためのマクロ

例えば `TF_WRAP(Hoge)` とした場合、下記のように展開されます
```cpp
ARCH_HIDDEN void wrapHoge();
wrapHoge()
```

wrap〜という関数の実装は別途行う必要があります
:::

https://github.com/PixarAnimationStudios/OpenUSD/blob/release/pxr/base/tf/pyModule.h

#### TF_WRAP_MODULE

:::note


`TF_WRAP` で展開された関数を処理するための `WrapModule()` という関数を定義します。`TF_WRAP` は基本的にこのマクロ内で使用します

[具体的](https://github.com/PixarAnimationStudios/OpenUSD/blob/release/pxr/base/tf/pyModule.h)には
```cpp
TF_WRAP_MODULE
{
    TF_WRAP(Hoge);
    TF_WRAP(Fuga);
}
```
というマクロがこのように展開されます
```cpp
static void WrapModule()
{
    ARCH_HIDDEN void wrapHoge(); wrapHoge();
    ARCH_HIDDEN void wrapFuga(); wrapFuga();
}
```

そして`WrapModule`関数は`PyInit_<module_name>`という
Boost.Pythonで拡張モジュールを初期化する際に使用される関数内で呼び出されています
:::

https://github.com/PixarAnimationStudios/OpenUSD/blob/release/pxr/base/tf/pyModule.h

https://github.com/boostorg/python/blob/master/include/boost/python/module_init.hpp

https://boostjp-python.readthedocs.io/ja/latest/tutorial.html

#### TF_DEBUG(`enumVal`)

:::note
`enumVal` が有効かどうかを確認するためのマクロ

有効であれば `TF_DEBUG(enumVal).Msg(...)` でデバッグメッセージを表示する事ができます。
また、`enumVal` は `TF_DEBUG_CODES()` のマクロで定義されている必要があります

```cpp
if (!TfDebug::IsEnabled(enumVal)) /* empty */ ; else TfDebug::Helper()
```
展開されると上記の式が評価され、`enumVal` が有効であれば `TfDebug::Helper` という構造体を返します
:::

#### TF_DEBUG_CODES(`...`)

:::note
`TF_DEBUG` で使用するための、デバッグシンボルを定義するためのマクロ

このマクロを使用する事でデバッグシンボルを列挙した `enum` 型を定義したり、
デバッグシンボルに関する構造体や関数などが定義されます。
```cpp
TF_DEBUG_CODES(
  MY_E1,
  MY_E2
);
```
というマクロがこのように展開されます
```cpp
// 列挙型の名前は 最初の引数に__DebugCodesを付けた名前が定義されます
enum MY_E1__DebugCodes {
    MY_E1,
    MY_E2,
    MY_E1__DebugCodes__PAST_END //内部的に使用される定数
};

// デバッグコードの情報を格納した構造体
template <>
struct TfDebug::_Traits<MY_E1__DebugCodes> {
    static constexpr bool IsDeclared = true;
    static constexpr int NumCodes = MY_E1__DebugCodes__PAST_END;
    static constexpr bool CompileTimeEnabled = true;
};

//
inline char const *
Tf_DebugGetEnumName(MY_E1__DebugCodes val) {
    constexpr char const *CStrings[] = {
        "MY_E1",
        "MY_E2",
        "MY_E1__DebugCodes__PAST_END"
    };
    return CStrings[static_cast<int>(val)];
};
```
:::

:::note
`TF_DEBUG`環境変数に `TF_DEBUG_REGISTRY` を設定することで挙動を確認できます
:::

#### TF_DEBUG_ENVIRONMENT_SYMBOL(`VAL`, `descrip`)

:::note
デバッグシンボルにデバッグ内容の説明文を指定することができるマクロ
このマクロを定義しなくても、`TF_DEBUG`でのデバッグ自体は有効

というのも、`TF_DEBUG_ENVIRONMENT_SYMBOL` では `Tf_DebugSymbolRegistry` を使ってデバッグシンボルを登録しますが、

`TF_DEBUG`の環境変数が設定されていたり、`TfDebug::SetDebugSymbolsByName` などの関数を実行したタイミングでも初期化されるため、`TF_DEBUG_ENVIRONMENT_SYMBOL` で登録しなくても `TF_DEBUG` 自体は効きます
:::

:::note warn
- ヘッダーではなく、ソースファイル内の `TF_REGISTRY_FUNCTION(TfDebug)` マクロの中で使うことが推奨されています
- 登録するデバッグシンボルは、`TF_DEBUG_CODES()` のマクロで定義されている必要があります
```cpp:debugCodes.cpp
TF_REGISTRY_FUNCTION(TfDebug) {
    TF_DEBUG_ENVIRONMENT_SYMBOL(MY_E1, "loading of blah-blah files");
    TF_DEBUG_ENVIRONMENT_SYMBOL(MY_E2, "parsing of mdl code");
    // etc.
}
```
:::

https://openusd.org/release/api/group__group__tf___debugging_output.html

https://openusd.org/release/api/class_tf_debug.html

https://github.com/PixarAnimationStudios/OpenUSD/blob/release/pxr/base/tf/debug.h

#### TF_REGISTRY_FUNCTION(`KEY_TYPE`)

:::note
レジストリ（`TfDebug`などの型）に関数を登録するためのマクロ
登録した関数を呼び出す時は`TfRegistryManager::SubscribeTo<KEY_TYPE>()`を使います

これによって、`SubscribeTo<KEY_TYPE>()`で指定された型の関数のみが実行されるので、不要な呼び出しを避ける事ができます
```c++
// 登録
TF_REGISTRY_FUNCTION(KEY_TYPE)
{
    //ここに処理を入れる
}

// 呼び出し
TfRegistryManager::GetInstance().SubscribeTo<KEY_TYPE>()
```
:::

:::note
`TF_DEBUG` 環境変数に `TF_DISCOVERY_TERSE` を設定することで挙動を確認できます
:::

:::note warn
KEY_TYPEはテンプレート化や、名前空間で修飾されていない必要があります
```cpp
// ダメな例 : テンプレート
template <typename T>
class Hoge {};
TF_REGISTRY_FUNCTION(Hoge<int>){}
// ダメな例 : Namespace
namespace Hoge {
    class Fuga {};
}
TF_REGISTRY_FUNCTION(Hoge::Fuga){}
```
もし名前空間を使用している場合は、`using namespace` を使って名前空間を指定した後、`TF_REGISTRY_FUNCTION` を使用します
:::

https://openusd.org/release/api/page_tf__registry_manager.html

https://openusd.org/release/api/registry_manager_8h.html

https://openusd.org/release/api/class_tf_registry_manager.html

#### TF_PP_STRINGIZE(`x`)

:::note
`x` を文字列に変換します

例えば `TfToken` のコンストラクタでマクロを使いたい場合、`TF_PP_STRINGIZE` を使用して文字列へと変換する事ができます

```cpp
#define HOGE fuga

TfToken(TF_PP_STRINGIZE(HOGE)); // TfToken("fuga")
```
:::

https://github.com/PixarAnimationStudios/OpenUSD/blob/release/pxr/base/tf/preprocessorUtilsLite.h

#### MFB_PACKAGE_NAME / MFB_ALT_PACKAGE_NAME / MFB_PACKAGE_MODULE

:::note
USDで内部的に使われているマクロ。

Cmakeの `pxr_plugin(NAME)` 関数からビルドする場合は
`PXR_PACKAGE` という変数を設定するだけでよい

手動で設定する場合は、
`MFB_PACKAGE_NAME` `MFB_ALT_PACKAGE_NAME` はライブラリの名前、
`MFB_PACKAGE_MODULE` は `MFB_PACKAGE_NAME` の頭文字を大文字にした名前を設定

主に下記で使われています
- `TfAutoMallocTag2` でメモリを管理する時のタグ
- `TfRegistryManager` でライブラリのコンストラクター、デストラクタを実行
- Pythonでモジュールを作成する時の名前
:::

https://github.com/PixarAnimationStudios/OpenUSD/blob/release/cmake/macros/Private.cmake

https://github.com/PixarAnimationStudios/OpenUSD/blob/9b0c13b2efa6233c8a4a4af411833628c5435bde/extras/usd/examples/usdResolverExample/CMakeLists.txt#L2

https://github.com/search?q=repo%3APixarAnimationStudios%2FOpenUSD+MFB_PACKAGE_NAME&type=code

https://github.com/search?q=repo%3APixarAnimationStudios%2FOpenUSD+_pxr_library&type=code

### Classes

#### TfRegistryManager

::: note
プログラム起動時にライブラリが自動的に初期化される仕組みを、効率的に管理するためのクラス

ライブラリの初期化を必要な時に呼び出せるので、
スレッドセーフに処理したり、不要な初期化を避ける事ができます

:::

##### 静的コンストラクタ
::: note
プログラム起動時に初期化する方法として、
静的メソッドや静的変数がプログラム開始時に初期化されるという特性を活かして、
静的コンストラクタに近い動作をさせるテクニックがあります。

```cpp:main.cpp
#include <iostream>

class MyClass {
public:
    static void initialize() {
        std::cout << "initialize!\n";
    }
};

struct Initializer {
    Initializer() { MyClass::initialize(); }
};

static Initializer forceInit;

int main() {
    std::cout << "main!\n";
}
```
```cpp:結果
initialize!
main!
```
静的変数 `forceInit` のインスタンスが作成されると、`Initializer` のコンストラクタが呼び出され、その中で `MyClass::initialize()` が実行されます

このように書く事で静的コンストラクタに近い動作を実現することができました
:::

##### TfRegistryManagerによる拡張

:::note
ただ、そのまま使用するといくつか問題があるので、`TfRegistryManager` によってそれらの問題を解決しています

- 各OSで初期化のタイミングが不確定
- 複数のライブラリがあった時の依存関係の管理が難しい
- 無駄な初期化が発生する
- スレッドセーフではない可能性がある

基本的には上記の静的コンストラクタやコンパイラで用意されている特別なアトリビュートを使用していますが、
※GCCやClangなら`__attribute__((used, constructor))`のような

そこではまず関数の登録のみを行います。その後、必要になったタイミングで`TfRegistryManager::SubscribeTo<KEY_TYPE>()`を呼び出す事で、任意のタイミングでの関数を実行する事ができます

:::

https://openusd.org/release/api/page_tf__registry_manager.html

https://github.com/PixarAnimationStudios/OpenUSD/blob/release/pxr/base/tf/registryManager.h

https://github.com/PixarAnimationStudios/OpenUSD/blob/release/pxr/base/arch/attributes.h

https://learn.microsoft.com/ja-jp/dotnet/csharp/programming-guide/classes-and-structs/static-constructors

#### TfScriptModuleLoader

:::note
Pythonで利用できるようにバインディングされたライブラリの
読み込みや依存関係の管理などを適切に行うクラス

主に以下のタイミングでモジュールが読み込まれます
- スクリプトモジュールがロードされた時
- `TfPyInitialize`が呼ばれた時
- Plugが共有ライブラリを開いた時
:::

:::note
`TF_DEBUG` 環境変数に `TF_SCRIPT_MODULE_LOADER` を設定することで挙動を確認できます
:::

##### RegisterLibrary(`name`, `moduleName`, `predecessors`)

:::note
ライブラリを`TfScriptModuleLoader`に登録するための関数

`name` には C++の動的ライブラリの名前
`moduleName` にはPythonのモジュール名
`predecessors` は依存するライブラリの動的配列

をそれぞれ`TfToken`型で指定します

```cpp:ar/moduleDeps.cpp
TF_REGISTRY_FUNCTION(TfScriptModuleLoader) {
    // List of direct dependencies for this library.
    const std::vector<TfToken> reqs = {
        TfToken("arch"),
        TfToken("js"),
        TfToken("plug"),
        TfToken("tf"),
        TfToken("vt")
    };
    TfScriptModuleLoader::GetInstance().
        RegisterLibrary(TfToken("ar"), TfToken("pxr.Ar"), reqs);
}

```
:::

https://openusd.org/release/api/class_tf_script_module_loader.html

https://github.com/PixarAnimationStudios/OpenUSD/blob/release/pxr/base/tf/scriptModuleLoader.cpp

#### PlugRegistry

:::note
C++の共有ライブラリや、Pythonモジュールなどで作成されたプラグインに対して登録や読み込みを動的に行い、それらを管理するためのクラス

プラグインを登録するには`plugInfo.json`を用意し、
`RegisterPlugins()`という関数を使用します

登録されたプラグインは、プラグインが必要とされるまで読み込まれないので、
効率的にリソースを管理する事ができます
:::

##### plugInfo.json

:::note
`plugInfo.json` はプラグインの登録に必要な情報を記述するためのファイルです
`PlugRegistry` は、このファイルをもとにプラグインを読み込み、依存関係を解決します
```json:/ar/resources/plugInfo.json
{
    "Plugins": [
        {
            "Info": {
                "Types": {
                    "ArResolver": {},
                    "ArDefaultResolver": {
                        "bases": [
                            "ArResolver"
                        ],
                        "implementsContexts": true
                    },
                    "ArPackageResolver": {}
                }
            },
            "LibraryPath": "../../libpxr_ar.so", 
            "Name": "ar", 
            "ResourcePath": "resources", 
            "Root": "..", 
            "Type": "library"
        }
    ]
}
```
:::

##### ライブラリの動的な読み込み

:::note
動的ライブラリの呼び出し、終了は `TfDlopen` `TfDlclose` を使用しています

[具体的](https://github.com/PixarAnimationStudios/OpenUSD/blob/release/pxr/base/arch/library.cpp)にはOSに応じて、下記関数が呼び出されています
```c++:Windows
LoadLibrary() // Open
FreeLibrary() // Close
```
```c++:Unix
dlopen()  // Open
dlclose() // Close
```
:::

https://openusd.org/release/api/plug_page_front.html

https://openusd.org/release/api/class_plug_registry.html

https://openusd.org/release/api/group__group__tf___systems_ext.html#ga603ea849894e7b2c2a3748cae00bea41

https://github.com/PixarAnimationStudios/OpenUSD/blob/release/pxr/base/arch/library.cpp

#### TfStaticData

:::note
グローバル変数や静的メンバー変数などの、グローバルデータを
安全に管理する為のクラステンプレート


:::

:::note warn
`TfStaticData` はローカル変数では使用できません
グローバルスコープや、無名名前空間のようなファイルスコープで使用する事が推奨されています
:::

##### グローバルデータの問題点

:::note
グローバル変数や静的メンバー変数はプログラム全体で共有する事ができるが、
それらの変数が初期化される前にアクセスされてしまうと、未定義動作を引き起こす可能性があります、

```cpp
#include <iostream>

int A = B; // Bがまだ初期化されていない状態でAを使おうとする
int B = 10;

int main() {
    std::cout << A << std::endl;
}
```

また、複数のスレッドからグローバルデータにアクセスしようとすると、競合によってデータの整合性が保たれなくなってしまい、スレッドセーフではなくなるという問題も起こります

```cpp
#include <iostream>
#include <thread>

int globalVar = 0;

void increment() {
    for (int i = 0; i < 1000; ++i) {
        globalVar++; // 複数スレッドで同時にアクセスすると競合が起こる
    }
}

int main() {
    std::thread t1(increment);
    std::thread t2(increment);

    t1.join();
    t2.join();

    std::cout << globalVar << std::endl; // 結果は予測できない（競合による不整合）
}

```
:::

##### TfStaticDataによる改善

:::note
`TfStaticData` はデータの初期化を遅延させる事でそれらの問題を回避しています

`TfStaticData` が指し示すデータはプログラムが実行された段階では初期化されずに、そのデータへ初めてアクセスした時に初期化されます。
そうする事で限りなく安全に初期化することができ、上記の問題を回避することができます

```cpp
static TfStaticData<std::set<std::string>> Xyz_nameSet;

void XyzAddName(std::string name) {
    Xyz_nameSet->insert(name);  // 初めてアクセスした時に遅延初期化される
}
```
:::

https://openusd.org/release/api/class_tf_static_data.html#details

#### TfNotice

:::note

通知に関するシステムを構成したり、管理するためのクラス

このクラスを継承する事で、
イベントが発生した事をリスナーに知らせる為のセンダーとしての役割も持ちます

リスナーは主に `TfWeakBase` や `TfRefBase` を継承したクラスで定義され、
それらが、通知を受け取る役割を持ちます

```cpp
#include <iostream>

#include <pxr/base/tf/type.h>
#include <pxr/base/tf/notice.h>
#include <pxr/base/tf/weakBase.h>

PXR_NAMESPACE_USING_DIRECTIVE

// Noticeを作成
class TestNotice : public TfNotice
{
public:
    TestNotice() = default;
};

// Listenerを作成
class TestListener : public TfWeakBase
{
public:
    TestListener()
    {
        TfWeakPtr<TestListener> me(this);
        TfNotice::Register(me, &TestListener::ProcessHoge); //登録
    };

    // Register時に引数の型を見ているので、対象のNoticeクラスを引数にする
    void ProcessHoge(const TestNotice &n)
    {
        std::cout << "Hello Notice!" << std::endl;
    };
};

// Noticeを使用するには、一度定義する必要がある
TF_REGISTRY_FUNCTION(TfType)
{
    TfType::Define<TestNotice, TfType::Bases<TfNotice> >();
}

int main() {
    // ListenerのコンストラクタでTestNoticeを登録
    TestListener *l1 = new TestListener();
    // TestNoticeからイベントをSend
    TestNotice().Send();
    return 0;
}
```

:::

##### TfWeakBase

:::note
`TfWeakPtr` を使用するための基底クラス
オブジェクトが削除されたかどうかを監視する事ができます

また、継承する事で `TfRefPtr` 変数が追加され、
`TfWeakPtr` は、`TfRefPtr` が管理しているオブジェクトの状態を監視し、
`TfRefPtr` は実際にオブジェクトを所有し、オブジェクトが有効かどうかを管理します
:::

##### TfWeakPtr

:::note
オブジェクトが削除されたかどうかを監視するためのポインタ
基本的には `TfWeakBase` を継承したクラスで使用します

参照しているオブジェクトが削除されると `TfWeakPtr` は自動的に無効化され `nullptr` になります

また、循環参照が起こらないように、オブジェクトに対する所有権は持ちません
:::

##### TfRefBase

:::note
`TfRefPtr` を使用するための基底クラス
オブジェクトが実際に参照されているかどうかを管理します

`TfRefBase` を継承することで、
クラスは参照カウントを管理できるようになります。
:::

##### TfRefPtr

:::note
オブジェクトの所有権を管理するポインタ
`TfRefBase` を継承したオブジェクトの参照カウントを実装しています

オブジェクトへの最後の参照が破棄されるとオブジェクトを自動的に削除します
:::

https://openusd.org/release/api/page_tf__notification.html

### Functions

#### ArWrapResolverContextForPython(`context`)

:::note
PythonからC++の `ArResolverContext` オブジェクトに変換できるコンテキストオブジェクトとして、指定された型を登録する関数
```
ArWrapResolverContextForPython<This>();
```
:::

#### TfGetBaseName(`fileName`)

:::note
ファイル名を取得する関数
```cpp
TfGetBaseName("hoge/fuga/fugo.txt") // fugo.txt
```
:::

#### TfGetPathName(`fileName`)

:::note
ディレクトリパスを取得する関数
最後にディレクトリ区切りのスラッシュがつきます
※ Windowsの場合は \
```cpp
TfGetPathName("hoge/fuga/fugo.txt") // hoge/fuga/
```
:::

#### TfStringTokenize(`source`, `delimiters`)

:::note
与えられた文字列を区切り文字で分解し、文字列のベクトルを返します
```cpp
TfStringTokenize("hoge:fuga:fugo",":")
//std::vector<std::string> {"hoge","fuga","fugo"}
```
:::

#### TfStringCatPaths(`prefix`, `suffix`)

:::note
ファイルパスなどで使われる `..` や `/` を考慮した上で、2つの文字列を結合します
```cpp
TfStringCatPaths( "foo/bar", "jive" )    => "foo/bar/jive"
TfStringCatPaths( "foo/bar", "../jive" ) => "foo/jive"
```
:::

https://openusd.org/release/api/group__group__tf___string.html

https://github.com/PixarAnimationStudios/OpenUSD/blob/release/pxr/base/tf/stringUtils.cpp

#### TfAbsPath(`path`)

:::note
指定されたファイルパスを絶対パスに変換するための関数
- すでに絶対パスが指定されている場合はそのままそのパスが返されます
- 相対パスの場合、現在の作業ディレクトリを基準にして絶対パスを返します
- 指定されたパスが実際に存在しない場合でも、絶対パスを返します
:::

https://github.com/PixarAnimationStudios/OpenUSD/blob/release/pxr/base/arch/fileSystem.cpp#L384

#### TfIsRelativePath(`path`)

:::note
指定されたファイルパスが相対パスかどうかを確認するための関数

```:Windows
WindowsAPIのPathIsRelativeW関数の戻り値がTrueかつ、
パスの先頭文字が / や　\\　ではない場合にTrue
```

```:Unix
パスが空、もしくは先頭文字が / ではない場合にTrue
```
::: 

https://learn.microsoft.com/ja-jp/windows/win32/api/shlwapi/nf-shlwapi-pathisrelativew

https://openusd.org/release/api/path_utils_8h.html#a2c4e842e7d4fda6f85aa64721ab3abec

https://github.com/PixarAnimationStudios/OpenUSD/blob/release/pxr/base/tf/pathUtils.cpp

#### TfPathExists(`path`, `resolveSymlinks = False`)

:::note
パスが存在するかどうかを確認するための関数
`resolveSymlinks` が `True` の場合はシンボリックリンクを解決した上で確認します
:::

https://openusd.org/release/api/file_utils_8h.html

https://github.com/PixarAnimationStudios/OpenUSD/blob/release/pxr/base/tf/fileUtils.cpp

#### TfGetenv(`envName`, `defaultValue = ""`)

:::note
環境変数を文字列で返します、見つからない場合は `defaultValue` が返されます
:::

https://openusd.org/release/api/group__group__tf___systems_ext.html