binder
От: Yagg Россия  
Дата: 15.09.03 11:41
Оценка:
Очень хочется засунуть работу с СУБД в отдельный класс, чтобы основная
работа программы шла только с объектами типа
class Person
{
property<int> id;
property<std::string> name;
...
};
и не зависела от запросов, таблиц...

Но тут возникает проблема — как обозначить соответствие между именами
полей в таблице БД и членами класса.
Хочется, чтобы работало примерно следующим образом:
Person p;
property<int>& id = binder<p,"PersonID">;
property<std::string>& name = binder<p,"PersonName">;
То есть по экземпляру класса и строке нужно возвращать ссылку на член
этого класса.
И обратно:
std::string field_name = binder<p,p.id>
То есть по экземпляру класса и ссылке(?) на член возвращать стоку с
названием поля.

Пока я придумал делать только так:
---------------------------------------------------------------
template<class T>
class bounder
{
public:
prop_base& operator()(const std::string& fname, T& obj)
{
}
};

template<class P, class O>
property<P>&
b_cast(O& obj, std::string fld)
{
static bounder<O> b;
return dynamic_cast<property<P>& >( b(fld, obj) );
}

template<>
class bounder<Person>
{
public:
prop_base& operator()(const std::string& fname, Person& obj)
{
if(stolower(fname)=="personid")
return obj.id;
if(stolower(fname)=="personname")
return obj.name;
...
}
};

void f()
{
property<int>& id = b_cast<int>(p, "PersonID");
}
---------------------------------------------------------------
А как сделать наоборот(получить строку по члену класса) придумать не
могу. Единственное, что приходит на ум — использовать offsetof, но это
как-то неправильно с точки зрения ОП...
Может кто сталкивался с такими проблемами? Подскажите как быть...
Может я вообще не то делаю?
Re: binder
От: MaximE Великобритания  
Дата: 15.09.03 11:58
Оценка:
Здравствуйте, Yagg, Вы писали:

Y>Очень хочется засунуть работу с СУБД в отдельный класс, чтобы основная

Y>работа программы шла только с объектами типа
Y>class Person
Y>{
Y> property<int> id;
Y> property<std::string> name;
Y> ...
Y>};

Например так (memento pattern):

class person
{
public:
    // ctors

public:
    const uuid& id() const { return variant_cast<uuid&>(memento_["id"]); }
    void id(const uuid& u) { memento_["id"] = u; }

    string name() const { return variant_cast<string>(memento_["name"]); }
    void name(const string& s) { memento_["name"] = s,c_str(); }

private:
    typedef map<string, variant> memento;
    memento memento_;
};


Должна быть фабрика, которая будет общаться с БД и читать/записывать memento и создавать твои конкретные классы. Так как класс обращается к свом свойствам по символьным именам, запросы к БД должны при необходимости переименовывать столбцы.

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

Y>и не зависела от запросов, таблиц...


Вот чтобы она ни отчего не зависела сделать непросто.
Re[2]: binder
От: Аноним  
Дата: 15.09.03 12:08
Оценка:
Здравствуйте, MaximE, Вы писали:

ME>Здравствуйте, Yagg, Вы писали:


Y>>Очень хочется засунуть работу с СУБД в отдельный класс, чтобы основная

Y>>работа программы шла только с объектами типа
Y>>class Person
Y>>{
Y>> property<int> id;
Y>> property<std::string> name;
Y>> ...
Y>>};

ME>Например так (memento pattern):


ME>
ME>class person
ME>{
ME>public:
ME>    // ctors

ME>public:
ME>    const uuid& id() const { return variant_cast<uuid&>(memento_["id"]); }
ME>    void id(const uuid& u) { memento_["id"] = u; }

ME>    string name() const { return variant_cast<string>(memento_["name"]); }
ME>    void name(const string& s) { memento_["name"] = s,c_str(); }

ME>private:
ME>    typedef map<string, variant> memento;
ME>    memento memento_;
ME>};
ME>


Поясни, плиз, из какой библиотеки variant_cast ?
(я так понимаю она из _variant_t в соответствующий тип преобразует ) ...


ME>Должна быть фабрика, которая будет общаться с БД и читать/записывать memento и создавать твои конкретные классы. Так как класс обращается к свом свойствам по символьным именам, запросы к БД должны при необходимости переименовывать столбцы.


ME>Св-ва для удобства можно обернуть в proxy, которые различают запись и чтение.


Y>>и не зависела от запросов, таблиц...


ME>Вот чтобы она ни отчего не зависела сделать непросто.
Re[2]: binder
От: Yagg Россия  
Дата: 15.09.03 12:09
Оценка:
Здравствуйте, MaximE, Вы писали:

ME>Например так (memento pattern):

[...]
ME>private:
ME> typedef map<string, variant> memento;
ME> memento memento_;
Очень смущает присутствие лишних данных. Объектов ведь будет много и все в памяти,
так что держать в _каждом_ объекте описание его полей мне кажется нерациональным.
Делать статическими? Или для каждого класса фабрику?
Re[3]: binder
От: Yagg Россия  
Дата: 15.09.03 12:11
Оценка:
Здравствуйте, Аноним, Вы писали:

А>Поясни, плиз, из какой библиотеки variant_cast ?

А>(я так понимаю она из _variant_t в соответствующий тип преобразует ) ...
Это не важно...
Re[3]: binder
От: MaximE Великобритания  
Дата: 15.09.03 12:26
Оценка:
Здравствуйте, Yagg, Вы писали:

Y>Здравствуйте, MaximE, Вы писали:


ME>>Например так (memento pattern):

Y>[...]
ME>>private:
ME>> typedef map<string, variant> memento;
ME>> memento memento_;
Y>Очень смущает присутствие лишних данных. Объектов ведь будет много и все в памяти,
Y>так что держать в _каждом_ объекте описание его полей мне кажется нерациональным.
Y>Делать статическими? Или для каждого класса фабрику?

Делать для каждого класса фабрику очень расточительно и утомительно. Фабрика должна быть максимально абстрактной и знать лишь имена запросов к БД для того, чтобы писать/читать мементо и адрес функции для создания класса.

Каждый объект будет содержать только имена и значения своих полей (в memento_). Конечно, можно сэкономить на именах, запихнув значения в vector<> и доступаюсь по индексу. Но так ты сильно потеряешь в гибкости и легкости поддержки.

Я тут тебе привел псевдокод (по поводу varaint_cast<>). Здесь varaint — это может быть и _variant_t и любой другой подходящий tagged/discriminated union (как и было в реальном проекте).
Re: binder
От: alnsn Великобритания http://nasonov.blogspot.com
Дата: 15.09.03 12:48
Оценка: 7 (1)
Есть задумка написать framework для связывания имен с членами классов. Отсюда уже и до имен полей в БД недалеко. Все идеи и код приветствуются

Демо-код:
http://groups.yahoo.com/group/boost/files/describe_classes.tar.gz

Обсуждение в бусте:
http://lists.boost.org/MailArchives/boost/msg49135.php


Небольшое пояснение для тех, кто не любит ходить по ссылкам.
Код в примере пишет дамп объекта в XML, используя описание класса:

   class_("Driver").derived_from<Person>()
   [
       member(&Driver::licence_id, "licence_id"), // note comma operator
       member(&Driver::licence_issue_date, "licence_issue_date")
   ];
Re[2]: binder
От: Аноним  
Дата: 23.09.03 10:36
Оценка:
Здравствуйте, alnsn, Вы писали:

A>Есть задумка написать framework для связывания имен с членами классов. Отсюда уже и до имен полей в БД недалеко. Все идеи и код приветствуются


A>Демо-код:

A>http://groups.yahoo.com/group/boost/files/describe_classes.tar.gz

A>Обсуждение в бусте:

A>http://lists.boost.org/MailArchives/boost/msg49135.php


A>Небольшое пояснение для тех, кто не любит ходить по ссылкам.

A>Код в примере пишет дамп объекта в XML, используя описание класса:

A>
A>   class_("Driver").derived_from<Person>()
A>   [
A>       member(&Driver::licence_id, "licence_id"), // note comma operator
A>       member(&Driver::licence_issue_date, "licence_issue_date")
A>   ];
A>


Хм ... вообще-то надо бы еще где нибудь и типы членов классов указывать, а также их длины ( особенно актуально если член класса это char* )
Re[3]: binder
От: MaximE Великобритания  
Дата: 23.09.03 11:33
Оценка:
Здравствуйте, Аноним, Вы писали:

A>>Небольшое пояснение для тех, кто не любит ходить по ссылкам.

A>>Код в примере пишет дамп объекта в XML, используя описание класса:

A>>
A>>   class_("Driver").derived_from<Person>()
A>>   [
A>>       member(&Driver::licence_id, "licence_id"), // note comma operator
A>>       member(&Driver::licence_issue_date, "licence_issue_date")
A>>   ];
A>>


А>Хм ... вообще-то надо бы еще где нибудь и типы членов классов указывать


Зачем? member — это функция, типы аргументов выводятся.

template<class Class, class T>
inline aux::member_leaf<Class,T> member(T Class::* p, const std::string& name)
{
    return aux::member_leaf<Class,T>(p, name);
}


А>... а также их длины ( особенно актуально если член класса это char* )


Не актуально. Есть std::string, std::vector<>...
Re[3]: binder
От: alnsn Великобритания http://nasonov.blogspot.com
Дата: 23.09.03 13:22
Оценка:
Здравствуйте, Аноним, Вы писали:
А>Хм ... вообще-то надо бы еще где нибудь и типы членов классов указывать, а также их длины ( особенно актуально если член класса это char* )
если тип char[N] то размер выводится, правда остается открытым вопрос насчет нуля в конце. char* логично, что это указатель на один char. Даже если я сделаю метод array, который принимает массив на пару с длиной, то непонятно, кто и как этот массив удаляет.

Поясню примером:

struct Person
{
    char first_name[80];
    char* last_name;
    size_t last_name_len;

    ~Person()
    {
        // удалять или нет last_name?
    }
};

class_("Person")
[
    member(&Person::first_name, "first_name"), // длину массива можно узнать
    array(&Person::last_name, &Person::last_name_len, "last_name")
];


Лучше использовать типы с общеизвестной стратегией удаления элементов. std::string или std::vector<char> вместо char*, а auto_ptr или shared_ptr для обычных указателей. Если в библиотечку добавить поддержку этих типов, то она правильно сумеет инициализировать объект при загрузке, а программист не будет думать об удалении в деструкторе.
 
Подождите ...
Wait...
Пока на собственное сообщение не было ответов, его можно удалить.