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


Автор: o2n3e 19.8.2011, 17:45
Здравствуйте! Я полный новичок в программировании, вернее в практической его части(теоретические знания имеются). Появилась возможность попрактиковаться, и тут же возникла загвоздка. 
Проблему решил сам, но решение мне не нравится, поэтому прощу направить на путь истинный.

Задача:
У нас есть http://ru.wikipedia.org/wiki/G-code. Они состоят из кадров, кадр - одна строка. В начале кадра указывается команда, потом параметры. 
Нужно хранить их в памяти программы для работы с ними, т.е. что-то типа массива классов, где класс - это один кадр.
Варианты входящих параметров:
g0 - это X или Y;
g1 - это X или Y или Z или F, возможны любые комбинации из x,y,f;
g2 - это x+y+i+j+f?;
g3 - то же самое, что и g2;
g4 - это x;
g90 и g91 без параметров.

Вот мой вариант:
Код

#ifndef GCODE_H
#define GCODE_H
#include <QFlags>
class Gcode
{

public:
    enum GcodeNumberFlag {
        G0x = 0x8074,
        G0y = 0x2074,
        G0xy = 0xA074,

        G1x = 0x8075,
        G1y = 0x4075,
        G1z = 0x007E,
        G1xy = 0xC075,
        G1xf = 0xA075,
        G1yf = 0x6075,
        G1xyf = 0xE075,

        G2 = 0xF076,
        G3 = 0xF078,
        G2f = 0xF876,
        G3f = 0xF878,

        G4 = 0x007A,
        G90 = 0x007B,
        G91 = 0x007C
    };
    Q_DECLARE_FLAGS(GcodeNumber, GcodeNumberFlag)
    
    Gcode();
    Gcode(GcodeNumber &gNumber) {
        this->GcodeNum = gNumber;
    }
    Gcode(GcodeNumber gNumber, qreal &pxyz);
    Gcode(GcodeNumber gNumber, qreal &px, qreal &py);
    Gcode(GcodeNumber gNumber, qreal &px, qreal &py, qreal &pf);
    Gcode(GcodeNumber gNumber, qreal &px, qreal &py, qreal &pi, qreal &pj);
    Gcode(GcodeNumber gNumber, qreal &px, qreal &py, qreal &pi, qreal &pj, qreal &pf);
    Gcode(const Gcode &other);
    virtual ~Gcode();
private:
    qreal px;
    qreal py;
    qreal pz;
    qreal pf;
    qreal pi;
    qreal pj;
    GcodeNumber GcodeNum;
};

Q_DECLARE_OPERATORS_FOR_FLAGS(Gcode::GcodeNumber)
#endif // GCODE_H

http://pastebin.com/6G2j9FC4

Класс должен знать какая команда в него записана, и исходя из этого выполнять методы. т.е. если есть метод toString, то он должен возвращать x, если он g0\1x, либо xy, если он g0\1xy.
Мне кажется, что можно сделать всё намного проще, но я не знаю как. От Qt тут только флаги, поэтому не стал писать в Qt раздел

Автор: bsa 19.8.2011, 17:52
Для этого существуют виртуальные методы. Делаешь абстрактный базовый класс "кадр" с чисто виртуальным методом toString(), а все остальные делаешь его наследниками и определяешь этот самый метод.

Автор: o2n3e 19.8.2011, 20:57
Если делать классы типа G0, G1, etc..., то всё ровно надо будет городить огород в методах.
Если делать все возможные варианты наследниками - это будет бида, т.е. будет over10 разных классов.

Что-то типа такого?
Код

class Gslice{
public:
  virtual QString toString() = 0;
  
};
class G0 : Gslice {
public:
  inline G0(qreal x, qreal y){this->x = x; this->y = y;}
  inline QString toString(){QString out; out.push_back(QString().setNum(x)); out.push_back(QString().setNum(y)); return out;}
private:
  qreal x,y;
};


По мне так проще сделать:
Код

  QString toString(){
    QString out;
  if(px)
    out.append(QString().setNum(px));
  if(py)
    out.append(QString().setNum(py));
  if(pz)
    out.append(QString().setNum(pz));
  if(pf)
    out.append(QString().setNum(pf));
  if(pi)
    out.append(QString().setNum(pi));
  if(pi)
    out.append(QString().setNum(pj));
  return out;
  }


Объясните пожалуйста, в чем смысл?
Так же, я задавал вопрос в основном именно про конструкторы. Можно ли избавиться от этих монстрозаумных громадин?

Автор: alexvs11 19.8.2011, 22:06
можно замутить чтото вроде

Код

#include <QtCore/QCoreApplication>
#include <QVector>
#include <QVariant>
#include <cassert>
#include <QDebug>

typedef QVector<QVariant> QVecVariant;

enum GcodeNumberFlag
{
    G0x  = 0x8074,
    G0y  = 0x2074,
    G0xy = 0xA074
};

class GBase
{
public:
    virtual QString toString() = 0;
    virtual GcodeNumberFlag getType() = 0;
protected:
    qreal px;
    qreal py;
    qreal pz;
    qreal pf;
    qreal pi;
    qreal pj;
};

template<const GcodeNumberFlag flag>
class G : public GBase
{
public:
    G(const QVecVariant&);
    GcodeNumberFlag getType(){ return flag; }
    QString toString();
};

template<> G<G0x>::G(const QVecVariant &command)
{
    px = command[0].toReal();
}

template<> QString G<G0x>::toString()
{
    return QString("%1").arg(px);
}

template<> G<G0y>::G(const QVecVariant &command)
{
    px = command[0].toReal();
    py = command[1].toReal();
}

template<> QString G<G0y>::toString()
{
    return QString("%1 %2").arg(px).arg(py);
}


int main(int argc, char *argv[])
{
    QCoreApplication a(argc, argv);

    QVecVariant params_g1, params_g2;
    params_g1 << 12.3;
    params_g2 << 131.2 << 123.2;

    QVector<GBase*> v;
    v.push_back( new G<G0x>(params_g1) );
    v.push_back( new G<G0y>(params_g2) );

    GBase* ptr;
    foreach( ptr, v )
    {
        qDebug() << ptr->getType() << ' ' << ptr->toString();
        delete ptr;
    }

    return a.exec();
}


Автор: bsa 20.8.2011, 23:09
Цитата(o2n3e @  19.8.2011,  20:57 Найти цитируемый пост)
out.append(QString().setNum(px));

Во-первых, лучше делать так: out += QString::number(px); //имхо, короче и быстрей работает.
Во-вторых, а ты что думал? Если хочешь, чтобы у объектов были одни рычаги управления, но разное поведение, то делай через виртуальные методы. Все остальное будет работать медленней.
В-третьих, сделав так, у тебя получится, что у каждого класса только один конструктор.

Автор: o2n3e 21.8.2011, 02:15
Спасибо, исправил. 

Допустим, если взять пример тов. alexvs11, то у нас получится 17 разных вариантов каждого метода. 
В чём профит от щаблонов? Имхо, так быстрее, понятней и меньше код"а.
Код

#include <QtCore/QCoreApplication>
#include <QVector>
#include <QVariant>
#include <cassert>
#include <QDebug>
typedef QVector<QVariant> QVecVariant;
enum GcodeNumberFlag
{
    G0x  = 0x8074,
    G0y  = 0x2074,
    G0xy = 0xA074
};
class GBase
{
public:
    virtual QString toString() = 0;
    virtual GcodeNumberFlag getType() = 0;
protected:
    qreal px;
    qreal py;
    qreal pz;
    qreal pf;
    qreal pi;
    qreal pj;
    GcodeNumberFlag flag;
};
class G : public GBase
{
public:
    G(const GcodeNumberFlag&, const QVecVariant&);
    GcodeNumberFlag getType(){ return flag; }
    QString toString();
};
G::G(const GcodeNumberFlag& flag, const QVecVariant& command)
{
  this->flag = flag;
  switch(flag){
    case G0x :  px = command[0].toReal(); break;
    case G0y :  py = command[1].toReal(); break;
    case G0xy : px = command[0].toReal(); py = command[1].toReal(); break;
  }
}

QString G::toString()
{
  switch(flag){
    case G0x :  return QString("%1").arg(px); break;
    case G0y :  return QString("%1").arg(py); break;
    case G0xy : return QString("%1 %2").arg(px).arg(py); break;
  }
}

int main(int argc, char *argv[])
{
    QCoreApplication a(argc, argv);
    QVecVariant params_g1, params_g2;
    params_g1 << 12.3;
    params_g2 << 131.2 << 123.2;
    QVector<GBase*> v;
    v.push_back( new G(G0x, params_g1) );
    v.push_back( new G(G0xy, params_g2) );
    GBase* ptr;
    foreach( ptr, v )
    {
        qDebug() << ptr->getType() << ' ' << ptr->toString();
        delete ptr;
    }
    return a.exec();
}


Но мне это как-то не нравится, хотя и лучше моего варианта. Ведь можно как-то сделать проще. Сижу ломаю голову...

Автор: mes 21.8.2011, 11:06
Цитата(o2n3e @  19.8.2011,  16:45 Найти цитируемый пост)
 В начале кадра указывается команда, потом параметры. 

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

Добавлено через 2 минуты и 35 секунд
Цитата(o2n3e @  19.8.2011,  16:45 Найти цитируемый пост)
ласс должен знать какая команда в него записана, и исходя из этого выполнять методы. т.е. если есть метод toString, то он должен возвращать x, если он g0\1x, либо xy, если он g0\1xy.

смысла не понял

Добавлено через 6 минут и 14 секунд
Цитата(o2n3e @  21.8.2011,  01:15 Найти цитируемый пост)
 Имхо, так быстрее, понятней и меньше код"а.

если смотреть на код, то вы хотите сделать не процессор/обработчик г-кода,  а наоборот создать удобный интерфейс по вызову команд.. 

Автор: mes 21.8.2011, 11:33
Цитата(o2n3e @  19.8.2011,  16:45 Найти цитируемый пост)
qreal &px, qreal &py, qreal &pf);

аргументы могут быть только одного типа ?

Добавлено через 2 минуты и 35 секунд
т.е. как я понял Вам надо преобразовать одно или несколько аргументов в строку в зависимости от кода ..
тогда вопрос Вам нужен жесткий статический контроль на соблюдение соответсвия кода передаваемым аргументам ?

Добавлено через 8 минут и 29 секунд
Цитата(mes @  21.8.2011,  10:33 Найти цитируемый пост)
с Вам нужен жесткий статический контроль

в этом случае все сводится к написанию функции для каждого кода.. 
т.е. 
Код

std::string g0 (double x, double y) { return .. }
std::string g1 (double x) { return .. }
std::string g90 () { return .. }

Автор: mes 21.8.2011, 12:01
если ж достаточно динамического контроля, то можно так :
http://liveworkspace.org/code/1d33a0600475b74e75b1703ee0504d47

это правда с использованием C++0x, но можно и вручную получить примерное.. 


Автор: o2n3e 21.8.2011, 15:33
Цитата(mes @  21.8.2011,  11:06 Найти цитируемый пост)
покажите пару примеров строкового вида этих кадров .. 
аргументы в сторке разделены однообразно ? независимо от кода команды ?
вам нужно сделать обработчики этого кадра, где для каждого кода свой обработчик ?

Cтроки вида:
G0X500.500Y500.500
G3X500.500Y500.500I500.500J500.500F500.500
Не совсем, нужно сделать что-то типа контейнера для кадров, и интерфейс к нему. 
Кадров может быть больше тысячи в одной программе. 

Архитектура программы: Я беру и парсю входящую строку, и передаю получившиеся значения в конструктор класса. Класс добавляется, допустим, в вектор базового класса. И потом уже работаю с кадрами, путём перебора вектора.
Как в примере тов. Alexvs11.
Допустим, запись в файл всех слайсов:
Код

foreach( Gclass, SliceVector )
    out << Gclass.toOfFileString;

Так же со смешением, масштабирование, интерполяцией.

Цитата(mes @  21.8.2011,  11:06 Найти цитируемый пост)
если смотреть на код, то вы хотите сделать не процессор/обработчик г-кода,  а наоборот создать удобный интерфейс по вызову команд.. 

В яблочко. Я хочу сделать удобное хранилище/обработчик этих самых кодов. С удобным интерфейсом.

Цитата(mes @  21.8.2011,  11:33 Найти цитируемый пост)
т.е. как я понял Вам надо преобразовать одно или несколько аргументов в строку в зависимости от кода ..
тогда вопрос Вам нужен жесткий статический контроль на соблюдение соответсвия кода передаваемым аргументам ?

Да. Код кадра указывает на количество аргументов, аргументы всегда имеют одну и туже последовательность, что на входе, что на выходе. Поэтому, если на конструктор приходит: float first, float second и код кадра G0XY, то first - это 100% X, а second - это 100% Y.

Автор: mes 21.8.2011, 17:09
Цитата(o2n3e @  21.8.2011,  14:33 Найти цитируемый пост)
 Класс добавляется, допустим, в вектор базового класса. И потом уже работаю с кадрами, путём перебора вектора.

с вектором понятно.. если можно удобно  работать с фреймом отдельно, то и с их массивом проблем не составит smile

Цитата(o2n3e @  21.8.2011,  14:33 Найти цитируемый пост)
Архитектура программы: Я беру и парсю входящую строку, и передаю получившиеся значения в конструктор класса.

значит статический контроль не нужен, так как на вход попадает уже в сторковом виде.. 

т.е. надо строку преобразовать к "код + аргументы" и иметь возможность преобразовать это обратно к строке..  тут понятно.. 

теперь вопрос по аргументам, их я как понимаю фиксированное кол-во, при этом какие аргументы будут переданы задается кодом ,
плюс у каждого имени аргумента есть очередность.. 
Цитата

 код кадра G0XY, то first - это 100% X, а second - это 100% Y.

т.е. как говорите Y может быть на первом месте только если нет Х.. так ?

Добавлено через 5 минут и 25 секунд
Цитата(o2n3e @  19.8.2011,  16:45 Найти цитируемый пост)
       G0x = 0x8074,
        G0y = 0x2074,
        G0xy = 0xA074,

если присмотреться к значением , то видно что аргументы представлены битовыми значениями.. 
т.е. правильней не делать перечисление всех вариантов команд, а разбить на составляющие. 
тогда G0xy = gcode::make_cmd(gcode::g0, gcode::x | gcode::y);

Добавлено через 7 минут
Цитата(o2n3e @  21.8.2011,  01:15 Найти цитируемый пост)
 qreal px;
    qreal py;
    qreal pz;
    qreal pf;
    qreal pi;
    qreal pj;

угу, кол-во вариантов аргуметов получается заранее известно..

Добавлено через 9 минут и 50 секунд
теперь как этот фрейм предполагается использовать ?  имеется ввиду обработка команд, а не превращение его опять  в строку.. 

Автор: mes 21.8.2011, 17:27
пока из сказанного видится такая структура самого фрейма:
Код

struct frame_t
{
   size_t code;

   struct flags_t
   {
     bool x :1;
     bool y :1; 
     bool z :1;
     bool f :1;
     bool i :1;
     bool j :1;     
   } flags;

   struct args_t
   {
     double x;
     double y; 
     double z;
     double f ;
     double i;
     double j;    
   } args;
};


флаги отделены от аргументов для экономии памяти..

Добавлено @ 17:35
Цитата(o2n3e @  21.8.2011,  14:33 Найти цитируемый пост)
G3X500.500Y500.500I500.500J500.500F500.500

парсер : читает команду (G3) ищет соотвествующее ей значение в карте и устанавливает код фрейма
после в цикле читает имя аргумента и его значение и устанавливает соответсвующий флаг и переменную.. 
тогда для удобной работы нам нужен mapper как посредник между строковым видом и данными..
Код

struct mapper {
  void set_code  (frame& f, std::string const& code);
  void set_value (frame& f, std::string const& val_name, double val);  
}


Добавлено через 14 минут и 59 секунд
Цитата(mes @  21.8.2011,  16:27 Найти цитируемый пост)
пока из сказанного видится такая структура самого фрейма:

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

Автор: mes 21.8.2011, 17:52
помимо этого нужен еще читатель (из строки или потока)
Код

struct reader_t {
   std::string get_code (..);
   std::string get_val_name (..);
   double get_value(..);
};

тогда парсер условно будет выглядить так :
Код

struct parser_t
{
   frame parse (..)
   {
        frame f;
        mapper.set_code (f, reader.get_code(..));

        while (.. != eof) 
           mapper.set_value (f, reader.get_val_name(..), reader.get_value());
    
       return f; 
   }

private:
   reader_t reader;
   mapper_t mapper;
};


или даже так :
Код

   frame parse (.., reader_t const&  reader, mapper const& mapper)
   {
        frame f;
        mapper.set_code (f, reader.get_code(..));

        while (.. != eof) 
           mapper.set_value (f, reader.get_val_name(..), reader.get_value());
    
       return f; 
   }
// естестевенно методы мапперы и ридера должны быть помечены как константные.. 


Добавлено @ 18:04
Цитата(mes @  21.8.2011,  16:09 Найти цитируемый пост)
если присмотреться к значением , то видно что аргументы представлены битовыми значениями..

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

Автор: mes 21.8.2011, 18:08
сейчас сдеривающим является ответ на этот вопрос :
Цитата(mes @  21.8.2011,  16:09 Найти цитируемый пост)
теперь как этот фрейм предполагается использовать ? 


Добавлено через 8 минут и 12 секунд
а также интересны ограничения на компилятор .. можно ли использовать C++0x ?

Добавлено через 12 минут и 8 секунд
Цитата(o2n3e @  19.8.2011,  16:45 Найти цитируемый пост)
        G1y = 0x4075,
        G1z = 0x007E,
        G1xy = 0xC075

тут точно опечатки нет ?

Автор: o2n3e 21.8.2011, 18:46
Числа в енуме вообще не имеют смысла.
Цитата(mes @  21.8.2011,  17:09 Найти цитируемый пост)
теперь как этот фрейм предполагается использовать ?  имеется ввиду обработка команд, а не превращение его опять  в строку.. 

Исключая текстовые операции и редактирование, Одна из самых сложных задач - это нарисовать этот самый г-код. Но пока до этого я не добрался.

Добавлено через 5 минут и 24 секунды
Цитата(mes @  21.8.2011,  18:08 Найти цитируемый пост)
а также интересны ограничения на компилятор .. можно ли использовать C++0x ?

Ограничение на тулкиты и компилятор нет, компилятор используется gcc-4.5.2.


Цитата(mes @  21.8.2011,  18:08 Найти цитируемый пост)
тут точно опечатки нет ?

Эти константы взяты из протокола обмена с контроллером, с которым общается данная программа по com-порту. Никакой смысловой нагрузки они не несут, кроме того, что их нужно использовать для работы с контролером.
Допустим кадр G1X500.500Y500.500, в формате, который понимает контроллер, выглядит так: C07500870007A31400870007A314.

Автор: mes 21.8.2011, 18:52
Цитата(o2n3e @  21.8.2011,  17:46 Найти цитируемый пост)
Числа в енуме вообще не имеют смысла.

понятно.. т.е привод в машинозависимый вид нас не интересует..

Добавлено @ 18:59
Цитата(o2n3e @  21.8.2011,  17:46 Найти цитируемый пост)
 Одна из самых сложных задач - это нарисовать этот самый г-код.

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

Автор: mes 21.8.2011, 19:33
подумав, все таки пришел к выводу что метод обработчик должен получать полный фрейм, 
тогда пример с использованием может выглядить так :
http://liveworkspace.org/code/383ff2f954f6810b910a64c7564c4e30

Автор: mes 21.8.2011, 19:56
чтоб не загружать рантайм, так как связи с процессором постоянные, можно определить карту как статическую переменную...
http://liveworkspace.org/code/8a0836e340090fb7f217bdfa8bd1e2e5
(для не C++0x нужно добавить статическую стурктуру-инициализер)

Автор: o2n3e 21.8.2011, 20:12
тов. Mes, спасибо вам большое.

Пытаюсь разобраться в вашем коде.

Автор: mes 21.8.2011, 23:09
http://liveworkspace.org/code/7cf298a446aad6a67669a66b44a8c080

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