背景:
当社のソフトウェアは複数の API を使用してファイル i/o を実行します:
FILE*
、CStdio
(およびいくつかの派生物) HANDLE
、...
私はFilePointer
用の RAII ラッパーを作成しましたFILE*
。これは、既存のすべての C コードのドロップイン置換として役立ちました。
新しいコードでは、通常、CStdio の派生クラスまたはラッパー クラスが使用されていました。
最近、SimpleTextFile
以前のバージョンの MBCS に加えて、UTF-16LE I/O を処理する を作成しました。
これらのさまざまなクラスのインターフェイスは似ていますが、同一ではありません。ポリシー テンプレート クラスを使用していくつかのユーティリティ アルゴリズムを記述し、ユーティリティ アルゴリズムをさまざまなファイル タイプに適合させることができると考えました。これはある程度成功していますが、ユーティリティ アルゴリズム内で、ある種のライン リーダー フィルターを混在させる必要があることがよくあります。
そして、ここで問題が発生します-フィルター付きの行リーダーを混在させた場合、これが渡されるアルゴリズムは、ポリシークラスを使用して、基になる型に適応する方法を理解できなくなります(R
現在はa Wrapper<R>
、および a のポリシーは存在しませんWrapper<R>
)。
質問:
既存の型に新しい動作を提供しながら、基礎となる型で機能するさまざまなポリシーを引き続き機能させることができる mixin テンプレート クラスを作成するにはどうすればよいですか?
詳細:
ポリシー テンプレート:
StreamPositionPolicy<T>
- T に適合した GetPosition() および SetPosition() を
提供します。 - LineReaderPolicy<T>
T から行を読み取るためのインターフェイスの汎用セットを提供します。
- TFileNamePolicy<T>
に GetFilename() を提供します。
したがって、T が CStdio の派生物、または FILE* の場合、上記は、元のファイル名を探し、行を読み取り、取得するための共通のインターフェイスを提供するために最善を尽くします。
さらに、I Have:
FilteredStringReader<F,R>
はフィルターをリーダーに結合します。以前は、次のようにしていました。
template <typename Filter, typename Reader>
class FilteredStringReader
{
Filter m_filter;
Reader & m_reader;
public:
// Constructors
FilteredStringReader(
Filter filter,
Reader & reader
) :
m_filter(filter),
m_reader(reader)
{
}
bool ReadString(CString & strLine)
{
return ReadFilteredString(m_reader, m_filter, strLine);
}
};
これは、LineReaderPolicy<> を使用するすべてのアルゴリズムでうまく機能します。これは、既定のポリシーが ReadString() インターフェイスの使用を試みることであり、このインターフェイスが既定の (汎用) ポリシーと一致し、問題がないためです。
ただし、このオブジェクトが、他のポリシーの 1 つを使用する必要があるアルゴリズムの 1 つに渡されるとStreamPositionPolicy<FilteredStringReader<F,R>>
、このスキームは破綻します。StreamPositionPolicy<>
aには aがなく、FilteredStringReader<>
aFilteredStringReader<>
はデフォルトに適合しませんStreamPositionPolicy<>
(ストリーム インターフェイスや名前インターフェイスなどではなく、ライン リーダー インターフェイスのみを提供します)。
そのため、そのような mixin はおそらく CRTP を使用し、その基になるファイル タイプ/リーダー タイプから派生する必要があると考えていました。次に、それはそれらの1つであり、基礎となるリーダーに特化したポリシークラスはすべて成功します。
しかし、それは生涯/所有権/コピーの問題を引き起こします:
template <typename Filter, typename Reader>
class FilteredStringReader : public Reader
{
Filter m_filter;
public:
// Constructors
FilteredStringReader(
Filter filter,
Reader & reader
)
: Reader(reader)
, m_filter(filter)
{
}
bool ReadString(CString & strLine)
{
return ReadFilteredString(m_reader, m_filter, strLine);
}
};
驚くべきことに、この種の動作 - このポリシー オブジェクトの構築は可能です... ただし、リーダー インスタンスをコピーします (リーダーの実装によっては、これは壮大なアイデアではない可能性があります。または、より可能性が高いのは、一部のリーダー タイプが単純に勝つ可能性があります)。コピーは許可されません)。
リーダー オブジェクトの 1 つのインスタンス (mixin テンプレート インスタンスによってラップされるインスタンス) だけが必要です。
だから、これは間違った道を進んでいるような気がします。
可変個引数テンプレートを利用でき、場合によっては完全転送を使用して、ミックスイン自体とそのベースをインプレースで構築できます。しかし、それは以前の化身の機能の一部を失います: 表示された元のバージョンはFilteredStringReader<F,R>
、リーダーの上にレイヤー化され、使用され、その後破棄される可能性があり、リーダー自体の寿命は続きます (または、別のアルゴリズムの目的のためにより深くラップされました)。 )。
そのため、CRTP を使用するのは適切ではないようです。しかし、それから、他のすべてをそのままにして、1つのインターフェイスだけをインターセプトするタイプRのラッパーを作成する方法という元の問題に戻りますか?