1

私は現在、クライアント、ビジネス、製品などのオブジェクトを表す多くのクラスを備えたシステムを実装しています。標準的なビジネスロジック。当然のことながら、各クラスには多数の標準属性があります。

次のような本質的に同一の要件の長いリストがあります。

  • 産業が製造業であるすべてのビジネスを検索する機能。
  • ロンドンに拠点を置くすべてのクライアントを取得する機能

クラス ビジネスには属性セクターがあり、クライアントには属性ロケーションがあります。明らかにこれはリレーショナルの問題であり、疑似 SQL では次のようになります。

SELECT ALL business in business' WHERE sector == manufacturing

残念ながら、DB へのプラグインはオプションではありません。

私がやりたいことは、署名が次の形式をとる単一の汎用集計関数を持つことです。

vector<generic> genericAggregation(class, attribute, value);

class は集約したいオブジェクトのクラスであり、attribute と value は対象のクラス属性と値です。私の例では、ベクトルを戻り値の型として入れましたが、これはうまくいきません。おそらく、関連するクラス型のベクトルを宣言し、それを引数として渡す方がよいでしょう。しかし、これは主な問題ではありません。

クラス、属性、および値の文字列形式で引数を受け取り、これらを汎用オブジェクト集約関数にマップするにはどうすればよいですか?

コードを投稿しないのは失礼なので、以下は想像力に富んだ名前のクラスのオブジェクトの束を作成するダミー プログラムです。含まれているのは、コマンド ラインで指定された ID と等しい A オブジェクトを持つ B オブジェクトのベクトルを返す特定の集計関数です。

$ ./aggregations 5

これは、A オブジェクトの 'i' 属性が 5 に等しいすべての B を返します。以下を参照してください。

#include <iostream>
#include <cstring>
#include <sstream>
#include <vector>

using namespace std;

//First imaginativly names dummy class
class A {
 private:
  int i;
  double d;
  string s; 
 public:
  A(){}
  A(int i, double d, string s) {
   this->i = i;
   this->d = d;
   this->s = s;
  }
  ~A(){}
  int getInt() {return i;} 
  double getDouble() {return d;} 
  string getString() {return s;}
};

//second imaginativly named dummy class
class B {
 private:
  int i;
  double d;
  string s;
  A *a; 
 public:
  B(int i, double d, string s, A *a) {
   this->i = i;
   this->d = d;
   this->s = s;
   this->a = a;
  }
  ~B(){}
  int getInt() {return i;} 
  double getDouble() {return d;} 
  string getString() {return s;}
  A* getA() {return a;}
};

//Containers for dummy class objects
vector<A> a_vec (10);
vector<B> b_vec;//100

//Util function, not important..
string int2string(int number) {
 stringstream ss;
 ss << number;
 return ss.str();
}


//Example function that returns a new vector containing on B objects
//whose A object i attribute is equal to 'id'
vector<B> getBbyA(int id) {
 vector<B> result;
 for(int i = 0; i < b_vec.size(); i++) {
  if(b_vec.at(i).getA()->getInt() == id) {
   result.push_back(b_vec.at(i));
  }
 }
 return result;
}



int main(int argc, char** argv) {

 //Create some A's and B's, each B has an A...
 //Each of the 10 A's are associated with 10 B's.
 for(int i = 0; i < 10; ++i) {
  A a(i, (double)i, int2string(i));
  a_vec.at(i) = a;
  for(int j = 0; j < 10; j++) {
   B b((i * 10) + j, (double)j, int2string(i), &a_vec.at(i));   
   b_vec.push_back(b);
  }
 }

 //Got some objects so lets do some aggregation

 //Call example aggregation function to return all B objects 
 //whose A object has i attribute equal to argv[1]
 vector<B> result = getBbyA(atoi(argv[1]));

 //If some B's were found print them, else don't...
 if(result.size() != 0) {
  for(int i = 0; i < result.size(); i++) {
   cout << result.at(i).getInt() << " " << result.at(i).getA()->getInt() << endl;
  }
 }
 else {
  cout << "No B's had A's with attribute i equal to " << argv[1] << endl;
 }

 return 0;
}

コンパイル:

g++ -o aggregations aggregations.cpp

ご希望の場合 :)

別の集計関数 (つまり、例では getBbyA() ) を実装する代わりに、すべての集計要件が満たされるように、すべての可能なクラス属性ペアを説明する単一の汎用集計関数が必要です..そしてイベントでは追加の属性後で追加される場合、または追加の集計要件がある場合、これらは自動的に考慮されます。

ここにはいくつかの問題がありますが、私が洞察を求めている主な問題は、ランタイム引数をクラス属性にマップする方法です。

私がやろうとしていることを適切に説明するのに十分な詳細を提供できたことを願っています...

4

3 に答える 3

1

あなたは間違いなく十分な詳細を提供しました:)簡単ではないことを理解していただければ幸いです。

あなたが探しているのは、「反射」と呼ばれる、オブジェクトがそれ自体を説明する能力です。

振り返りは難しいですが、それでもできます!

私は通常、メタテンプレートなどのファンですが、今回は純粋な OO-Way を提案します。これをサポートするには、オブジェクトを実際に変更する必要があるという意味で、本質的です。

基本的な考え方は、このイディオムをネイティブにサポートする言語と同じ方法で行うことです。

1-基本タイプを適応させます

enum Type
{
  Int,
  String
};

class Field
{
public:
  Type type() const { return mType; }

  virtual Field* clone() const = 0;

  virtual std::string toString() const = 0;
  virtual void fromString(const std::string& s) = 0;

  virtual ~Field() {}
protected:
  explicit Field(Type t): mType(t) {}
private:
  Type mType;
};

bool operator==(const Field& lhs, const Field& rhs)
{
  return lhs.type() == rhs.type() && lhs.toString() == rhs.toString();
}

class IntField: public Field
{
public:
  typedef int data_type;

  IntField(): Field(Int), mData() {}

  virtual IntField* clone() const { return new IntField(*this); }

  data_type get() const { return mData; }
  void set(data_type d) { mData = d; }

  virtual std::string toString() const
  { return boost::lexical_cast<std::string>(mData); }

  virtual void fromString(const std::string& s)
  { mData = boost::lexical_cast<data_type>(s); }

private:
  data_type mData;
};

// Type information allow for more efficient information
bool operator==(const IntField& lhs, const IntField& rhs)
{
  return lhs.get() == rhs.get();
}

2-次に、これらすべてを保持できるクラスを構築します

class Object
{
public:
  virtual ~Object(); // deal with memory

  Field* accessAttribute(const std::string& name);
  const Field* getAttribute(const std::string& name) const;
  void setAttribute(const std::string& name, const Field& value);

protected:
  // Deal with memory
  Object();
  Object(const Object& rhs);
  Object& operator=(const Object& rhs);

  void addAttribute(const std::string& name, const Field& value);

private:
  std::map<std::string, Field*> mFields; // tricky, we must deal with memory here
};

3- 使用法:

class Person: public Object
{
public:
  Person(const std::string& surname,
         const std::string& givenname,
         int age): Object()
  {
    this->addAttribute("surname", StringField(surname));
    this->addAttribute("givenname", StringField(givenname));
    this->addAttribute("age", IntField(age));
  }
};

int main(int argc, char* argv[])
{
  // initialize
  std::vector<Person> persons = /**/;

  std::cout << "Please enter an attribute and the expected value" << std::endl;
  std::string name, value;
  std::cin >> name >> value;

  std::vector<Person*> result;
  for (std::vector<Person>::iterator it = persons.begin(), end = persons.end();
       it != end; ++it)
  {
    const Field* field = it->getAttribute(name);
    if (field && field->toString() == value) result.push_back(&(*it));
  }

  std::cout << "Selected persons for " << name
            << " = " << value << " are:\n";
  for (std::vector<Person*>::iterator it = result.begin(), end = result.end();
       it != end; ++it)
  {
    const Person& person = **it;
    std::cout << "  " << person.surname() << " " << person.givenname() << "\n";
  }
  std::cout.flush();
}

おそらく他にも多くの方法があり、多かれ少なかれ自動化されています。特に、既存のクラスに対する適応構造を考えることができます。マクロに似たものBOOST_FUSION_ADAPT_STRUCTですが、指定された構造の名前を持つという付加価値があります。

明らかな利点がないため、もう少しあいまいであることが判明する可能性があることを恐れています...また、可能であれば、これをネイティブにサポートする言語で直接コーディングすることをいとわないかもしれないことを追加する必要があります:)

于 2010-05-09T17:46:59.383 に答える
1

生の C++ データとしてではなく、ハッシュマップまたは法線マップにデータを保存する必要があります。C++ で考えられるすべての値に対して switch/case/default を実行せずに文字列 "hai" から A.hai に移動することは不可能です。

前の回答に関しては、これはリフレクションであり、RTTI ではありません。C++ には RTTI があります。それが typeid() の目的です。しかし、あなたがそれを何と呼びたいとしても、C++は間違いなくそれをサポートしていません。

于 2010-05-09T15:58:38.330 に答える
0

したがって、ランタイム引数をクラス属性にマップする方法という主な質問に対する答えは、できません

あなたが探しているのは、通常、Runtime Type Information (略して RTTI) と呼ばれ、実行時に型に関する情報を取得する機能です

C++ は RTTI をサポートしていません。良くも悪くも、話は終わりです。

したがって、独自の何かを発明する必要があります。大まかに言えば、すべてのBクラスに のようなメソッドを持つインターフェースを実装さgetClass( string className )せ、オブジェクトへのポインターを返し、次にAのようなメソッドを持つ別のインターフェースも実装しgetAttribute( string attrName )ます。それをあなたの価値と比較することができます。もう 1 つの可能性は、の代わりにAメソッドを使用することです。これにより、単に文字列として結合するのではなく、値のメカニズムを処理できます。compareToValue( string attrName, string value )getAttribute

さらに、必要に応じて、対応するgetAllClassesandgetAllAttributesメソッドを使用してリストを取得できます。

また、C++ に縛られていない場合は、RTTI をサポートする他のプラットフォームがあります。Java と .NET は 2 つの例です。

于 2010-05-09T15:09:47.547 に答える