🤺

FBX SDKの"シンボルがあいまいです"エラーを駆逐する

2023/12/09に公開

結論

.vscode/c_cpp_properties.jsonのdefinesにFBXSDK_DEFINE_NAMESPACE=0FBXSDK_NAMESPACE_USING=0を追加する。

.vscode/c_cpp_properties.json
{
    "configurations": [
        {
            "name": "Win32",
            "defines": [
                "FBXSDK_DEFINE_NAMESPACE=0",
                "FBXSDK_NAMESPACE_USING=0"
            ],
            "configurationProvider": "ms-vscode.cmake-tools"
        }
    ],
    "version": 4
}

.vscode/settings.jsonに以下の項目を追加する。

.vscode/settings.json
{
    "C_Cpp.default.mergeConfigurations": true
}

以上。

はじめに

VSCodeでFBX SDKを使ってコードを書いているとよくこうなる。
(Visual Studio使えとか言わないで)

ウザすぎる
何があいまいなんだ。3秒前までちゃんと補完してくれていたじゃないか。
CMakeを使っている場合は再度Configureすれば直るがあまりにも頻繁になるので鬱陶しすぎる。
これをとにかく何とかしたい。

FBX SDKにnamespaceってないの?

あいまいです系のエラーといえば大体namespace被りとかその辺りである。
そもそもFbxManagerとかFbxNodeとかは特にnamespaceをつけなくても使用することができるが、別にnamespaceが定義されていないわけではない。
fbxsdk.hを覗いてみると末尾に以下の記述がある

fbxsdk.h
...
#if defined(FBXSDK_NAMESPACE) && (FBXSDK_NAMESPACE_USING == 1)
	using namespace FBXSDK_NAMESPACE;
#endif
...

つまり、FBXSDK_NAMESPACEという名前のマクロで定義されたnamespaceがあり、それをusing namespaceすることでスコープ外からそのまま使えるようにしている。

namespaceはどこで定義されているか

当たり前だがFBXSDK_NAMESPACEの定義がどこかに存在する。これはfbxsdk.hでincludeされるfbxsdk/fbxsdk_def.hでincludeされているfbxsdk/fbxsdk_version.hの中にある。

fbxsdk/fbx_version.h
...
//FBX SDK namespace definition
#ifndef FBXSDK_DEFINE_NAMESPACE
    #define FBXSDK_DEFINE_NAMESPACE 1
#endif

#if FBXSDK_DEFINE_NAMESPACE == 1
    #define FBXSDK_NAMESPACE fbxsdk
#else
    #define FBXSDK_NAMESPACE
#endif
...

FBXSDK_DEFINE_NAMESPACEが定義されていなければ1と定義し、この場合FBXSDK_NAMEが"fbxsdk"になる。
つまり、

#define FBXSDK_NAMESPACE_USING 0
#include <fbxsdk.h>

などとしてやれば

int main() {
    fbxsdk::FbxManager* manager = fbxsdk::FbxManager::Create();
}

のようなかたちで書くことになる。

namespaceブロックはどうなっているか

namespaceがどこで定義されているのかは分かったが、実際に各クラスはどのようにしてnamespaceの中に収まっているのか。たとえばfbxsdk/core/fbxobject.hの中身を覗いてみると何やら怪しげな記述がある

fbxsdk/core/fbxobject.h
//! \file fbxobject.h
#ifndef _FBXSDK_CORE_OBJECT_H_
#define _FBXSDK_CORE_OBJECT_H_

/* ~色々なinclude~ */
#include <fbxsdk/fbxsdk_nsbegin.h>
/* ~さまざまな定義~ */
#include <fbxsdk/fbxsdk_nsend.h>

#endif /* _FBXSDK_CORE_OBJECT_H_ */

このfbxsdk_nsbegin.hfbxsdk_nsend.hの中身は

fbxsdk/fbxsdk_nsbegin.h
//! \file fbxsdk_nsbegin.h
#include <fbxsdk/fbxsdk_version.h>

#if FBXSDK_DEFINE_NAMESPACE == 1
	namespace FBXSDK_NAMESPACE {
#endif
fbxsdk/fbxsdk_nsend.h
//! \file fbxsdk_nsend.h

#if FBXSDK_DEFINE_NAMESPACE == 1
	}
#endif

という短いもの。これを各ファイルでincludeすることで全部のクラスをnamespace内に収めている。

なぜエラーは起こるか

ところでこのfbxsdk_nsbegin.hをIntelliSenseが効いた状態で覗いてみよう

なんかエラー出てる~
これは推測に過ぎないが、これこそがあいまいですエラーを出している元凶である。C++のIntelliSenseはnamespaceが別ファイルに記述されてincludeされるという状態を想定していないと思われる。

ということはnamespaceを消せば…!

ここまでくれば解決策は簡単に思いつく。
FBX SDKではnamespaceを定義されたうえで全体でusingされている。つまりnamespace定義を抹消してもコード記述は変わらない。じゃあ消せばいいんや!

#define FBXSDK_DEFINE_NAMESPACE 0
#define FBXSDK_NAMESPACE_USING 0
#include <fbxsdk.h>

これで全部解決!計算通り、完璧〜♪

そうは問屋が卸さなかった

😱😱😱

何が起こったか。そう、リンカエラーである。
namespaceを消した状態でビルドするとシンボル名が変わってしまうためdllで定義された名前と変わってしまい、ビルドに失敗してしまう。
ならこんな回りくどいnamespaceのつけ方するな

本当の解決策

要するに「IntelliSenseにはnamespaceがないかのように見せかけ、ビルドするときは存在していればいい」のである。ので、冒頭の結論通りに.vscode/c_cpp_properties.jsonのdefinesにFBXSDK_DEFINE_NAMESPACE=0FBXSDK_NAMESPACE_USING=0を追加する。

.vscode/c_cpp_properties.json
{
    "configurations": [
        {
            "name": "Win32",
            "defines": [
                "FBXSDK_DEFINE_NAMESPACE=0",
                "FBXSDK_NAMESPACE_USING=0"
            ],
            "configurationProvider": "ms-vscode.cmake-tools"
        }
    ],
    "version": 4
}

ビルドはVisual StudioでやっててIntelliSenseだけ別に設定するならここまででよい。しかしCMake Toolsを使っている場合、configurationProviderから提供される設定でc_cpp_properties.jsonの設定のほとんどが上書きされてしまう。そこで下記を追加する。

.vscode/settings.json
{
    "C_Cpp.default.mergeConfigurations": true
}

この設定をするとconfigurationProviderから来た設定とc_cpp_properties.jsonの設定を混ぜることができる。つまり、IntelliSenseだけ騙すことができ、無事"あいまいです"エラーは出なくなるし、ビルドも通るようになる。

これにて本当に、一件落着。

Discussion