Я в настоящее время реализую систему с объектами представления многого класса, такими как клиент, бизнес, продукт и т.д. Стандартная бизнес-логика. Поскольку можно было бы ожидать, что каждый класс имеет много стандартных атрибутов.
У меня есть длинный список чрезвычайно идентичных требований, таких как:
Бизнес класса имеет сектор атрибута, и клиент имеет расположение атрибута. Очевидно это реляционная проблема и в псевдо 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 () в примере) я хотел бы иметь единственную универсальную функцию агрегирования, которая составляет всех возможных пар атрибута класса, таким образом, что всем требованиям агрегирования отвечают.. и в конечном счете дополнительные атрибуты добавляются позже, или дополнительные требования агрегирования, они будут автоматически составляться.
Таким образом, существует несколько проблем здесь, но основной, я ищу понимание, - то, как отобразить аргумент во время выполнения атрибуту класса.
Я надеюсь, что обеспечил достаточно детали для соответствующего описания то, что я пытаюсь сделать...
Вам нужно будет хранить свои данные в хэш-карте или карте нормалей, а не в виде необработанных данных C ++. Невозможно перейти от строки «hai» к A.hai без переключения / case / default для всех возможных значений в C ++.
Что касается предыдущего ответа, это отражение, а не RTTI. В C ++ есть RTTI - для этого и нужен typeid (). Но C ++ определенно не поддерживает это, как бы вы это ни называли.
Вы определенно предоставили достаточно подробностей :) Надеюсь, вы понимаете, что это будет нелегко.
То, что вы ищете, называется "Отражение", способность объекта описывать самого себя.
Отражение - это сложно, но, тем не менее, это можно сделать!
Я обычно являюсь поклонником меташаблонов и тому подобного, но в этот раз я собираюсь предложить чистый 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
, но с добавленной ценностью иметь имя данной структуры.
Боюсь, что это может оказаться немного более непонятным, хотя и без очевидного выигрыша... также, я должен добавить, что если возможно, вы могли бы захотеть напрямую кодировать на языке, который поддерживает это нативно :)
.Итак, ответ на главный вопрос, как сопоставить аргумент времени выполнения с атрибутом класса, таков: Нельзя
То, что вы ищете, обычно называется 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 - два примера.