Помощь - Поиск - Пользователи - Календарь
Полная версия этой страницы: К знатокам
Форум разработчиков электроники ELECTRONIX.ru > Микроконтроллеры (MCs) > Cредства разработки для МК > IAR
Страницы: 1, 2, 3
Rst7
Цитата(dxp @ Sep 24 2007, 15:49) *
Мне кажется, что вы слишком напряжены. Прошу прощения, если ошибся. Как говорят "там" "take it easy". smile.gif Я не ставлю себе целью кого-то на чем-то ловить и "ставить подножку". smile.gif


В последнее время, к сожалению, такое очень распространено на данном форуме. Вот поэтому я так и отписал. Сорри, что подумал, что вы на меня сейчас нападете smile.gif

Цитата
Я про полиморфизм вот почему спросил. Тут на протяжении уже десятка постов постоянно звучит термин ООП. А ООП без полиморфизма не бывает. Вот я и не понял, где там ООП начинается. Классов и наследования тут (для ООП) недостаточно. Если полиморфизма нет, то и ООП нет, а есть только ОП, сиречь объектное программирование.
Кстати, тождество С++ === ООП совсем не верно. С++ - язык гибридный, мультипарадигменный, как его называют. Т.е. в нем вполне гармонично уживаются и процедурная, и объектная, и объектно-ориентированная парадигмы. Всякая из них к своему месту. Надо их применять так, чтоб удобно было и эффективно, спорить, какая лучше, достаточно безсмысленно.


Безусловно. Просто на протяжении двух-трех десятков постов вижу такие фразы:
Цитата
Вызывает сомнение "чуство объекта" у автора.


Цитата
собственно классы здесь притянуты за уши.


Цитата
Позвольте об этом судить тем, кто умеет пользоваться инструментом.


Но вижу только слова. Не вижу ни одного красивого примера, вызывающего чувство "Вот оно, ради этого стоит жить" smile.gif Пример с фильтром не удовлетворил, такого класса код в каждом посте, агитирующем за цпп/ооп, прекрасно напишу на си и не буду выдумывать, что в какой класс положить, и как побольше разных ненужных фенечек прикрутить.

Привел пример на голом Си. Прошу рассказать, каким образом на С++ с использованием ООП будет лучше - мне рассказывают про кучу наследований, рассказывают, как мне прибить костыль рядом и таскать его по всем проектам, и так далее.

Цитата(Непомнящий Евгений @ Sep 24 2007, 15:59) *
А вот тут вы не правы. Мне надо будет написать внешнюю функцию, которая будет включать и выключать этот таймер.


Ну и я могу так сделать. Когда мне понадобится. А, скорее всего, никогда не понадобится, и посему я обойдусь парой операторов, а не написанием функций и их вызовом.

Цитата
А в прерывании таймера дернуть метод объекта TCanal.


Если вы немного посмотрели мой код, то наверное увидели, что меня меньше всего волнуют какие там прерывания от таймера. Есть флаг и все. Я получил управление (вышел из wait_int()), проверил его. А так как прерывание от таймера - суть вылет по таймауту, то больше мне тут делать нечего, и с флагом ошибки я вываливаюсь из функции. Зачем мне звать какой-то метод? ООП ради ООП? Вот против я этого.


Цитата
Крут не полиморфизм, а те возможности, которые он дает.


Давайте обсудим.

Цитата
Вы говорите: вам нужен костыль на нижнем уровне.
Ваше решение: переписать этот самый уровень для данного проекта.
Полиморфизм: вытащить специфичный код в отдельный объект и подключать тот вариант, который вам нужен.


И в чем же плюс? Старый вариант у меня останется в виде исходника проекта 1, из которого я сделаю ^C. Т.е. костыль будет в том исходнике проекта 2, в который я сделаю ^V и поработаю руками. Но в следующий проект я этот костыль не понесу, он останется в проекте 2.

Конечно, тут можно сказать, что это тоже способ наследования biggrin.gif не средствами языка, а средствами редактора текстов. Я сторонник такого способа. Считаю, что он несет меньше оверхеда в проект.
dxp
Цитата(Rst7 @ Sep 24 2007, 20:44) *
Не вижу ни одного красивого примера, вызывающего чувство "Вот оно, ради этого стоит жить" smile.gif

Ну, это уже от вас зависит. На любой пример можно сказать, что он, де, фигня, не то, не цепляет, не удовлетоворяет и т.д. smile.gif Апологеты асма тоже грят, что их все устраивает и никаих Сей они знать не желают. smile.gif Это не в ваш огород камень, это к слову. smile.gif

Цитата(Rst7 @ Sep 24 2007, 20:44) *
Пример с фильтром не удовлетворил, такого класса код в каждом посте, агитирующем за цпп/ооп, прекрасно напишу на си и не буду выдумывать, что в какой класс положить, и как побольше разных ненужных фенечек прикрутить.

В том-то и дело, что не надо стараться делать лишь бы на классах. Надо просто стараться думать, моделировать свою программу в виде объектов. А классы уже - это просто удобное средство реализации, не более.

Цитата(Rst7 @ Sep 24 2007, 20:44) *
Привел пример на голом Си. Прошу рассказать, каким образом на С++ с использованием ООП будет лучше - мне рассказывают про кучу наследований, рассказывают, как мне прибить костыль рядом и таскать его по всем проектам, и так далее.

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

Обязательно почитайте Г.Буча.
SasaVitebsk
Хочу отметить (просто мысли вслух причём чисто мои), анализируя дискуссию, что возможно на мои пристрастия и взгляды (а возможно и на взгляды Rst7) влияет то с чем мы работаем.
Приведу пример.

1) У меня в одном проекте есть 485 и несколько прерываний. Одно из них (регенерация изображения) невозможно прервать и сложно разбить. Время его исполнения такова, что я могу пропустить два байта на скорости 115200. Что для AVR равносильно "дырке в данных". В то время, как на LPC с его буфером FIFO мне это будет по барабану. Иными словами на ARM7 мне можно выписывать практически любой обработчик и не беспокоится об оверхеаде. На AVR - нет.

2) За всю мою историю мне не приходилось работать с 2 а тем более с 3/4/5 UARTами. Не было такой задачи.

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

Ну а применение более мощных процов будет обязательно. От этого никуда не денешься вне зависимости от наших желаний. Снижение цены 32-ух битников с более мощной переферией и более серьёзными ресурсами, приведёт к сворачиванию 8-ми битников. Это только вопрос времени.
alexander55
Каждому овощу свое время (поговорка).
А е сли время еще не пришло ?
Непомнящий Евгений
to rst7
позволю себе резюмировать нашу дискуссию.
Ваши аргументы
1. Код на С более компактен, чем код на С++ с использованием ООП
2. В каждом своем проекте вы имеете оригинальный код, который получили копипастом из предыдущего проекта и который можете совершенно независимо менять.
3. У вас нет никакого оверхеда ни по памяти ни по скорости. За счет прямого использования железно-зависимых вещей ваш код работает максимально быстро и не требует дополнительных переменных.
Итого: ООП-возможности С++ для вас бесполезны.

Мои аргументы:
1. Большой код на С++ с использованием ООП более компактен, чем на С
2. В каждом проекте я использую один и тот же код без изменений. Все мои проекты опираются на общую библиотеку (каркас) объектов. А это существенно сокращает время написания проекта.
3. За счет того, что надо выделять общую часть, у меня автоматически выделяется слой взаимодействия с железом; в общем коде отсутствуют обращения к регистрам процессора. Таким образом, переход на другой процессор проходит максимально безболезненно.
Итого: ООП-возможности С++ мне нужны.

Дальнейший спор на эту тему ИМХО бессмысленен; я не ставлю своей целью убедить вас использовать ООП. Вы не докажете мне преимуществ своего подхода smile.gif .
Rst7
Цитата
Почитайте что-нибудь типа "Искусства программирования" (к стыду своему забыл автора).Там на эту тему очень подробно написано.


Вот че-то я не помню, что бы Кнут что-то писал про ООП. Во времена первого издания и ООП не было smile.gif

Кнута, правда, читал 15 лет назад. Новое издание пробежал по диагонали (грешен), вроде особо отличий не нашел...

Цитата
За счет прямого использования железно-зависимых вещей ваш код работает максимально быстро и не требует дополнительных переменных.


Да одна же функция ходит к железу, ну две. Не сто, не двести. У вас пара методов. Так что не аргумент.

Цитата
возможно на мои пристрастия и взгляды (а возможно и на взгляды Rst7) влияет то с чем мы работаем


Да я не только с AVR работаю. Вот могу привести пример из другой области. Областью этой я занимаюсь исключительно как хобби, для развлечения. Есть платформа на ARM926EJS 104МГц (можно и 208). Внутри есть RTOS (Nucleos), куча всякой периферии, GUI, сокеты и прочее. Приведу пример, где заканчиваются ресурсы системы. При количестве чанков кучи больше 4500 (примерно), начинает заметно тормозить система (из-за часто используемого malloc'a и mfree). В результате простое использование какого-нибудь STL (уж ооп в полный рост. Или нет?) для разбора более-менее приличных размеров XML (или странички ОперыМини) мгновенно приводит к дикой фрагментации памяти. Приходится все делать ручками, с минимизацией фрагментации. Вот и смысл этой портабельности, если я ее применить не могу?
А сам внутренний софт платформы, кстати, писан без всяких плюсов. И самое смешное, отлично был в свое время спортирован с C166 на ARM smile.gif
Хотя конечно, все фунции гуев имеют первым параметром указатель на структуру (типа this), но это не плюсы.
Есть и другие платформы подобного плана. И во всех плюсами и не пахнет. Кстати, одну из этих платформ портировали с AVR на ARM... До сих пор можно увидеть куски кода явно AVRовского происхождения smile.gif Ниче, пашет...
Непомнящий Евгений
Цитата(Rst7 @ Sep 25 2007, 09:41) *
При количестве чанков кучи больше 4500 (примерно), начинает заметно тормозить система (из-за часто используемого malloc'a и mfree). В результате простое использование какого-нибудь STL (уж ооп в полный рост. Или нет?) для разбора более-менее приличных размеров XML (или странички ОперыМини) мгновенно приводит к дикой фрагментации памяти.


Ну так там же аллокатор настраивается. По умолчанию используется стандартный. Если у вас дикая фрагментация на нем получается, надо написать свой собственный, заточенный под конкретный размер блоков, к примеру. Он кстати получится и более быстрым smile.gif. Зато вы сможете продолжать использовать stl, и вам не придется заново писать собственные контейнеры.

Цитата
А сам внутренний софт платформы, кстати, писан без всяких плюсов. И самое смешное, отлично был в свое время спортирован с C166 на ARM smile.gif
Хотя конечно, все фунции гуев имеют первым параметром указатель на структуру (типа this), но это не плюсы.

Это тоже самый ОП (или даже ООП - если там еще какие-никакие указатели на функции появляются), только средствами С... Почему не писали на С++ - это вопрос к авторам. Может не было тогда компилятора С++ под их платформу, может еще какие резоны.

Насчет "искусства программирования" - извиняюсь, имел в виду "Совершенный код" Макконнелла
Сергей Борщ
Цитата(Непомнящий Евгений @ Sep 25 2007, 07:43) *
2. В каждом проекте я использую один и тот же код без изменений. Все мои проекты опираются на общую библиотеку (каркас) объектов. А это существенно сокращает время написания проекта.
Евгений, а можно глянуть на этот ваш TCanal? Мне близок ваш подход, но попытки создать такой класс пока не увенчались успехом.
SasaVitebsk
Цитата(Сергей Борщ @ Sep 25 2007, 13:57) *
Евгений, а можно глянуть на этот ваш TCanal? Мне близок ваш подход, но попытки создать такой класс пока не увенчались успехом.

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

smile.gif
Непомнящий Евгений
Цитата(Сергей Борщ @ Sep 25 2007, 14:57) *
Евгений, а можно глянуть на этот ваш TCanal? Мне близок ваш подход, но попытки создать такой класс пока не увенчались успехом.


Буду на работе - выложу. Наверное, сегодня ближе к вечеру.
Dog Pawlowa
Абстрагируясь от спора по поводу С++ и ООП, замечу одну деталь - сам я отказался от универсальных исходников и библиотек, и теперь включаю все исходники в один проект, в том числе и повторяющиеся многократно. Причина - желание замедлить увеличение энтропии в версиях проектов. Нужно иметь возможность откатить любой проект. И повторить однозначно результат компиляции любой версии. Отсюда редкое обновление среды, и тайная страсть к запуску компилятора из командной строки smile.gif
alexander55
Цитата(Dog Pawlowa @ Sep 26 2007, 15:37) *
желание замедлить увеличение энтропии

Браво !!!
HARMHARM
Цитата(Dog Pawlowa @ Sep 26 2007, 14:37) *
Отсюда редкое обновление среды, и тайная страсть к запуску компилятора из командной строки smile.gif

А что мешает запускать компилятор (IAR, как я понимаю) из командной строки? wink.gif
Впрочем, я вас понимаю. Неделю писал makefile и еще неделю искал как заставить работать свои функции вместо оных из DLIB. Оказалось - порядок задания линкеру объектных файлов smile.gif
Непомнящий Евгений
выкладываю свой канал ...

Цитата(Dog Pawlowa @ Sep 26 2007, 15:37) *
Нужно иметь возможность откатить любой проект. И повторить однозначно результат компиляции любой версии.

А чем не нравится система контроля версий?
Если после каждого выпуска прошивки делать коммит всего, создавать таг и включать его название\номер в прошивку, то можно совершенно однозначно повторить результат... Некоторые товарищи рекомендуют включать в систему контроля версий и используемые инструменты - компилятор и т.д.
Dog Pawlowa
Цитата(Непомнящий Евгений @ Sep 27 2007, 12:41) *
А чем не нравится система контроля версий?

Да нравится, использую.
Речь идет об общих исходниках. Допустим, я поменял общие исходники (например программный UART AVR) сегодня, 27 сентября. Но один проект будет производиться без изменений еще год, а второй требует доработки у заказчика. То есть общий исходник должен быть включен в индивидуальный проект, и он перестает быть общим.
singlskv
Цитата(dxp @ Sep 24 2007, 07:38) *
Нет, хак - это рассовывание переменных уровня С в РОН процессора с использованием нестандартных, непереносимых средств компилятора, имеющее последствиями как минимум ухудшение качества оптимизации со стоторы компилятора - за этим кодом нужно постоянно следить, дает ли он дейтсвительно выигрыш, и как меняется ситуация при изменении кода - не пришла ли, скажем, пора освободить пару регистров уже для компилятора, а то он что-то медленный код стал генерить - регистров ему не хватат. Т.е. хак в данном случае - это ручная низкоуровенвая оптимизация распределения ресурсов, оптимизация на уровне асма в программе на С.
хорошо, допустим я соглашусь с Вами что все это был просто хак,
тогда давайте сравнивим с Вашим такой код:
Код
volatile BYTE ADCL_;
volatile BYTE ADCH_;
volatile BYTE ADMUX_;
volatile BYTE ADCSRA_;
volatile BYTE SYSTICKSTART_;
volatile BYTE PINB_;
volatile BYTE PINC_;
volatile BYTE PIND_;

//-------------------------------------------------------------------
// Прерывание системного таймера
//-------------------------------------------------------------------


void TIMER2_COMP_vect(void) __attribute__((signal));
void TIMER2_COMP_vect()
{
  ADCL_  = ADCL;       // читаем результат последнего преобразования
  ADCH_  = ADCH;
  ADMUX  = ADMUX_;     // новый канал
  ADCSRA = ADCSRA_;    // запускаем новое преобразование
                       // информируем о начале нового цикла
  SYSTICKSTART_= ADCSRA_;     // записью значения ADCSRA в SYSTICKSTART_
                              // сбрасывается в 0 в основном цикле
  PINB_  = PINB;       // читаем порты
  PINC_  = PINC;
  PIND_  = PIND;
}

Это уже чистый C ???

Как насчет реализации на С++ ?
Maddy
Цитата(Dog Pawlowa @ Sep 27 2007, 18:10) *
Да нравится, использую.
Речь идет об общих исходниках. Допустим, я поменял общие исходники (например программный UART AVR) сегодня, 27 сентября. Но один проект будет производиться без изменений еще год, а второй требует доработки у заказчика. То есть общий исходник должен быть включен в индивидуальный проект, и он перестает быть общим.

Хм ... ну таки кто мешает ветку в СКВ сделать ? Но собственно грабли возможны в другом - если поменялся компилятор - возможны варианты sad.gif IMHO в СКВ к проекту надо добавлять файлик с описанием используемых средств ....
dxp
Цитата(singlskv @ Sep 28 2007, 01:40) *
хорошо, допустим я соглашусь с Вами что все это был просто хак,
тогда давайте сравнивим с Вашим такой код:
Это уже чистый C ???

Да, это уже почти чистый С - с небольшими необходимыми расширениями, без которых не обойтись.

Цитата(singlskv @ Sep 28 2007, 01:40) *
Как насчет реализации на С++ ?

Ну, собсно, тут на плюсах особенно не развернуться, чтобы показаться во всей красе smile.gif, но и этот фрагмент я бы написал не так. Плюсатый вариант:

Код
#include <iom16.h>

#define INLINE _Pragma("inline=forced")

typedef unsigned char byte;

//---------------------------------------------------------
class TADC
{
public:
    INLINE void handle()
    {
        adcl        = ADCL;      // читаем результат последнего преобразования
        adch        = ADCH;
        ADMUX       = admux;     // новый канал
        ADCSRA      = adcsra;    // запускаем новое преобразование
        systickstart= adcsra;
    }

private:
    byte adcl;
    byte adch;
    byte admux;
    byte adcsra;
    byte systickstart;
}
adc;
//---------------------------------------------------------
class TPorts
{
public:
    INLINE void read()
    {
       pinb  = PINB;       // читаем порты
       pinc  = PINC;
       pind  = PIND;
    }

private:
    byte pinb;
    byte pinc;
    byte pind;
}
ports;

//-------------------------------------------------------------------
// Прерывание системного таймера
//-------------------------------------------------------------------
#pragma vector=TIMER2_COMP_vect
__interrupt void Comp_ISR()
{
    adc.handle();
    ports.read();
}

Комментарий. Собственно, объекта Системного Таймера тут по сути нет. От него есть только прерывание, но собственного представления нет, поэтому я не стал его выделять в класс. Конечно, можно было бы включить в него объекты adc и ports, но мне тут не понятна их связь. Возможно, она есть и вы ее знаете - скажем оба этих объекта функционально по замыслу являются неотъемлемой частью системного таймера. Но из самого предложенного фрамента мне так не показалось. Поэтому я не стал заводить лишнуюю сущность. Вообще, это уже вопросы проектирования, область близкая, но все же не та, которую мы тут сейчас рассматриваем.

В целом код на мой взгляд даже в этом мелком случае выглядит стройнее и понятнее - он подчеркивает наличие разнокачественных объектов - АЦП и портов, акцентирует внимание на функциональность кода: сначала - в обработчике прерывания - подчеркнуто, что делается, потом - как это реализуется. Такое разделение улучшает читабельность и понимбельность кода, особенно другими людьми. На С в принципе можно было бы похоже написать, если задействовать inline функции, которые появились в С99. Но С++ прямо тяготеет к такому стилю. А главное, мы тут не показываем представление и к потрохам наших объектов никто просто так не доберется. Классы, конечно, не полные, в них нет функций, которые делали бы что-то полезное с теми же значениями, считанными в обработчике прерываний. Но это просто выходит за рамки данного примера. Там тоже никаких проблем ни в реализации, ни в эффективности нет.

Возможно, вы хотели как раз посмотреть реализацию прерывания внутри объекта класса. Отвечу: никакой принципиальной разницы нет - просто сама функция-обработчик прерывания была при этом объявлена внутри класса и снабжена при этом квалификатором static, только и всего. Качество кодогенерации от этого никак не зависит.

Кстати, о кодогенерации. Вот, что получилось:

Код
                    In segment CODE, align 2, keep-with-next    
__interrupt void Comp_ISR()                                      
        ??Comp_ISR:                                              
{                                                                
93FA               ST      -Y, R31                              
93EA               ST      -Y, R30                              
930A               ST      -Y, R16                              
    adc.handle();                                                
....               LDI     R30, LOW(`adc`)                      
....               LDI     R31, (`adc`) >> 8                    
B104               IN      R16, 0x04                            
8300               ST      Z, R16                              
B105               IN      R16, 0x05                            
8301               STD     Z+1, R16                            
8102               LDD     R16, Z+2                            
B907               OUT     0x07, R16                            
8103               LDD     R16, Z+3                            
B906               OUT     0x06, R16                            
8103               LDD     R16, Z+3                            
8304               STD     Z+4, R16                            
    ports.read();                                                
B306               IN      R16, 0x16                            
8305               STD     Z+5, R16                            
B303               IN      R16, 0x13                            
8306               STD     Z+6, R16                            
B300               IN      R16, 0x10                            
8307               STD     Z+7, R16                            
}                                                                
9109               LD      R16, Y+                              
91E9               LD      R30, Y+                              
91F9               LD      R31, Y+                              
9518               RETI                                        
                                                                
                    In segment INTVEC, offset 0xc, root          
        `??Comp_ISR??INTVEC 12`:                                
........           JMP     ??Comp_ISR

По-моему, тут и руками лучше не напишешь. smile.gif
alexander55
Цитата(dxp @ Sep 28 2007, 08:31) *
По-моему, тут и руками лучше не напишешь. smile.gif

Согласен. Но добавлю.
Конструкторы позволяют всю инициализацию убрать в классы.
При этом получается следующие плюсы C++:
-отсутствие бесчисленной инициализации в main;
-в классах все видно (в одном флаконе): инициализация, реализация, все несущественное не торчит.
dxp
Цитата(alexander55 @ Sep 28 2007, 11:53) *
Согласен. Но добавлю.
Конструкторы позволяют всю инициализацию убрать в классы.
При этом получается следующие плюсы C++:
-отсутствие бесчисленной инициализации в main;
-в классах все видно (в одном флаконе): инициализация, реализация, все несущественное не торчит.

Все так. Конструкторы я тут не стал определять, т.к. мне не ясно, что они должны делать. Как и функции, которые дальше делают что-то с полученными данными. Поэтому классы и не полные.
alexander55
Цитата(dxp @ Sep 28 2007, 09:47) *
Все так. Конструкторы я тут не стал определять, т.к. мне не ясно, что они должны делать. Как и функции, которые дальше делают что-то с полученными данными. Поэтому классы и не полные.

Я это понял. smile.gif
Сергей Борщ
Цитата(alexander55 @ Sep 28 2007, 07:53) *
Конструкторы позволяют всю инициализацию убрать в классы.
При этом получается следующие плюсы C++:
-отсутствие бесчисленной инициализации в main;
Все в этом хорошо кроме двух существенных недостатков - в конструкторах нельзя использовать сервисы ОС, ибо ос запускается из main() и второй - не гарантирован порядок вызова конструкторов. И если один объект использует при инициализации данные другого - возможны три варианта:
1) повезет и порядок случайно окажется нужным
2а) не повезет, и работать не будет,
2б) не повезет, но работать будет
3) завести в каждом классе init() и вызвать его в нужном порядке из main(), а конструктор не нужен.

Как решать такие ситуации? Объясню на двух простых примерах:
1) scmRTOS. Каждый поток в своем конструкторе прописывает себя в таблице объекта Kernel. Чаще всего объект Kernel еще не существует и спасает лишь то, что он глобальный, и его адрес известен компилятору. Потоки складывают данные туда, где потом разместится Kernel. Это случай типа 2б и спасает лишь то, что конструктор TKernel фактически разбит на две части - сначала при обнулении неинициализированных глобальных переменных обнуляется и память на месте размещения Kernel, а потом, при конструировании объекта заполняются своими значениями ненулевые члены. Здесь процесс борьбы вроде бы очевиден - поскольку ядро может быть лишь одно, то его можно сделать виртуальным базовым классом для потоков или все его(ядра) члены - статическими. Второй способ менее элегантен, ибо придется где-то отдельно перечислять инициализацию каждого члена.

2) Микросхема Wiznet. Имеет 4 сокета (каждый со своим набором регистров) и набор общих настроек. Описана классом TWiznet, который имеет членами массив из четырех TSocket. Вроде все логично.
Есть объекты (Ttelnet, Ttftp), которые используют сокеты и которые по хорошему в конструкторах должны делать настройку сокетов, но проблема в том, что на момент вызова конструкторов этих объектов объекты сокетов еще не созданы. Тупик, приводящий к варианту 3.


Для Непомнящий Евгений: Посмотрел ваш исходник, есть вопрос. Почему вы в конструкторе используете конструкцию { this->member = initvalue; } вместо рекомендуемой Страуструпом (правда он тоже не всегда ее придерживается) : member1(initvalue1) {}?
dxp
Цитата(Сергей Борщ @ Sep 28 2007, 14:49) *
Как решать такие ситуации? Объясню на двух простых примерах:
1) scmRTOS. Каждый поток в своем конструкторе прописывает себя в таблице объекта Kernel. Чаще всего объект Kernel еще не существует и спасает лишь то, что он глобальный, и его адрес известен компилятору. Потоки складывают данные туда, где потом разместится Kernel. Это случай типа 2б и спасает лишь то, что конструктор TKernel фактически разбит на две части - сначала при обнулении неинициализированных глобальных переменных обнуляется и память на месте размещения Kernel, а потом, при конструировании объекта заполняются своими значениями ненулевые члены.

Оно не то, чтобы спасает, оно на этом и основано - объект Kernel размещается статически, т.е. память под него выделяется на этапе компиляции, и на рантайме туда уже писать вполне безопасно. Безотносительно к тому, инициализирована эта память или нет.

Цитата(Сергей Борщ @ Sep 28 2007, 14:49) *
2) Микросхема Wiznet. Имеет 4 сокета (каждый со своим набором регистров) и набор общих настроек. Описана классом TWiznet, который имеет членами массив из четырех TSocket. Вроде все логично.
Есть объекты (Ttelnet, Ttftp), которые используют сокеты и которые по хорошему в конструкторах должны делать настройку сокетов, но проблема в том, что на момент вызова конструкторов этих объектов объекты сокетов еще не созданы. Тупик, приводящий к варианту 3.

Есть честный обходной путь. Размещай объявления объектов в одной единице компиляции и порядок вызова конструкторов будет гарантирован. На уровне проекта это вполне нормальное и устойчивое решение.
alexander55
[quote name='Сергей Борщ' date='Sep 28 2007, 11:49' post='300070']
И если один объект использует при инициализации данные другого - возможны три варианта:
1) повезет и порядок случайно окажется нужным
2а) не повезет, и работать не будет,
2б) не повезет, но работать будет
3) завести в каждом классе init() и вызвать его в нужном порядке из main(), а конструктор не нужен.
quote]
Проблема понятная.
Возможные решения.
1. Если логично, использовать наследование (тогда порядок вызова конструкторов однозначный).
2. Использование наследования виртуальных классов.
3. В main сразу всех родить.
4. Нехороший вариант. Проверять есть ли данный класс.

По поводу scmRTOS лучше ответит Гари (я его видел мельком).
PS. Пока я ходил обедать, он уже ответил.
Непомнящий Евгений
Цитата(Сергей Борщ @ Sep 28 2007, 11:49) *
Для Непомнящий Евгений: Посмотрел ваш исходник, есть вопрос. Почему вы в конструкторе используете конструкцию { this->member = initvalue; } вместо рекомендуемой Страуструпом (правда он тоже не всегда ее придерживается) : member1(initvalue1) {}?


У меня имя члена совпадает с именем аргумента, и я как-то не пробовал что получится при записи
: member1(member1) {}

Ну и как-то привычнее smile.gif. Я списки инициализации использую для конструкторов предков, объектов-членов и констант, а все остальное обычно размещаю в теле конструктора.
dxp
Цитата(Непомнящий Евгений @ Sep 28 2007, 16:44) *
У меня имя члена совпадает с именем аргумента, и я как-то не пробовал что получится при записи
: member1(member1) {}

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

Цитата(Непомнящий Евгений @ Sep 28 2007, 16:44) *
Ну и как-то привычнее smile.gif. Я списки инициализации использую для конструкторов предков, объектов-членов и констант, а все остальное обычно размещаю в теле конструктора.

Дык для этого и введен универсальный синтаксис, чтобы единообразно все делать. Кроме того, как вы правильно заметили, проинициализировать константные члены другого способа и нет.
Сергей Борщ
Цитата(Непомнящий Евгений @ Sep 28 2007, 12:44) *
Ну и как-то привычнее smile.gif. Я списки инициализации использую для конструкторов предков, объектов-членов и констант, а все остальное обычно размещаю в теле конструктора.
Но ведь тот же Страуструп пишет, что если член будет не POD-типа, то при таком варианте для члена сначала будет вызван конструктор по умолчанию, а потом (уже в теле) копирующий конструктор. К тому же если член не имеет конструктора по умолчанию, то компилятор просто выдаст ошибку.

Правильно ли я понял, что все сводится только к эстетическим соображениям? Я пока только учусь, поэтому хочу разобраться laughing.gif
singlskv
Цитата(dxp @ Sep 22 2007, 20:52) *
Я не буду оригинальным, просто повторю, что говорят авторитетные дядьки вроде Г.Буча и Б.Страуструпа. Самое главное при объектном подходе - это сопоставлять классы и объекты объектам реального мира. Мир в восприятии человека представляется дискретным - человек выделяет в нем более-менее законченные целостные объекты, понятия, которыми он оперирует, строя модели. Так вот объектные (и объектно-ориентированные) языки позволяют на уровне средств языка оперировать типами, отображаемыми на объекты реального мира. Просто старайтесь вычленить в своей программе и прежде всего в той предметной области, которую описывает программа, эти самые законченные (на достаточном уровне абстракции, конечно) объекты и понятия, а после этого уже можно спокойно описывать эти объекты у себя в программе. Этот подход позволяет при разработке программы сразу думать на уровне объектов, а не разрозненных переменных, связи между которыми при отсутствии тех же классов разработчику приходится держать в голове. Понятно, что думать на уровне объектов реального мира намного проще, чем на уровне гораздо большего количества неких в разной мере абстрактных переменных встроенных типов.


[quote name='dxp' post='299963' date='Sep 28 2007, 08:31']
Ну, собсно, тут на плюсах особенно не развернуться, чтобы показаться во всей красе smile.gif,
Код
...........................
class TPorts
{
public:
    INLINE void read()
    {
       pinb  = PINB;       // читаем порты
       pinc  = PINC;
       pind  = PIND;
    }

private:
    byte pinb;
    byte pinc;
    byte pind;
}
ports;
[/quote]Не могли бы Вы на примере Вашего объекта ответить на несколько вопросов

Класс который вы назвали TPorts(хотя логичнее его было бы назвать TPortsBCD) это дискретный
законченный целостный объект ? (про незаконченную реализацию я уже читал...)
Если в другом проекте мне понадобятся порты С, D и E, я должен буду создавать для этого
новый класс ?
Если в другом проекте мне понадобится не 3 таких порта а например 2 или 4, я должен
создать новый класс ? (с самого начала)
Если иногда мне нужно читать пины всех портов одновременно, а иногда только пины одного
порта, я должен в этом классе предусмотреть функции для чтения каждого порта по отдельности ?
А если мне нужно еще и писать иногда в порт, причем в любой из 3 по определенным битам ?
.........................................
и т.д. .......

Для меня Ваш класс как минимум нелогичен,
ИМХО, начинать таки нужно с одного класса под названием TPort(один порт, любой).
Ваш класс, ИМХО, имеет очень маленькое отношение к ОПП, это больше похоже на
вариант "меня попросили написать с классами, я и написал".

P.S. А насчет того что развернуться негде, еще как есть...
Тока оверхеда при этом будет мама не горюй...


[quote name='dxp' post='299963' date='Sep 28 2007, 08:31']
Кстати, о кодогенерации. Вот, что получилось:
.........................
По-моему, тут и руками лучше не напишешь.  :)
[/quote]попробуйте вот так:
[code]
    INLINE void handle()
    {
                                byte tmp;
        adcl        = ADCL;      // читаем результат последнего преобразования
        adch        = ADCH;
        ADMUX       = admux;     // новый канал
                                tmp = adcsra;
        ADCSRA      = tmp;    // запускаем новое преобразование
        systickstart =  tmp;
    }

Должно слегка помочь smile.gif
И это исчо без применения асм и всяких хаков smile.gif
Непомнящий Евгений
Цитата(singlskv @ Sep 29 2007, 01:29) *
Тока оверхеда при этом будет мама не горюй...


Мой сослуживец писал класс "Ножка порта". Использовал шаблон и инлайн-функции. Проверил результат - оверхеда нету. Потом параметризовал такими классами класс, считающий импульсы.

Единственное, что мне не нравится в таком подходе - если использовать шаблоны на низком уровне иерархии, то они "тянутся" наверх; притом реализацию функций класса-шаблона надо делать в h-файле.



Цитата(Сергей Борщ @ Sep 29 2007, 00:09) *
Но ведь тот же Страуструп пишет, что если член будет не POD-типа, то при таком варианте для члена сначала будет вызван конструктор по умолчанию, а потом (уже в теле) копирующий конструктор. К тому же если член не имеет конструктора по умолчанию, то компилятор просто выдаст ошибку.

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


Так я ж вроде бы и сказал, что для инициализации объектов - членов, предков и констант использую список инициализации.
Члены встроенных типов я инициализирую в теле конструктора по привычке; может стоит от нее отказаться.

Счас проверил, запись вида : member1(member1) действительно работает правильно.
dxp
Цитата(singlskv @ Sep 29 2007, 04:29) *
Класс который вы назвали TPorts(хотя логичнее его было бы назвать TPortsBCD) это дискретный
законченный целостный объект ? (про незаконченную реализацию я уже читал...)

Как может быть незаконченный объект быть "дискретным законченным целостным" объектом? Любой объект класса дискретный по определению. Насколько он целостный, зависит от многих факторов, но как правило - это субъективная характеристика. А то, что он незаконченный, я уже сказал и объяснил почему.

Цитата(singlskv @ Sep 29 2007, 04:29) *
Если в другом проекте мне понадобятся порты С, D и E, я должен буду создавать для этого
новый класс ?
Если в другом проекте мне понадобится не 3 таких порта а например 2 или 4, я должен
создать новый класс ? (с самого начала)
Если иногда мне нужно читать пины всех портов одновременно, а иногда только пины одного
порта, я должен в этом классе предусмотреть функции для чтения каждого порта по отдельности ?
А если мне нужно еще и писать иногда в порт, причем в любой из 3 по определенным битам ?
.........................................
и т.д. .......

Это все вопросы уровня проектирования. Безусловно, я не могу написать реализацию, которая вас устроила бы, и никто не сможет, если он не телепат. Только вы сами можете это сделать. Я написал пример, основываясь на том коде, который вы привели. Что из него можно было понять? Только то, что в прерывании по системного таймеру производится чтение данных АЦП, запуск нового преобразования и чтение портов. Как между собой связаны таймер, АЦП и порты, я не знаю. Почему чтение и запуск АЦП производится в прерывании от таймера, а не в собственном, я тоже не знаю. Но не берусь давать такому подходу оценку, т.к. не знаю ни предметной предметной области, ни конкретной ситуации, для которой написана программа. Я лишь написал с помощью классов тот фрагмент, который был предложен к рассмотрению.

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

Цитата(singlskv @ Sep 29 2007, 04:29) *
Для меня Ваш класс как минимум нелогичен,
ИМХО, начинать таки нужно с одного класса под названием TPort(один порт, любой).

Это и есть проектирование программы. Вам тут безусловно виднее. Но то, что об этом заходит речь - это показывает различия в подходе при разработке программы на С и на C++. На С можно писать код, не задумываясь об этих моментах - программных сущностях, их иерархиях и взаимосвязях, - многие так и пишут, "в лоб". И, как правило, такие программы не отличаются структурной стройностью, внутренней логичностью, тяжелее в расширении и сопровождении. Конечно, можно писать на С и по-другому, разработав структуру и придерживаясь ее, но это требует известных усилий и квалификации.

С++ же напротив - тяготеет к такому стилю, при использовании классов сам подход сводится к определению нужных классов, наделения их необходимой функциональностью. А для этого надо сразу четко представлять, что мы имеем, что хотим реализовать, какие сущности у нас присутствуют в предметной области и т.д. Т.е. этап проектирования возникает сразу в явном виде на базе объектного подхода. Поскольку все эти различия идут на уровне исходного кода, кодогенерация и эффективность по сравнению с С не страдают.

Цитата(singlskv @ Sep 29 2007, 04:29) *
Ваш класс, ИМХО, имеет очень маленькое отношение к ОПП,

Он вообще к ООПу никакого отношения не имеет. biggrin.gif

Цитата(singlskv @ Sep 29 2007, 04:29) *
это больше похоже на вариант "меня попросили написать с классами, я и написал".

Почти так. Меня попросили реализацию фрагмента на С++, я ее сделал. Вы не это просили разве?

Цитата(singlskv @ Sep 29 2007, 04:29) *
P.S. А насчет того что развернуться негде, еще как есть...
Тока оверхеда при этом будет мама не горюй...

Не понял, что вы тут имели в виду. И про какой оверхед речь?

Цитата(singlskv @ Sep 29 2007, 04:29) *
попробуйте вот так:
Код
    INLINE void handle()
    {
                                byte tmp;
        adcl        = ADCL;      // читаем результат последнего преобразования
        adch        = ADCH;
        ADMUX       = admux;     // новый канал
                                tmp = adcsra;
        ADCSRA      = tmp;    // запускаем новое преобразование
        systickstart =  tmp;
    }

Должно слегка помочь smile.gif
И это исчо без применения асм и всяких хаков smile.gif

За счет чего? И причем тут уже ++?
singlskv
Цитата(dxp @ Sep 29 2007, 11:04) *
Почему чтение и запуск АЦП производится в прерывании от таймера, а не в собственном, я тоже не знаю. Но не берусь давать такому подходу оценку, т.к. не знаю ни предметной предметной области, ни конкретной ситуации, для которой написана программа.
Дык ответ то прост и банален, потому что джиттера не будет smile.gif при чтении портов например...
Цитата
Я лишь написал с помощью классов тот фрагмент, который был предложен к рассмотрению.

Цитата
Но ведь исходно вопрос-то возник не из-за этих вопросов проектирования, а из-за того, что якобы, классы привносят в код оверхед сами по себе. Это нисколько не так, что приведенный пример, надеюсь, ясно продемонстрировал.
dxp, я надеюсь что Вы сами то понимаете что слегка лукавите ?
Вариант классы ради классов я все-таки не рассматривал.
Вы будете меня убеждать что класс подобный описанному Вами будет когда-либо присутствовать
в Вашей программе ? (лучше соврите если это правда smile.gif )
Цитата
С++ же напротив - тяготеет к такому стилю, при использовании классов сам подход сводится к определению нужных классов, наделения их необходимой функциональностью. А для этого надо сразу четко представлять, что мы имеем, что хотим реализовать, какие сущности у нас присутствуют в предметной области и т.д.
И эта функциональность бывает иногда(часто) излишней для конкретного проекта.
Заметим сразу же что если какая-то функция реализованна в классе, то линкер не
имеет возможности выкинуть эту функцию из конечного бинарника(или имеет такое право...,
засомневался, кто знает просветите...)
Цитата
Он вообще к ООПу никакого отношения не имеет. biggrin.gif
Почти так. Меня попросили реализацию фрагмента на С++, я ее сделал. Вы не это просили разве?
Повторюсь еще раз, классы ради классов... smile.gif
Цитата
Не понял, что вы тут имели в виду. И про какой оверхед речь?
Все очень просто,
Вы создаете реальный(похожий на реальность) класс под названием типа TPort.
Этот объект будет включать в себя переменную указывающую на адрес
регистра PINx(в общем случае там таких переменных будет 3 (pin, port и ddr), хотя
в частных реализациях можно обойтись и одним адресом).
Так же в этом классе будет реализован доступ к этому порту в разных вариантах,
типа считать, записать, считать с маской, записать с маской, и т.д.
ну и конечно конструктор с привязкой к конкретному порту.
Про оверхед нужно объяснять или сами все поняли ?
Цитата
И причем тут уже ++?
С++ здесь уже действительно не при чем.
Цитата
За счет чего?
Ну это вроде очивидно,
за счет исключения лишнего считывания adcsra из памяти.
dxp
Цитата(singlskv @ Sep 30 2007, 02:59) *
dxp, я надеюсь что Вы сами то понимаете что слегка лукавите ?
Вариант классы ради классов я все-таки не рассматривал.
Вы будете меня убеждать что класс подобный описанному Вами будет когда-либо присутствовать
в Вашей программе ? (лучше соврите если это правда smile.gif )

Не понимаю, где я дал основание подозревать меня во вранье? Подобные мелкие классы я использовал и использую в своих программах, т.к. не вижу тому препятствий. Удобство же налицо - логическое объединение кода в отдельное пространство имен с возможностью управления доступом. Если вам это не надо, это не причина для обвинений во лжи.

Цитата(singlskv @ Sep 30 2007, 02:59) *
И эта функциональность бывает иногда(часто) излишней для конкретного проекта.
Заметим сразу же что если какая-то функция реализованна в классе, то линкер не
имеет возможности выкинуть эту функцию из конечного бинарника(или имеет такое право...,
засомневался, кто знает просветите...)

А почему не имеет? Что мешает? Функция-член класса ничем особенным в бинарнике не отличается. Вот если это виртуальная функция, то ее выкинуть нельзя, т.к. ее адрес помещается в vtbl. Но и на С если есть массив указателей на функции, то все функции будут включены, даже если какие-то из них не используются.

Цитата(singlskv @ Sep 30 2007, 02:59) *
Повторюсь еще раз, классы ради классов... smile.gif

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


Цитата(singlskv @ Sep 30 2007, 02:59) *
Все очень просто,
Вы создаете реальный(похожий на реальность) класс под названием типа TPort.
Этот объект будет включать в себя переменную указывающую на адрес
регистра PINx(в общем случае там таких переменных будет 3 (pin, port и ddr), хотя
в частных реализациях можно обойтись и одним адресом).
Так же в этом классе будет реализован доступ к этому порту в разных вариантах,
типа считать, записать, считать с маской, записать с маской, и т.д.
ну и конечно конструктор с привязкой к конкретному порту.

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

Цитата(singlskv @ Sep 30 2007, 02:59) *
Про оверхед нужно объяснять или сами все поняли ?

См выше. Кроме того, оверхед зависит от целевого процессора. Например, на MSP430 оверхеда не будет.

Цитата(singlskv @ Sep 30 2007, 02:59) *
Ну это вроде очивидно,
за счет исключения лишнего считывания adcsra из памяти.

Если вы заметили, у меня там представление класса объявлено без квалификаторов volatile, поэтому компилятору ничего не мешает оптимизировать доступ и не читать два раза. То, что он этого не сделал - это вопрос к качеству кодогенерации данного компилятора. А использовать временную переменную - это заниматься низкоуровневой оптимизацией на высоком уровне, что попахивает опять же хаком, хотя и безобидным в данном случае (из минусов то, что на пустом месте загромождается код). По правильному бороться с этим недостатком компилятора надо путем написания репорта в саппорт, чтобы этот момент в будущих версиях пофиксили.

Возвращаясь к основной теме. Мне не понятно, какую вы преследуете цель в рамках данной дискуссии. Если хотите понять преимущества С++, то стоит уже перестать спорить с очевидным и начать пробовать писать - "дров" по С++ вам тут накидали достаточно. Там появятся совсем другие вопросы, на которые участники темы, использующие С++ в своей работе (включая и меня), думаю, с удовольствием ответят. Если же вы пытаетесь мне доказать, что С++ не нужен и достаточно С, то это пустая потеря времени с вашей стороны - я достаточно знаком с обоими языками и в теории, и на практике, чтобы иметь собственное мнение и глубокую убежденность, основанную на собственном опыте. Поэтому, дабы прекратить переливание из пустого в порожнее, наверное, буду сворачивать свое участие в теме в этом контексте.
SasaVitebsk
Цитата(dxp @ Sep 30 2007, 14:12) *
Поэтому, дабы прекратить переливание из пустого в порожнее, наверное, буду сворачивать свое участие в теме в этом контексте.


Тем не менее спасибо за полезную информацию. Несмотря на то, что получилось что-то вроде спора людей с разными взглядами, надо учесть то что некоторые не вступали в дискуссию, так как не имели знаний и опыта применения. Но при этом читали её и пытались делать свои выводы. smile.gif

С этой точки зрения, как мне кажется, дискуссия была очень полезна.
Для просмотра полной версии этой страницы, пожалуйста, пройдите по ссылке.
Invision Power Board © 2001-2025 Invision Power Services, Inc.