特定のデータソースに関するメタデータを格納するクラスを作成中です。メタデータは、XMLの構造と非常によく似たツリーで構造化されています。メタデータ値は、整数値、10進値、または文字列値にすることができます。
このような状況でバリアントデータを格納するためのC++の良い方法があるかどうか知りたいです。バリアントで標準ライブラリを使用したいので、使用可能なCOM、Ole、およびSQLVARIANTタイプは避けています。
私の現在の解決策は次のようになります。
enum MetaValueType
{
MetaChar,
MetaString,
MetaShort,
MetaInt,
MetaFloat,
MetaDouble
};
union MetaUnion
{
char cValue;
short sValue;
int iValue;
float fValue;
double dValue;
};
class MetaValue
{
...
private:
MetaValueType ValueType;
std::string StringValue;
MetaUnion VariantValue;
};
MetaValueクラスには、現在格納されているバリアント値を取得するためのさまざまなGet関数がありますが、値に対するすべてのクエリが、探している値を特定するためのif/elseifステートメントの大きなブロックになります。
また、値を文字列としてのみ保存し、変換を実行してさまざまなバリアント型を取得することも検討しましたが、これを確認した限り、内部の文字列の解析とエラー処理が大量に発生します。浮動小数点値に関する精度とデータ損失の問題の大きな古い缶を上げても、上記の問題がある場合はクエリを排除しません。
標準ライブラリを使用してC++バリアントデータ型に使用するのがよりクリーンなものを実装または見た人はいますか?
C++ 17の時点で、 std::variant
。
まだ使用できない場合は、 Boost.Variant が必要になる場合があります。ポリモーフィズムをモデル化するための類似しているが異なるタイプは、 std::any
(そして、C++ 17以前、 Boost.Any )。
追加のポインタと同じように、「 型消去 」を探すことができます。
Konradの答え(既存の標準化されたソリューションを使用)は、バグが発生しやすい独自のバージョンを作成するよりも確かに望ましいですが、ブーストバリアントには、特にコピーの構築とメモリにいくつかのオーバーヘッドがあります。
一般的なカスタマイズされたアプローチは、次の変更されたファクトリパターンです。
Derived
クラスを使用してインターフェースを実装します。create
関数を使用してファクトリクラスを作成します。template <typename _T> Base * Factory::create ();
これにより、ヒープ上に_Derived<_T>
_オブジェクトが内部的に作成され、動的キャストポインタが再調整されます。実装するクラスごとにこれを専門にします。
最後に、この_Base *
_ポインターを含み、テンプレートのget関数とset関数を定義するVariant
ラッパーを定義します。 getType()
、isEmpty()
、代入演算子、等式演算子などのユーティリティ関数は、ここで適切に実装できます。
ユーティリティ関数とファクトリ実装に応じて、サポートされるクラスは、代入やコピー構築などのいくつかの基本的な関数をサポートする必要があります。
また、システム上でdoubleのサイズのvoid *と、使用しているタイプの列挙型を持つ、よりCっぽいソリューションに進むこともできます。それはかなりクリーンですが、システムの生のバイトに完全に満足している人にとっては間違いなく解決策です。
C++ 17にはstd::variant
があり、まさにあなたが探しているものです。
質問は長い間答えられていましたが、記録のために私は言及したいと思います QVariant もこれを行います。