Модераторы: bsa

Поиск:

Ответ в темуСоздание новой темы Создание опроса
> Конструкторы преобразования 
:(
    Опции темы
o2n3e
Дата 19.8.2011, 17:45 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Новичок



Профиль
Группа: Участник
Сообщений: 10
Регистрация: 19.8.2011

Репутация: нет
Всего: -5



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

Задача:
У нас есть 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

cpp часть

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

PM MAIL   Вверх
bsa
Дата 19.8.2011, 17:52 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Эксперт
****


Профиль
Группа: Модератор
Сообщений: 9185
Регистрация: 6.4.2006
Где: Москва, Россия

Репутация: 85
Всего: 196



Для этого существуют виртуальные методы. Делаешь абстрактный базовый класс "кадр" с чисто виртуальным методом toString(), а все остальные делаешь его наследниками и определяешь этот самый метод.
PM   Вверх
o2n3e
Дата 19.8.2011, 20:57 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Новичок



Профиль
Группа: Участник
Сообщений: 10
Регистрация: 19.8.2011

Репутация: нет
Всего: -5



Если делать классы типа 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;
  }


Объясните пожалуйста, в чем смысл?
Так же, я задавал вопрос в основном именно про конструкторы. Можно ли избавиться от этих монстрозаумных громадин?
PM MAIL   Вверх
alexvs11
Дата 19.8.2011, 22:06 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


hell is here
**


Профиль
Группа: Участник
Сообщений: 518
Регистрация: 21.8.2010

Репутация: 2
Всего: 10



можно замутить чтото вроде

Код

#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();
}



Это сообщение отредактировал(а) alexvs11 - 19.8.2011, 22:07
PM MAIL   Вверх
bsa
Дата 20.8.2011, 23:09 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Эксперт
****


Профиль
Группа: Модератор
Сообщений: 9185
Регистрация: 6.4.2006
Где: Москва, Россия

Репутация: 85
Всего: 196



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

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


Новичок



Профиль
Группа: Участник
Сообщений: 10
Регистрация: 19.8.2011

Репутация: нет
Всего: -5



Спасибо, исправил. 

Допустим, если взять пример тов. 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();
}


Но мне это как-то не нравится, хотя и лучше моего варианта. Ведь можно как-то сделать проще. Сижу ломаю голову...
PM MAIL   Вверх
mes
Дата 21.8.2011, 11:06 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


любитель
****


Профиль
Группа: Участник Клуба
Сообщений: 7954
Регистрация: 14.1.2006

Репутация: 79
Всего: 250



Цитата(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 Найти цитируемый пост)
 Имхо, так быстрее, понятней и меньше код"а.

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



--------------------
PM MAIL WWW   Вверх
mes
Дата 21.8.2011, 11:33 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


любитель
****


Профиль
Группа: Участник Клуба
Сообщений: 7954
Регистрация: 14.1.2006

Репутация: 79
Всего: 250



Цитата(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 .. }



--------------------
PM MAIL WWW   Вверх
mes
Дата 21.8.2011, 12:01 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


любитель
****


Профиль
Группа: Участник Клуба
Сообщений: 7954
Регистрация: 14.1.2006

Репутация: 79
Всего: 250



если ж достаточно динамического контроля, то можно так :
http://liveworkspace.org/code/1d33a0600475...5b1703ee0504d47

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




--------------------
PM MAIL WWW   Вверх
o2n3e
Дата 21.8.2011, 15:33 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Новичок



Профиль
Группа: Участник
Сообщений: 10
Регистрация: 19.8.2011

Репутация: нет
Всего: -5



Цитата(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.
PM MAIL   Вверх
mes
Дата 21.8.2011, 17:09 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


любитель
****


Профиль
Группа: Участник Клуба
Сообщений: 7954
Регистрация: 14.1.2006

Репутация: 79
Всего: 250



Цитата(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:09


--------------------
PM MAIL WWW   Вверх
mes
Дата 21.8.2011, 17:27 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


любитель
****


Профиль
Группа: Участник Клуба
Сообщений: 7954
Регистрация: 14.1.2006

Репутация: 79
Всего: 250



пока из сказанного видится такая структура самого фрейма:
Код

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:39


--------------------
PM MAIL WWW   Вверх
mes
Дата 21.8.2011, 17:52 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


любитель
****


Профиль
Группа: Участник Клуба
Сообщений: 7954
Регистрация: 14.1.2006

Репутация: 79
Всего: 250



помимо этого нужен еще читатель (из строки или потока)
Код

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:09


--------------------
PM MAIL WWW   Вверх
mes
Дата 21.8.2011, 18:08 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


любитель
****


Профиль
Группа: Участник Клуба
Сообщений: 7954
Регистрация: 14.1.2006

Репутация: 79
Всего: 250



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


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

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

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



--------------------
PM MAIL WWW   Вверх
o2n3e
Дата 21.8.2011, 18:46 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Новичок



Профиль
Группа: Участник
Сообщений: 10
Регистрация: 19.8.2011

Репутация: нет
Всего: -5



Числа в енуме вообще не имеют смысла.
Цитата(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.
PM MAIL   Вверх
Ответ в темуСоздание новой темы Создание опроса
Правила форума "C/C++: Для новичков"
JackYF
bsa

Запрещается!

1. Публиковать ссылки на вскрытые компоненты

2. Обсуждать взлом компонентов и делиться вскрытыми компонентами

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


Если Вам понравилась атмосфера форума, заходите к нам чаще! С уважением, JackYF, bsa.

 
0 Пользователей читают эту тему (0 Гостей и 0 Скрытых Пользователей)
0 Пользователей:
« Предыдущая тема | C/C++: Для новичков | Следующая тема »


 




[ Время генерации скрипта: 0.0623 ]   [ Использовано запросов: 22 ]   [ GZIP включён ]


Реклама на сайте     Информационное спонсорство

 
По вопросам размещения рекламы пишите на vladimir(sobaka)vingrad.ru
Отказ от ответственности     Powered by Invision Power Board(R) 1.3 © 2003  IPS, Inc.