Версия для печати темы
Нажмите сюда для просмотра этой темы в оригинальном формате
Форум программистов > С/С++: Кроссплатформенное программирование, Qt/Gtk+/wxWidgets > Шаблоны функций


Автор: SABROG 21.4.2008, 21:11
У меня много однотипных параметров, но в принципе эти типы: QString, double, int, unsigned int, bool, unsigned char. Поэтому, чтобы не писать для каждого типа свою реализацию функции я решил воспользоваться шаблонами:

Код

template <class T>
bool Dict::cTemplates::setParam(Tp::tParTypes parType, T)
{
     QString parName;
    switch(parType)
    {
    case Tp::Name:
        parName = "name";      
        break;
        
    };
    parent->pBase->prepare(QString("UPDATE tt SET %1=:par WHERE inc=:inc;").arg(parName));
    parent->pBase->bindValue(QLatin1String(":par"), qVariantFromValue(T));
    parent->pBase->bindValue(QLatin1String(":inc"), inc);
    if (!parent->pBase->exec())
    {
        qDebug(qPrintable(parent->pBase->lastError().text()));
        return false;
    }
    else
    {
        changeDate = QDateTime::currentDateTime();
        return true;
    }
}


Соответственно получаю косяк:

Цитата

src\Dict.cpp:976: error: expected primary-expression before ')' token

(13 строка на форуме в секции кода, где юзается T)

Как бы так исхитрится, чтобы заставить QVariant хавать то что он и будет хавать в принципе, но видимо его пугает, что вместо стандартных типов могут быть кастомы ?
---
Всё, допетрил. Ключевой момент:

Код

QVariant parVal(T);

Автор: SABROG 22.4.2008, 11:46
Нет, и все-таки есть проблема. Не удается преобразовать ни в какой тип данных:

Код

QString name;
name = T;


Получаю: error: expected primary-expression before ';' token

Я уж этот QVariant и через convert(QVariant::String) и через .value<QString>() пытался, ну никак не хочет. Теряется смысл шаблона, я думал компилятор должен закрывать глаза на это и подставлять только в том случае, когда идет непосредственный вызов функции из кода с нужными типами в параметрах.

Автор: pilotnet 22.4.2008, 12:18
Что-то я не допераю 
вы везде пишите 
bool Dict::cTemplates::setParam(Tp::tParTypes parType, T)
parent->pBase->bindValue(QLatin1String(":par"), qVariantFromValue(T));

"Т" - это ведь тип данных а не переменная этого типа 

это должно выглядеть как 
bool Dict::cTemplates::setParam(Tp::tParTypes parType,const T &value)

а потом 
parent->pBase->bindValue(QLatin1String(":par"), qVariantFromValue(value));

или я не о том ?

Автор: SABROG 22.4.2008, 12:21
О том, все правильно. Мой косяк, допетрил уже. Сейчас не могу другое понять, почему компилятор считает, что тип изначально передается как QString ?

Код

template <class T>
bool Dict::cTemplates::setParam(Tp::tParTypes parType, const T &parVal)
...
QString name = parVal; // все ок!
stour *pTour = (stour *)parVal; // косяк! error: cannot convert `parVal' from type `const QString' to type `Dict::stour*'


---
Добавил еще пару функций с разными типами параметров, теперь характер ошибок изменился. Заподло, вот действительно было бы проще воспользоваться Copy->Paste и не заморачиваться насчет дублирующего кода.

Автор: pilotnet 22.4.2008, 12:28
потому что в темплейтах определение типа происходит на стадии компиляции
компилятор генерит код для каждого типа  "T"


Автор: SABROG 22.4.2008, 12:31
Цитата(pilotnet @ 22.4.2008,  12:28)
потому что в темплейтах определение типа происходит на стадии компиляции
компилятор генерит код для каждого типа  "T"

Значит придется возвращаться к тому с чего я начал. Обидно 2 вечера и утро потратил на разбор характера ошибок. Воспользуюсь тогда void * и cast'ами.

Автор: pilotnet 22.4.2008, 12:41
чем не нравиться

Код

bool Dict::cTemplates::setParam(Tp::tParTypes parType, const T &parVal)

QVariant value(parVal);

 parent->pBase->bindValue(QLatin1String(":par"), value );


Автор: SABROG 22.4.2008, 12:48
класс сTemplates имеет все те же члены, что в базе данных, поэтому их надо синхронно изменять:

Например меняя имя (name), надо изменить и запись в БД
Код

name = parVal; // QString name;
...
parent->pBase->bindValue(QLatin1String(":par"), value );


name это QString. Помимо имени есть еще параметры других типов, например ссылка на объект, которая может менятся, но в базе данных она принимает вид ключа с типом unsigned int.

Код

tour = parVal; // stour *tour;
value.setValue(tour->inc);
...
parent->pBase->bindValue(QLatin1String(":par"), value);

---
Изначально идея была такая и видимо к ней придется вернутся:

Код

bool Dict::cTemplates::setName(const QString &newtitle)
{
   parent->pBase->prepare(QLatin1String("UPDATE tt SET name=:name WHERE inc=:inc;"));
   parent->pBase->bindValue(QLatin1String(":name"), newtitle);
   parent->pBase->bindValue(QLatin1String(":inc"), inc);
   if (!parent->pBase->exec())
   {
       qDebug(qPrintable(parent->pBase->lastError().text()));
       return false;
   }
   else
   {
       name = newtitle;
       changeDate = QDateTime::currentDateTime();
       return true;
   }
}
bool Dict::cTemplates::setNote(const QString &newnote)
{
    parent->pBase->prepare(QLatin1String("UPDATE tt SET note=:note WHERE inc=:inc;"));
    parent->pBase->bindValue(QLatin1String(":note"), newnote);
    parent->pBase->bindValue(QLatin1String(":inc"), inc);
    if (!parent->pBase->exec())
    {
        qDebug(qPrintable(parent->pBase->lastError().text()));
        return false;
    }
    else
    {
        note = newnote;
        changeDate = QDateTime::currentDateTime();
        return true;
    }    
}
bool Dict::cTemplates::setCom(double newcom)
{
    parent->pBase->prepare(QLatin1String("UPDATE tt SET comission=:comission WHERE inc=:inc;"));
    parent->pBase->bindValue(QLatin1String(":comission"), newcom);
    parent->pBase->bindValue(QLatin1String(":inc"), inc);
    if (!parent->pBase->exec())
    {
        qDebug(qPrintable(parent->pBase->lastError().text()));
        return false;
    }
    else
    {
        Com = newcom;
        changeDate = QDateTime::currentDateTime();
        return true;
    }  
}



Я бы не мог ЭТО назвать хорошим стилем программирования...

Автор: pilotnet 22.4.2008, 13:03
дак тогда и передавай в эту функцию 
уже (unsigned int)tour->inc

все равно тебе что передовать надо разбираться самому
в зависимости от типа

Автор: SABROG 22.4.2008, 13:16
Выбросил шаблон и сделал так:

Код

bool Dict::cTemplates::setName(const QString &newtitle)
{
    if (setParam(Tp::Name, newtitle))
    {
        name = newtitle;
    }
}

bool Dict::cTemplates::setParam(Tp::tParTypes parType, const QVariant &parVal)
{
    QString parName;
    switch(parType)
    {
    case Tp::Name:
        parName = "name";
        break;
    case Tp::Note:
        parName = "note";
        break;
    case Tp::Tour:
        parName = "tour";
        break;
    case Tp::Country:
        parName = "country";
        break;
    case Tp::Operator:
        parName = "operator";
        break;
    case Tp::Postfix:
        parName = "postfix";
        break;
    case Tp::Owner:
        parName = "owner";
        break;
    case Tp::OpComission:
        parName = "opcomission";
        break;
    case Tp::Comission:
        parName = "comission";
        break;
    case Tp::Currency:
        parName = "currency";
        break;
    case Tp::SposNote:
        parName = "sposnote";
        break;
    case Tp::SpogNote:
        parName = "spognote";
        break;
    case Tp::CatNote:
        parName = "catnote";
        break;
    case Tp::SpoSort:
        parName = "sposort";
        break;
    case Tp::XmlEngine:
        parName = "xmlengine";
        break;
    case Tp::Type:
        parName = "type";
        break;
    case Tp::tSort:
        parName = "sort";
        break;
    };
    parent->pBase->prepare(QString("UPDATE tt SET %1=:par WHERE inc=:inc;").arg(parName));
    parent->pBase->bindValue(QLatin1String(":par"), parVal);
    parent->pBase->bindValue(QLatin1String(":inc"), inc);
    if (!parent->pBase->exec())
    {
        qDebug(qPrintable(parent->pBase->lastError().text()));
        return false;
    }
    else
    {
        changeDate = QDateTime::currentDateTime();
        return true;
    }
}

Автор: Lazin 22.4.2008, 14:46
не знаю что такое QVariant, но мне кажется что boost::variant is more suitable 

Код

bool Dict::cTemplates::setParam(boost::variant & v)
{
  struct helper_t : : public boost::static_visitor<>
  {
     void operator (QString & str) {..здесь.сохраняешь.в.бд..}
     void operator (double f) {..и.здесь..}
      ......
  } helper;
  boost::apply_visitor(helper, v);
}

Автор: SABROG 22.4.2008, 15:27
С бустом у меня что-то никак не склеивается, не хочет под mingw собирать. Я так понимаю это паттерн ?
Так сразу и не скажешь как он работает.

Автор: Lazin 22.4.2008, 15:39
boost::apply_visitor вызывает одну из перегруженных версий оператора () функтора helper, в зависимости от типа своего содержимого
если там строка, то вызовется
void operator (QString & str) 
если int, то 
void operator (int)

Автор: SABROG 22.4.2008, 16:18
А чем же это отличается от обычной перегрузки параметров ?

К тому же у меня параметры хоть и имеют разные типы, но эти типы могут быть общими для двух разных параметров.

Автор: Lazin 22.4.2008, 16:39
Цитата(SABROG @  22.4.2008,  16:18 Найти цитируемый пост)
А чем же это отличается от обычной перегрузки параметров ?

тем что тебе не нужно проверять вручную, что у тебя в variant переменной, будет вызвана правильная ф-я

Цитата(SABROG @  22.4.2008,  16:18 Найти цитируемый пост)
К тому же у меня параметры хоть и имеют разные типы, но эти типы могут быть общими для двух разных параметров. 

я это должен был по положению звезд определить  smile 

Автор: SABROG 22.4.2008, 17:33
У меня сейчас идеологическая проблема возникла. По сути сейчас я пишу основное ядро программы. В прошлой версии при занесении или удалении данных из базы данных создавался диалог с прогрессбаром. Вот сейчас пытаюсь решить, если уж я пишу ядро, то каким боком мне сдался этот GUI внутри класса ? Врятли кто бы стал пихать в синглтон-класс гуевые окошечки. Походу надо передавать какую-нибудь callBack функцию или эмитить сигналы.

Автор: JackYF 23.4.2008, 00:01
SABROG, да, было такое. Я emittил сигналы в этом случае.

Автор: SABROG 23.4.2008, 11:12
Цитата(JackYF @ 23.4.2008,  00:01)
SABROG, да, было такое. Я emittил сигналы в этом случае.

А для этого необходимо чтобы класс был на базе QObject'a ?

У меня такая задумка. Я не хочу использовать processAllEvents, т.к. у меня имеется скажем 50 операций, который могут выполнятся по 2-3 минуты каждая, я сделал прогрессбар, но естественно даже с процессоллевентс все висит.

Вот теперь думаю как сделать так, чтобы из ядра программы сэмитить сигнал, который бы не только создал поток и прогрессбар, но и увеличивал при этом счетчик на прогрессе O.o

Автор: Любитель 23.4.2008, 12:08
Наследовать от QObject надо. В чём проблема-то? Реализуешь негуёвую модель твоего пргресса (данные о прогрессе + сигналы для уведомления об его изменении). Затем при коннектишь гуй.

Автор: SABROG 23.4.2008, 13:45
Мне необходим такой функционал из негуишного класса:

- возможность менять ход прогресса
- отменять прогресс
- перехватывать отмену прогресса (юзер кликнул Cancel или крест)

Это все мне может дать такая модель. Я создаю класс на базе QThread, куда пихаю QProgressDialog. Сам этот класс (QThread) создаю внутри негуишного класса, коннекчусь. Но в итоге получается что негуишный класс жестко привязан к GUI так или иначе. Но видать никуда от этого не деться, т.к. метод приватный и передать ссылку на объект QThread извне не получится.

Автор: Любитель 23.4.2008, 14:13
Ты создаёшь без всякого гуя класс, у которого есть:
  • данные о пргрессе
  • сигнал о изменении этих данных
  • сигналы начала и окончания прогресса (опционально)
  • слот отмены действия

Этот же класс производит сами действия. Или же он универсальный абстрактный с чисто виртуальным методом для действия + наследник для этой ситуации (а в будущем ещё для сотни).

С гую коннектишься к началу пргресса - создаёшь диалог. На изменение меняешь пргресс-бар. На клик по канселу коннектишь слот отмены.

Мешать гуй и бизнес-логику - ни в коем случае!

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