[C++] ダウンキャストを回避してサブクラスにアクセスする

端的に言うと単なる前方宣言を利用したトリックです。
いないと思いますが、期待された方はごめんなさい。

#概要

本記事では、インターフェース`B`を引数に取るメソッド
`virtual IA::Func(const IB* b) = 0;`の実装について考えます。
このメソッドの実装`A::Func(const IB* b) override;`で、`IB`のサブクラス`B`から利用しているAPI等のハンドルを取得したいとなったとき、
最も手っ取り早いのは`static_cast<const B*>(b)`でサブクラスへキャストすることでしょう。
コーディング規約など、何らかの理由でこのキャストを回避したい場合を考えてみました。

既存のデザインパターンやイディオムとして存在するかどうか分かりませんでした。
名称や類似パターンをご存じの方是非教えてください。
（何が近いんでしょう？`Proxy`？`Bridge`？）

#方法

冒頭で述べた通り、前方宣言を使います。
以下のようにします。

```cpp:IB.h
struct IB
{
	struct ImplHolder;										// この二行。名前は適当
	virtual const ImplHolder& GetImplHolder() const = 0;	// 

	virtual ~IB() {}
	static IB* New();
};
```

サブクラスの`B`から参照できる場所で、前方宣言した`IB::ImplHolder`を定義します。

```cpp:B.h
#include "IB.h"

struct B;
struct IB::ImplHolder
{
	B& ref;
	explicit ImplHolder(B& b) : ref(b) {}

	B* operator->() const noexcept { return &ref; }  // ポインタのように使えると楽
};

class B : public IB
{
	IB::ImplHolder m_holder;
public:
	virtual const ImplHolder& GetImplHolder() const override;

	uint32_t GetHandle() const;
	B();
};
```

御覧のように、インターフェース側ではインナークラス`ImplHolder`を定義せず、サブクラス側で定義します。
また、内部にサブクラスへの参照を持つことで、`b->GetImplHolder()->GetHandle()`のようにしてサブクラスにアクセスすることができます。
利用する側にとってはインナークラスとその取得メソッドがあること位しか分かりません。
`B.h`は非公開にしておきましょう。

実際にサブクラス`A`で利用する場合は以下のようになるでしょうか。

```cpp:A.cpp
void A::Func(const IB* b)
{
	std::cout << "A::Func(const IB* b)" << std::endl;

	auto impl = b->GetImplHolder();
	//auto impl = static_cast<const B*>(b);  // キャストを使う場合

	auto handle = impl->GetHandle();

	std::cout << "handle = " << handle << std::endl;
}

```

このような小細工を入れておくことで、ダウンキャストを回避しつつ、実際に使う側からはサブクラス`A`,`B`の存在を隠すことができます。

```cpp:main.cpp
#include "IA.h"
#include "IB.h"

int main()
{
	auto a = IA::New();
	auto b = IB::New();

	a->Func(b);

	delete a;
	delete b;

	return 0;
}
```

注意点として、サブクラスへの参照を保持する必要があるため、ポインタ一つ分だけサイズが大きくなってしまいます。
64bitだと8byte分増えます（筆者環境）。

```bat:実行結果(x86)
A::A() [4byte]
B::B() [8byte]
A::Func(const IB* b)
B::GetHandle()
handle = 4294967295
```

```bat:実行結果(x64)
A::A() [8byte]
B::B() [16byte]
A::Func(const IB* b)
B::GetHandle()
handle = 4294967295
```

#プログラムの全体

本記事の説明用プログラムの全体を載せておきます。

```cpp:IA.h
#pragma once

struct IB;
struct IA
{
	virtual void Func(const IB* b) = 0;
	virtual ~IA() {}

	static IA* New();
};
```

```cpp:IB.h
#pragma once

struct IB
{
	struct ImplHolder;										// この二行。名前は適当
	virtual const ImplHolder& GetImplHolder() const = 0;	// 

	virtual ~IB() {}
	static IB* New();
};
```

```cpp:A.h
#pragma once
#include "IA.h"

class A : public IA
{
public:
	A();
	virtual void Func(const IB* b) override;
};
```

```cpp:B.h
#pragma once
#include "IB.h"

class B;
struct IB::ImplHolder
{
	B& ref;
	explicit ImplHolder(B& b) : ref(b) {}
	B* operator->() const noexcept { return &ref; }  // ポインタのように使えると楽
};

class B : public IB
{
	IB::ImplHolder m_holder;
public:
	virtual const ImplHolder& GetImplHolder() const override;

	B();
	uint32_t GetHandle() const;
};
```

```cpp:A.cpp
#include <iostream>
#include "A.h"
#include "B.h"

A::A()
{
	std::cout << "A::A() [" << sizeof(*this) << "byte]" << std::endl;
}

void A::Func(const IB* b)
{
	std::cout << "A::Func(const IB* b)" << std::endl;

	auto impl = b->GetImplHolder();
	//auto impl  = static_cast<const B*>(b);  // キャストを使う場合

	auto handle = impl ->GetHandle();

	std::cout << "handle = " << handle << std::endl;
}

IA* IA::New()
{
	return new A;
}
```

```cpp:B.cpp
#include <iostream>
#include "B.h"

const IB::ImplHolder& B::GetImplHolder() const
{
	return m_holder;
}

uint32_t B::GetHandle() const
{
	std::cout << "B::GetHandle()" << std::endl;
	return ~0;  // 適当
}

B::B() : m_holder(*this)
{
	std::cout << "B::B() [" << sizeof(*this) << "byte]" << std::endl;
}

IB* IB::New()
{
	return new B;
}
```

```cpp:main.cpp

#include "IA.h"
#include "IB.h"

int main()
{
	auto a = IA::New();
	auto b = IB::New();

	a->Func(b);

	delete a;
	delete b;

	return 0;
}
```

#どんな時に使うのか

`Vulkan`や`DirectX`などを利用したライブラリを構築する際、利用者側に実装を隠したい場合でしょうか。
例えば`IDeviceContext::DrawTexture2D(const ITexture2D* pTex, int x, int y) = 0;`を実装したい場合は、
`DirectX11`を利用する`IDeviceContext`のサブクラスは、`ITexture2D`から`ID3D11ShaderResourceView`なんかを取得したいかと思います。

```cpp:DX11DeviceContext.cpp
DX11DeviceContext::DrawTexture2D(const ITexture2D* pTex, int x, int y)
{
	auto impl = pTex->GetImplHolder();
	auto srv = impl->GetSRV();
	m_deviceContext->PSSetShaderResources(0, 1, &srv);
	// ...
}
```

#その他

クラス名以外同じコードなのでマクロにしておくといいんじゃないでしょうか。

```cpp:マクロ

#define INTERFACE									\
struct ImplHolder;									\
virtual const ImplHolder& GetImplHolder() const = 0;\

#define HOLDER(base, derive)							\
class derive;											\
struct base::ImplHolder									\
{														\
	derive& ref;										\
	explicit ImplHolder(derive& b) : ref(b) {}			\
														\
	derive* operator->() const noexcept { return &ref; }\
};

#define DERIVED(type)		\
	IB::ImplHolder m_holder;\
public:						\
	virtual const ImplHolder& GetImplHolder() const override { return m_holder; }

//	struct IB
//	{
//		INTERFACE
//	
//		virtual ~IB() {}
//	
//		static IB* New();
//	};
//	
//	HOLDER(IB, B)
//	
//	class B : public IB
//	{
//		DERIVED(IB)
//	
//		B(); // m_holder の初期化に注意
//		uint32_t GetHandle() const;
//	};

```