Универсальное Агрегирование Объектов C++ Атрибутом, Когда Название атрибута Неизвестно в Runtime

Я в настоящее время реализую систему с объектами представления многого класса, такими как клиент, бизнес, продукт и т.д. Стандартная бизнес-логика. Поскольку можно было бы ожидать, что каждый класс имеет много стандартных атрибутов.

У меня есть длинный список чрезвычайно идентичных требований, таких как:

  • способность получить весь бизнес, промышленность которого является обрабатывающей.
  • способность получить все клиенты, базирующиеся в Лондоне

Бизнес класса имеет сектор атрибута, и клиент имеет расположение атрибута. Очевидно это реляционная проблема и в псевдо SQL посмотрело бы что-то как:

SELECT ALL business in business' WHERE sector == manufacturing

К сожалению, включение DB не является опцией.

То, что я хочу сделать, имеют единственную универсальную функцию агрегирования, подпись которой приняла бы форму:

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

Где класс является классом объекта, я хочу агрегировать, приписать и оценить быть атрибутом класса и значением интереса. В моем примере я поместил вектор как тип возврата, но это не работало бы. Вероятно, лучше объявить вектор соответствующего типа класса и передать его как аргумент. Но это не основная проблема.

Как я могу принять аргументы в строковой форме для класса, атрибута и значения и затем отобразить их в функции агрегирования родового объекта?

Так как это грубо не к почтовому индексу, ниже фиктивная программа, которая создает набор объектов образно именованных классов. Включенный определенная функция агрегирования, которая возвращает вектор объектов B, объект которых равен идентификатору, указанному в командной строке, например.

$ ./aggregations 5

который возвращает весь B, чей объекты, которые 'я' приписываю, равны 5. Посмотрите ниже:

#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 () в примере) я хотел бы иметь единственную универсальную функцию агрегирования, которая составляет всех возможных пар атрибута класса, таким образом, что всем требованиям агрегирования отвечают.. и в конечном счете дополнительные атрибуты добавляются позже, или дополнительные требования агрегирования, они будут автоматически составляться.

Таким образом, существует несколько проблем здесь, но основной, я ищу понимание, - то, как отобразить аргумент во время выполнения атрибуту класса.

Я надеюсь, что обеспечил достаточно детали для соответствующего описания то, что я пытаюсь сделать...

1
задан Péter Török 9 May 2010 в 14:54
поделиться

3 ответа

Вам нужно будет хранить свои данные в хэш-карте или карте нормалей, а не в виде необработанных данных C ++. Невозможно перейти от строки «hai» к A.hai без переключения / case / default для всех возможных значений в C ++.

Что касается предыдущего ответа, это отражение, а не RTTI. В C ++ есть RTTI - для этого и нужен typeid (). Но C ++ определенно не поддерживает это, как бы вы это ни называли.

1
ответ дан 3 September 2019 в 00:43
поделиться

Вы определенно предоставили достаточно подробностей :) Надеюсь, вы понимаете, что это будет нелегко.

То, что вы ищете, называется "Отражение", способность объекта описывать самого себя.

Отражение - это сложно, но, тем не менее, это можно сделать!

Я обычно являюсь поклонником меташаблонов и тому подобного, но в этот раз я собираюсь предложить чистый OO-способ. Он интригующий в том смысле, что вам придется фактически модифицировать ваши объекты для поддержки этого.

Основная идея состоит в том, чтобы сделать это так, как это делают языки, поддерживающие эту идиому.

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, но с добавленной ценностью иметь имя данной структуры.

Боюсь, что это может оказаться немного более непонятным, хотя и без очевидного выигрыша... также, я должен добавить, что если возможно, вы могли бы захотеть напрямую кодировать на языке, который поддерживает это нативно :)

.
1
ответ дан 3 September 2019 в 00:43
поделиться

Итак, ответ на главный вопрос, как сопоставить аргумент времени выполнения с атрибутом класса, таков: Нельзя

То, что вы ищете, обычно называется Runtime Type Information (RTTI для краткости), то есть способность получать информацию о типах во время выполнения.

C++ не поддерживает RTTI. К лучшему или к худшему, он просто не поддерживает, и точка.

Поэтому вам придется придумать что-то свое. В общих чертах, вы заставите все ваши классы B реализовать интерфейс, который будет иметь метод типа getClass( string className ), который вернет указатель на ваш объект A, который, в свою очередь, также реализует другой интерфейс, который будет иметь метод типа getAttribute( string attrName ), и затем вы сможете сравнить его с вашим значением. Другая возможность заключается в том, чтобы A имел метод compareToValue( string attrName, string value ) вместо getAttribute, чтобы он мог работать с механикой значения, а не просто сравнивать как строки.

Кроме того, вы могли бы иметь соответствующие методы getAllClasses и getAllAttributes для получения списков - если вам это нужно.

Кроме того, если вы не привязаны к C++, есть и другие платформы, которые поддерживают RTTI. Java и .NET - два примера.

0
ответ дан 3 September 2019 в 00:43
поделиться
Другие вопросы по тегам:

Похожие вопросы: