一般的な“ getData”の書き方関数?
-
07-07-2019 - |
質問
XMLデータを読み取り、ノード名によるアクセスを提供するクラス「CDownloader」などがあります。このようなゲッター関数がいくつかあります:
BOOL CDownloader::getInteger ( const CString &name, int *Value );
BOOL CDownloader::getImage ( const CString &name, BOOL NeedCache, CImage *Image );
BOOL CDownloader::getFont ( const CString &name, CFont *Font );
CDownloaderクラスを変更できません。代わりに、実際の名前ではなくブールフラグを使用してアイテムをダウンロードする関数を作成したいと思います。このようなもの:
BOOL DownloadFont( const CDownloader &Loader, bool Flag, CFont *Font )
{
if (Flag) {
// first try the "name_1"
if ( Loader.getFont("name_1", Font) ) return TRUE;
}
// if "name_1" fails or disabled by flag, try "name_2"
return Loader.getFont("name_2", Font);
}
Download(Font | Integer | Image)関数を個別に記述できますが、これによりコードが重複します。私のアイデアはテンプレートを書くことですが、まだ迷っています。CDownloaderクラスからどのメソッドを呼び出すべきかをどのように判断できますか?各データ型のテンプレートを特化するということは、コードの複製に再び立ち向かうことを意味します。 「関数へのポインタ」としてゲッター関数を渡すにはパラメータ?しかし、ゲッター署名はCDownloaderで異なります...
それをまとめると、質問は次のとおりです。CDownloaderの一般的なラッパーを書くことは可能ですか、それとも各「get ***」のコードを複製する必要がありますか。関数?事前に感謝します!
解決
3つの異なる名前の関数があり、タイプに応じていずれかを選択する必要がある限り、ある時点で、適切な関数を選択するためにオーバーロードまたは特性クラスのいずれかが必要です。それを回避する方法はないと思います。ただし、これらの関数の1つへの呼び出しがこれを必要とする唯一のものであるため、これらの DownloadXXX()
関数に示されたよりも多くのコードがある場合は、それでも意味があります。
これは、オーバーロードの代替手段を使用して実行できることのスケッチです。最初に、それぞれが3つの異なる関数の1つを呼び出す同じ関数の3つのオーバーロードが必要です。関数の1つに追加された BOOL
パラメーターは、汎用性に多少の混乱をもたらしますが、すべての関数がその BOOL
を受け入れることで回避できましたが、そのうちの2つはそれを無視します:
inline BOOL Load(CDownloader& Loader, const CString &name, int &Value, BOOL)
{return Loader.getInteger(name, &Value);
inline BOOL Load(CDownloader& Loader, const CString &name, CImage &Value, BOOL NeedCache)
{return Loader.getImage(name, NeedCache, &value);
inline BOOL Load(CDownloader& Loader, const CString &name, CFont &Value, BOOL)
{return Loader.getFont(name, &Font);
これで、汎用関数を作成できます。ただし、 BOOL
についてどうするかを決める必要があります。
template< typename T >
BOOL Download(const CDownloader &Loader, bool Flag, T &Obj, BOOL NeedCache /*= true*/)
{
if (Flag) {
if ( Load(Loader, "name_1", Obj, NeedCache) ) return TRUE;
}
return Load(Loader, "name_1", Obj, NeedCache);
}
ただし、ご覧のとおり、これは Download
関数がサンプルコードよりもはるかに複雑な場合にのみ、面倒な価値があります。さもなければ、追加された複雑さは、増加した汎用性がもたらす利益を簡単に上回ります。
他のヒント
Atesが答えに書いているように、CDownloaderメンバーのラッパーを作成する必要があるため、最終結果は単純な方法よりも冗長で理解しにくい可能性があります。たとえば、これは可能性があります(警告:未テストのコードが先にあります):
BOOL Get(const CDownloader &Loader, const CString& Name, int* Result)
{
return Loader.getInteger(Name, Result);
}
BOOL Get(const CDownloader &Loader, const CString& Name, CImage* Result)
{
return Loader.getImage(Name, SomeDefaultValueForNeedCache, Result);
}
BOOL Get(const CDownloader &Loader, const CString& Name, CFont* Result)
{
return Loader.getFont(Name, Result);
}
template<class T>
BOOL Download(const CDownloader &Loader, bool Flag, T* Result)
{
if (Flag) {
// first try the "name_1"
if ( Get(Loader, "name_1", Result) ) return TRUE;
}
// if "name_1" fails or disabled by flag, try "name_2"
return Get (Loader, "name_2", Result);
}
「賢い」ことを目指して、「getted」によってインデックス付けされたゲッターのboost :: fusion :: mapを作成することができます。タイプ:
fusion::map<
fusion::pair<int, boost::function<BOOL(const CDownloader&, int*)>,
fusion::pair<CImage, boost::function<BOOL(const CDownloader&, CImage*)>,
fusion::pair<CFont, boost::function<BOOL(const CDownloader&, CFont*)>
>
GetterMap = fusion::make_map(
fusion::make_pair<int>(bind(&CDownloader::getInteger, _1, _2)),
fusion::make_pair<CImage>(&CDownloader::getImage, _1, SomeDefaultValueForNeedCache, _2),
fusion::make_pair<CFont>(&CDownloader::getFont, _1, _2)
);
template<class T>
BOOL Download(const CDownloader &Loader, bool Flag, T* Result)
{
if (Flag) {
// first try the "name_1"
if ( fusion::at<T>(GetterMap)(Loader, "name_1", Result) ) return TRUE;
}
// if "name_1" fails or disabled by flag, try "name_2"
return fusion::at<T>(GetterMap)(Loader, "name_2", Result);
}
ご覧のとおり、単純な方法と比較した場合のゲインは明らかではありません。
異なるシグネチャに適応できるため、関数オブジェクトが最適だと思います。
struct FontLoader {
CFont *Font;
FontLoader() {}
BOOL operator()(const CDownloader& Loader, bool Flag) {
if (Flag && Loader.getFont("name_1", Font) )
return TRUE;
return Loader.getFont("name_2", Font);
}
};
struct ImageLoader {
CImage *Image;
BOOL NeedCache;
ImageLoader(BOOL nc) : NeedCache(nc) {}
BOOL operator()(const CDownloader& Loader, bool Flag) {
if (Flag && Loader.getImage("name_3", NeedCache, Image) )
return TRUE;
return Loader.getImage("name_4", NeedCache, Image);
}
};
template <typename T> // T has application operator CDownloader x bool -> T1
BOOL Download( const CDownloader &Loader, bool Flag, T& func)
{
return func(Loader, Flag);
}
呼び出しは次のようになります:
FontLoader Font_func;
BOOL ret1 = Download(Loader, Flag, Font_func);
ImageLoader Image_func(TRUE);
BOOL ret2 = Download(Loader, Flag, Image_func);
および渡された構造体には、ダウンロードされたオブジェクトが含まれます。 C ++ 0xでは、テンプレートパラメーターTの型チェックを改善する概念を定義できます。
ジェネリックラッパーを記述すると、3つのゲッターのメソッドシグネチャが異なるため、コードや重複が少なくなるとは思わない。これらに関係なく、ラッパー関数が必要になります。 3つの異なるダウンロード*機能を持つ簡単な方法を使用することもできます。マクロを使用して条件付きロジックを中央の場所に保持することができますが、それによりコードが非常に読みにくくなる可能性があり、その価値はありません。
メンバー関数へのポインタを使用してどこかにアクセスできます:
struct X
{
bool getInt(int* p) const { *p = 42; return true; }
bool getFloat(float* p) const { *p = 3.14; return true; }
};
template <class Func, class T>
bool load(const X& x, Func f, T* t)
{
return (x.*f)(t);
}
int main()
{
int i;
float f;
X x;
load(x, &X::getInt, &i);
load(x, &X::getFloat, &f);
//load(x, &X::getFloat, &i);
}
現在、getImageメソッドの例外により難しくなっています。代わりに、boost :: bind / std :: tr1 :: bindインスタンスのようなものでこの機能を試してみてください。
#include <boost/bind.hpp>
struct X
{
bool getInt(int* p) const { *p = 42; return true; }
bool getFloat(float* p, bool b) const { *p = 3.14; return b; }
};
template <class Func, class T>
bool load(Func f, T* t)
{
return f(t);
}
int main()
{
using namespace boost;
int i;
float f;
X x;
load(bind(&X::getInt, x, _1), &i);
load(bind(&X::getFloat, x, _1, true), &f);
}
これを行うC-hackyの方法を次に示します。
void* DownloadFont( const CDownloader &Loader, bool Flag, CFont *Font )
{
if (Flag) {
// first try the "name_1"
if ( Loader.getFont("name_1", Font) ) return (void*)1; //access this directly and *die*
}
// if "name_1" fails or disabled by flag, try "name_2"
return (void*)(Loader.getFont("name_2", Font);
}
最後に、整数/フォント/画像/ foobars /魔法の猿の特殊な取得に関連するロジックが何らかの形で必要になります。私はそれを吸い込んでDownload *()ファミリーを書くだけです。