Версия для печати темы
Нажмите сюда для просмотра этой темы в оригинальном формате
Форум программистов > C/C++: Для новичков > Smart pointer и meta-properties


Автор: xRaux 16.2.2012, 20:15
Задача следующая. Нужно реализовать класс, который будет выступать в роли умного указателя: операторы * и -> отложенно создают объект, который инициализируется значениями из базы данных. Проблема в том, что этот класс должен использоваться вместе с мета-свойствами Qt, которые не позволяют полноценно работать с шаблонами. У меня родился такой вариант:

Код

class MappedObject : public QObject
{
public:
    void load();
};

class MappedObjectPtrBase
{
public:
    virtual MappedObject* operator*() { return 0; }
};

Q_DECLARE_METATYPE(MappedObjectPtrBase*)

template<typename T>
class MappedObjectPtr
{
public:
    MappedObject* operator*()
    {
        if (!pMappedObject)
        {
            pMappedObject = new T();
            pMappedObject->load();
        }
        return pMappedObject;
    }
    
private:
    T* pMappedObject;
};

class SomeMappedObject : public MappedObject
{
    Q_OBJECT
    Q_PROPERTY(MappedObjectPtrBase* someProp READ getSomeProp WRITE setSomeProp)
public:
    MappedObjectPtrBase* getSomeProp()const { return somePropValue; }
    void setSomeProp(MappedObjectPtrBase* p) { somePropValue = p; }
    
protected:
    MappedObjectPtrBase* somePropValue;
};


Но он меня сильно не устраивает.
1. SomeMappedObject (как и любой другой потомок MappedObject) вынужден использовать голые указатели со всеми вытекающими - создавать объект в конструкторе, удалять в деструкторе.
2. Нет контроля типов для someProp. Хотелось бы писать что-то вроде Q_PROPERTY(MappedObjectPtr<SomeMappedObject> propName ...)
Короче говоря, запутался я совсем. Может быть, возможно реализовать какой-нибудь другой, более приемлемый вариант? Заранее спасибо.

Автор: bsa 17.2.2012, 10:35
xRaux, а зачем тебе это нужно?

Автор: xRaux 17.2.2012, 10:37
Что именно, сорри?

Автор: bsa 17.2.2012, 10:40
Цитата(xRaux @  17.2.2012,  11:37 Найти цитируемый пост)
Что именно, сорри? 

Цитата(xRaux @  16.2.2012,  21:15 Найти цитируемый пост)
Нужно реализовать класс, который будет выступать в роли умного указателя: операторы * и -> отложенно создают объект, который инициализируется значениями из базы данных.


Зачем тебе нужен такой класс? Просто есть подозрение, что ты пытаешься решить основную задачу не теми методами. Поэтому залез в такие дебри. Если ты озвучишь основную задачу, то тебе подскажут оптимальное решение.

Автор: xRaux 17.2.2012, 11:28
Это нечто вроде ORM - данные из базы мэппируются на объекты через свойства. Проблема в поддержке foreign keys - при мэппинге из такой, например, таблицы:
Код

CREATE TABLE book(id INTEGER PRIMARY KEY, title TEXT, author_id INTEGER, FOREIGN KEY(author_id) REFERENCES author(id));
CREATE TABLE author(id INTEGER PRIMARY KEY, name TEXT);

на класс
Код

class Book : public MappedObject
{
public:
    QString title;
    Author* author;
};

у меня возникают проблемы с полем author. Как его мэппировать? Хотелось бы, чтоб объект Author инициализировался не сразу при инициализации Book, а только при необходимости, то есть обращении к нему. Вот у меня и появилась мысль о том, что нужно сделать умный указатель.

Автор: borisbn 17.2.2012, 12:35
Цитата(xRaux @  17.2.2012,  11:28 Найти цитируемый пост)
Проблема в поддержке foreign keys - при мэппинге из такой, например, таблицы

дык делай SELECT с LEFT JOIN'ом. нет ?

Автор: xRaux 17.2.2012, 13:45
Цитата(borisbn @  17.2.2012,  12:35 Найти цитируемый пост)
дык делай SELECT с LEFT JOIN'ом. нет ?

База данных может иметь довольно сложную структуру +связи один ко многим. Имхо без отложенной выборки здесь все равно не обойтись. Или я что-то не понимаю?

Автор: bsa 17.2.2012, 13:50
xRaux, помести все свойства класса в приватную область. А доступ к ним через методы (как собственно в Qt сделано). При вызове метода свойства будут при необходимости инициализироваться и отдаваться пользователю.

Автор: xRaux 17.2.2012, 15:25
bsa, то есть примерно так?
Код

class Book : public MappedObject
{
    Q_PROPERTY(Author* author READ getAuthor WRITE setAuthor)
    
    std::shared_ptr<Author> valueAuthor;
public:
    Author* getAuthor()const
    {
        if (!valueAuthor)
        {
            valueAuthor.reset(new ValueAuthor());
            valueAuthor->load();
        }
        return valueAuthor.get();
    }
};

Нужно еще где-то хранить информацию об объекте Author, который должен быть создан (в этом примере - author_id). Это можно реализовать в защищенном интерфейсе MappedObject. Но опять же мне не нравится, что свойство отдает неизвестно кому голый указатель. Смириться?

Автор: mes 17.2.2012, 15:34
xRaux, не могу до конца вашу затею понять.. 
для чего наследование от MappedObject ? 

Автор: xRaux 17.2.2012, 15:52
Цитата(mes @  17.2.2012,  15:34 Найти цитируемый пост)
для чего наследование от MappedObject ? 

Можно аналогичный механизм реализовать и без наследования, суть не в этом. Просто так удобнее - можно предоставить методы типа load/save/create для синхронизации с базой данных. Практически аналогичный кодес есть, например, в QDjango. Правда, она для моих целей не совсем подходит, да и не нужна мне полноценная ORM. Плюс, реализованный там подход к сабжу, насколько я понял - те же голые указатели с ручной аллокацией-деаллокацией. Я хотел чуть-чуть это усовершенствовать.

Автор: bsa 17.2.2012, 16:23
Код
class Book : public MappedObject
{
public:
    Book() : author_(0) {}
    ~Book() {
        delete author_;
    }
    Author& author() {
        if (!author_) {
            author_= new Author;
            author_->load(); //а в конструктор никак нельзя это поместить?
        }
        return *author_;
    }
private:
    Author *author_;
};

Powered by Invision Power Board (http://www.invisionboard.com)
© Invision Power Services (http://www.invisionpower.com)