1

次のような継承関係を持ち、マテリアル タイプに関連付けられたさまざまなプロパティを持つクラスを含むコードを作成する予定です。

  1. 抽象基本クラスFoo。それに関連付けられたプロパティはありません。
  2. Foo1GeneralElasticクラス Foo から継承し、おそらく異方性弾性材料に関連付けられたプロパティを持ちます。
  3. Foo2GeneralElasticもクラス Foo から継承し、 と同じ種類のマテリアル プロパティを持ちますがFoo1GeneralElastic、それ以外は異なります。
  4. Foo1PiezoElasticFoo1GeneralElastic圧電特性と一般的な弾性特性の両方を継承し、両方を備えています。
  5. Foo1IsotropicElasticから継承しFoo1GeneralElasticますが、そのプロパティを共有しません。

MyPropMap抽象基本クラスには、次のように定義されたtype のマップを受け取るメソッドが 1 つ以上あると判断しました。

typedef std::map<PropertyLabel,std::vector<double> > MyPropMap

PropertyLabel 型が何であるかについていくつかの異なるオプションがあり、それぞれの長所と短所を比較検討しています。

PropertyLabel をenum: これは軽量ですが、基本的には、検討しているすべての素材のさまざまなプロパティすべてのラベルの袋になります。

PropertyLabel を単にint: ここでは、マテリアル タイプごとに個別のヘッダー ファイルを用意します。それぞれのヘッダー ファイルには、関連するマテリアル プロパティのラベルとなる静的整数定数の定義が含まれます。たとえばMatPropKeyGenElastic.hpp、整数定数ELASTICITY_MATRIXMatPropKeyIsotropicElastic.hpp定義し、定数ELASTIC_MODULUSおよびPOISSONS_RATIOを定義MatPropKeyPiezoElastic.hpp#include、ファイルMatPropKeyGenElastic.hppを定義し、さらに定数 を定義しますPIEZO_CONST_MATRIX

注意が必要なのは、一緒に使用できる定数が同じ値にならないようにすることです。これは、これらの定数の値を一意の値に設定するスクリプトを使用してヘッダー ファイルを生成することで実現できます。

std::stringここから、いくつかの異なる方法を取ることができますコードのよう"ELASTICITY_MATRIX"に文字列リテラルを使用し、これらのリテラルのつづりを決して間違えないようにすることもできます。つまり、コンパイル時ではなく実行時に検出されるエラーです。上記の整数定数のスキームに似た方法で文字列定数を定義できます。定数を一意に保つタスクは簡単です。 to の値、 to の値などを設定するだけELASTICITY_MATRIXです。"ELASTICITY_MATRIX"POISSONS_RATIO"POISSONS_RATIO"

余分なオーバーヘッドは別として、私が目にするキャッチは、非 POD のグローバル静的定数に関連するホラー ストーリーを見たことです。トピックの非整数定数およびクラス文字列定数の定義のコメントにあるようなものです。 C++? . グローバル静的定数を配列にすることができると思います。これは、マップ キーとして使用すると暗黙的に s にconst char[]変換される POD です(いいえ、マップ キー自体を にするつもりはありません)。プリプロセッサを使用して文字列リテラルを定義することもできましたが、それらを名前空間内に保持できませんでした。std::stringconst char*

上記のアプローチのいずれかをお勧めしますか? 私が気付かなかった隠されたトラップがありますか?あなたが推奨する他のアプローチはまだありますか?

4

3 に答える 3

1

文字列を使用することはお勧めしません。このような単純な作業にはコストがかかりすぎます。私は列挙型に投票します。

しかし、すべてのラベル定数を 1 つの場所に保持するのが見苦しすぎる場合は、より複雑なアプローチを作成できます - 2 つの数値のペアのような複合キー (クラス ID、プロパティ ID) を使用します。

どちらも列挙型として定義でき、ネストされている可能性があります。さらに、クラス ID を自動的に生成することもできます。たとえば、reinterpret_castonstd::type_infoポインターを使用するか、単にstd::type_infoポインターを使用するか、またはstd::type_indexサポートされている場合です。コードでアイデアを説明する:

// PropertyLabel type, could be used as associative container key
struct PropertyLabel: std::pair<const std::type_info*, int>
{
  // Template ctor allows implicit conversion from enums
  // (actually not only from enums but from any int-compatible types)
  // Uncomment explicit keyword if implicit conversions scares you and use
  // explicit conversion syntax - PropertyLabel(smth).
  template <typename T> /*explicit*/ PropertyLabel(T label):
     std::pair<const std::type_info*, int>(&typeid(T), label)
  {
  }
};

// First property holder 
class PropertyUser1
{
public:
  enum Labels
  {
     eProperty1,
     eProperty2,
     eProperty3,
  };
 };

// Second property holder 
class PropertyUser2
{
public:
  enum Labels
  {
     eProperty1,// Due to class scope you could use same names for different properties
     eProperty2,
     eProperty3,
  };
 };

// Usage. A bit dangerous due to implicit conversions, but intuitive and handy:
MyPropMap properties;
properties[PropertyUser1::eProperty1].push_back(42.0);
properties[PropertyUser2::eProperty1].push_back(42.42);
// Will be with explicit ctor:
// properties[PropertyLabel(PropertyUser1::eProperty1)].push_back(42.0);
// properties[PropertyLabel(PropertyUser2::eProperty1)].push_back(42.42);

のような非列挙型を使用する可能性を排除するなど、型の安全性を高めることで改善できるようです。intたとえば、のような呼び出しを無効にしますPropertyLabel(42)。しかし、これは単にアイデアを説明するためのものです。

于 2012-09-03T23:00:59.310 に答える
0

私は、あまり大騒ぎせずに、私が望むものをほとんど提供する比較的単純な解決策を実現しました。このタイプの特定のインスタンスについて、等方性弾性、圧電、異方性弾性など特定の種類の材料MyPropMapの特性を扱っています。これにより、各マテリアルタイプに対応する列挙型を独自の名前空間でラップし、適切なヘッダーファイルに配置できます。たとえば、

// MatPropKey/IsotropicElastic.hpp:
namespace IsotropicElastic {
   enum { ELASTIC_MODULUS, POISSONS_RATIO };
}

// MatPropKey/GenElastic.hpp
namespace GenElastic {
   enum { ELASTICITY_MATRIX }
}

// MatPropKey/PiezoElastic.hpp
namespace PiezoElastic {
   enum { PIEZO_CONST_MATRIX, ELASTICITY_MATRIX }
}

ここにはある程度の冗長性がありますが、私はそれで生きることができます。上記の規則に固執する限り、各名前空間内で値は一意であり、特定の名前空間内の値を---とにかくやりたい---の各インスタンスenumにのみ使用する限り、元気です。(現実的には、これらの名前空間のそれぞれを共通の名前空間内にラップしたいと思います。)もちろん、これは絶対確実ではありません。十分に創造的な愚か者は、たとえば、両方を決定し、次に。を使用することができます。その後、悪いことが起こる可能性があります。それでも、コードは名前付き定数がどのようにグループ化されるかを伝え、不要な名前の衝突を回避することは簡単です。enumMyPropMapMPKey#includeGenElastic.hppPiezoElastic.hppGenElastic::ELASTICITY_MATRIXPiezoElastic::PIEZO_CONST_MATRIX

早く考えたらいいのに。

于 2012-09-04T21:55:48.537 に答える
0

少し考えた後、私はいくつかのことに気づきました:

  1. マップの記述方法をもう少し制御できるように、マップをクラス内にラップすることをお勧めします。
  2. ラップされたマップでさえ汎用的であり、任意のマテリアル パラメータ タイプに対応できる必要があるため、私が提供できるコンパイル タイプの安全性は限られています。

これを考慮して、MatProp大まかに次のようにクラスを設計することにしました。

#include <vector>
#include <map>

class MatProp {
public:
   // Skipping the constructor details ...

   void setProp_Raw(int propId, double val);
   void getProp_Raw(int propId, double & val) const;

   void setProp_Raw(int propId, const std::vector<double> & vals);
   void getProp_Raw(int propId, std::vector<double> & vals) const;

   // More overloaded set/get funcs for complex scalars and vectors ...

private:
   // The typedef allows me to write MatPropMap_::iterator, etc. in the
   // implementation of the member functions, which is handy if, say, 
   // I want to swap the std::map for an unordered_map later on.
   typedef std::map<PropertyLabel,std::vector<double> > MatPropMap_;

   MatPropMap_ matPropMap_;

};

_Rawプロパティ ID と値の組み合わせを間違えやすいため、set/get 関数には接尾辞 が付けられています。のコンストラクターに情報を渡して、MatPropこれらの関数への入力を実行時に検証できるようにすることもできますが、その設定は扱いにくくなり、クラスが使いにくくなる可能性があります。安全性をさらに高めるために、たとえば次のようにします。

void setIsotropicLinearElasticParameter(MatProps mProp,
          ElasPropEnum propId, // ELASTIC_MODULUS and POISSONS_RATIO are the 
                               // *only* valid values of this parameter.
          double val) {
    mProp.setParam_Raw(propId, val);
}

関数は単純ですが、(1) 許可されるキーは 2 つだけであり、(2) それらは実際には型であると想定されていることを明確に宣言していますdouble。このインターフェースは完全に万能というわけではありませんが、正しく使用するのはかなり簡単で、間違って使用するには多少の努力が必要です。FWIW、同様のことがここで行われました: http://blog.knatten.org/2010/04/23/make-apis-hard-to-use-incorrectly/

于 2012-09-06T02:17:26.987 に答える