オブジェクト指向におけるFizzBuzz問題
誰もやってないみたいなので、COM(っぽい感じ)で書いてみましたw
問題の意図は実装できていますが、問題そのものを書き換えちゃってます(ぇ
#include <windows.h>
#include <iostream>
const IID IID_IAnimal =
{ 0x440a41b9, 0x7220, 0x49f9, { 0xbd, 0x6a, 0xe0, 0x36, 0x5e, 0xcc, 0x80, 0xf4 } };
const IID IID_IDog =
{ 0xe4edbf0, 0xb936, 0x4213, { 0x89, 0xa5, 0xa, 0xeb, 0x5f, 0x8b, 0xa9, 0xa3 } };
const IID IID_ICat =
{ 0xefd41d5, 0xebf8, 0x4c44, { 0xb3, 0x5, 0x6d, 0x16, 0x63, 0x37, 0x5f, 0xce } };
REFCLSID CLSID_Cat = IID_ICat;
REFCLSID CLSID_Dog = IID_IDog;
class IAnimal : public IUnknown
{
public:
virtual HRESULT STDMETHODCALLTYPE SetCount(LONG count) = 0;
virtual HRESULT STDMETHODCALLTYPE Sound() = 0;
};
class IDog : public IAnimal
{
};
class ICat : public IAnimal
{
};
class AnimalImpl : public IDog, public ICat
{
private:
ULONG m_nRef;
LONG m_nCount;
BSTR m_szSound;
REFIID m_rThisIid;
public:
AnimalImpl(REFIID rThisIid)
: m_nRef(0), m_nCount(1),
m_szSound(NULL), m_rThisIid(rThisIid) { }
virtual ~AnimalImpl()
{
::SysFreeString(m_szSound);
}
HRESULT SetSound(LPCWSTR szSound)
{
if (m_szSound != NULL)
{
::SysFreeString(m_szSound);
}
m_szSound = ::SysAllocString(szSound);
if (m_szSound == NULL)
{
return E_POINTER;
}
return S_OK;
}
virtual ULONG STDMETHODCALLTYPE AddRef()
{
return ++m_nRef;
}
virtual ULONG STDMETHODCALLTYPE Release()
{
if (--m_nRef == 0)
{
delete this;
return 0;
}
return m_nRef;
}
virtual HRESULT STDMETHODCALLTYPE QueryInterface(REFIID riid, void** ppv)
{
if (ppv == NULL)
{
return E_POINTER;
}
if (::InlineIsEqualGUID(riid, IID_IUnknown))
{
*ppv = static_cast<IUnknown*>(static_cast<IDog*>(this));
}
else if (::InlineIsEqualGUID(riid, IID_IAnimal))
{
*ppv = static_cast<IAnimal*>(static_cast<IDog*>(this));
}
else if (
::InlineIsEqualGUID(riid, IID_IDog) &&
::InlineIsEqualGUID(riid, m_rThisIid))
{
*ppv = static_cast<IDog*>(this);
}
else if (
::InlineIsEqualGUID(riid, IID_ICat) &&
::InlineIsEqualGUID(riid, m_rThisIid))
{
*ppv = static_cast<ICat*>(this);
}
else
{
*ppv = NULL;
return E_NOINTERFACE;
}
reinterpret_cast<IUnknown*>(*ppv)->AddRef();
return S_OK;
}
virtual HRESULT STDMETHODCALLTYPE SetCount(LONG nCount)
{
if (nCount < 0)
{
return E_INVALIDARG;
}
m_nCount = nCount;
return S_OK;
}
virtual HRESULT STDMETHODCALLTYPE Sound()
{
if (m_szSound == NULL)
{
return E_POINTER;
}
for (LONG i = 0; i < m_nCount; i++)
{
std::wcout << reinterpret_cast<LPWSTR>(m_szSound);
if (std::wcout.fail())
{
return E_FAIL;
}
}
std::wcout << std::endl;
if (std::wcout.fail())
{
return E_FAIL;
}
return S_OK;
}
};
HRESULT CreateAnimal(REFCLSID rclsid, IUnknown** ppv)
{
if (ppv == NULL)
{
return E_POINTER;
}
AnimalImpl* p;
if (::InlineIsEqualGUID(rclsid, CLSID_Dog))
{
try
{
p = new AnimalImpl(IID_IDog);
}
catch (std::bad_alloc&)
{
return E_OUTOFMEMORY;
}
catch (...)
{
return E_UNEXPECTED;
}
HRESULT hr = p->SetSound(L"わん");
if (FAILED(hr))
{
delete p;
return hr;
}
*ppv = reinterpret_cast<IUnknown*>(p);
}
else if (::InlineIsEqualGUID(rclsid, CLSID_Cat))
{
try
{
p = new AnimalImpl(IID_ICat);
}
catch (std::bad_alloc&)
{
return E_OUTOFMEMORY;
}
catch (...)
{
return E_UNEXPECTED;
}
HRESULT hr = p->SetSound(L"にゃー");
if (FAILED(hr))
{
delete p;
return hr;
}
*ppv = reinterpret_cast<IUnknown*>(p);
}
else
{
return E_NOINTERFACE;
}
reinterpret_cast<IUnknown*>(p)->AddRef();
return S_OK;
}
int main()
{
IUnknown* pUnknown;
HRESULT hr = ::CreateAnimal(CLSID_Dog, &pUnknown);
if (FAILED(hr))
{
return hr;
}
IAnimal* pAnimal;
hr = pUnknown->QueryInterface(IID_IAnimal, reinterpret_cast<void**>(&pAnimal));
if (FAILED(hr))
{
pUnknown->Release();
return hr;
}
hr = pAnimal->Sound();
if (FAILED(hr))
{
pAnimal->Release();
pUnknown->Release();
return hr;
}
hr = pAnimal->SetCount(3);
if (FAILED(hr))
{
pAnimal->Release();
pUnknown->Release();
return hr;
}
hr = pAnimal->Sound();
if (FAILED(hr))
{
pAnimal->Release();
pUnknown->Release();
return hr;
}
pAnimal->Release();
pUnknown->Release();
hr = ::CreateAnimal(CLSID_Cat, &pUnknown);
if (FAILED(hr))
{
return hr;
}
hr = pUnknown->QueryInterface(IID_IAnimal, reinterpret_cast<void**>(&pAnimal));
if (FAILED(hr))
{
pUnknown->Release();
return hr;
}
hr = pAnimal->Sound();
if (FAILED(hr))
{
pAnimal->Release();
pUnknown->Release();
return hr;
}
pAnimal->Release();
pUnknown->Release();
return 0;
}
AnimalImpl で全部のインターフェースを同時に実装しているせいで犬と猫がごちゃ混ぜ状態のキメラになっていますが、インターフェースはちゃんとしてるので問題なしです。
あと QueryInterface をいつも通りの実装にすると「ん~?IID_IDog から IID_ICat に QueryInterface 出来ちゃうよほれほれ~w」とかいじめられそうだったので、きな臭い方法で IID_IDog から IID_ICat に QueryInterface が出来ないようにしてみました。
QueryInterface のアイデンティティには反していないはず。今後継承とか出てきたら実装が面倒になりますが。
それにしても COM を真正面から使おうと思うとこんなに面倒なんですね。
しかもちゃんとした COM として使おうと思うのであればクラスの登録とか作成も必要だし。
僕もう(;´ρ`)チカレタヨ・・・