Помощь - Поиск - Пользователи - Календарь
Полная версия этой страницы: К знатокам
Форум разработчиков электроники ELECTRONIX.ru > Микроконтроллеры (MCs) > Cредства разработки для МК > IAR
Страницы: 1, 2, 3
dxp
Цитата(Непомнящий Евгений @ Sep 21 2007, 13:49) *
А насчет оверхеда виртуальных функций можно посмотреть вот с какой стороны:
чтобы сделать эквивалентное в С и минимизировать ручную работу и ошибки, надо хорошо продумать стратегию + возможно реализовать несколько макросов + все равно останется масса ручной работы. Поэтому в С "виртуальные" функции используются только там, где это действительно нужно и после долгих размышлений об альтернативных вариантах.

В С++ это тоже точно так же. Если представляешь, как оно реализуется, во что выливается, если это слишком дорогой оверхед (один указатель smile.gif ), то тоже не используешь, а ищешь какие-то обходные пути. Все в руках разработчика. smile.gif

Цитата(Непомнящий Евгений @ Sep 21 2007, 13:49) *
С другой стороны, в С++ достаточно написать слово virtual, что несколько развращает. Поэтому возникает желание запихивать виртуальные ф-ции во все дырки. Соответственно, растет и оверхед biggrin.gif

Ну-у, вы еще шаблоны забыли - вот где возможность одним движением нагенерить мегатонны кода. smile.gif Никто ж не спорит, что надо осмотрительно пользоваться средствами языка. А для этого надо прежде всего их знать. Т.е. изучать. Чем мы, кроме всего прочего, тут и занимаемся. smile.gif

Цитата(alexander55 @ Sep 21 2007, 13:51) *
Я уверен, никто, кто работает с С++, не запутается, а если человек работает с С - то он даже не обратит на это внимания.

Ну, не знаю. Лично я, когда в контексте С++ вижу слово "метод", сразу понимаю под ним виртуальную функцию, т.к. это соответсвует терминологии, введенной по этому поводу Б.Страуструпом, а он для меня в этих вопросах авторитет. smile.gif
Maddy
Цитата(dxp @ Sep 21 2007, 11:14) *
Ну, не знаю. Лично я, когда в контексте С++ вижу слово "метод", сразу понимаю под ним виртуальную функцию, т.к. это соответсвует терминологии, введенной по этому поводу Б.Страуструпом, а он для меня в этих вопросах авторитет. smile.gif
Не флейма ради , а инфы для - _где_ он это писал ? Щаз пробежался по книжке - не углядел....
dxp
Цитата(Maddy @ Sep 21 2007, 15:46) *
Не флейма ради , а инфы для - _где_ он это писал ? Щаз пробежался по книжке - не углядел....

ссылку я уже давал однажды, но не помню, в какой теме. Ну, вот еще раз (даю в контексте):

Bjarne Stroustrup - The C++ Programming Language 3rd Edition

12.2.6 Virtual Functions [derived.virtual]

...
To allow a virtual function declaration to act as an interface to functions defined in derived
classes, the argument types specified for a function in a derived class cannot differ from the argu-
ment types declared in the base, and only very slight changes are allowed for the return type
(§15.6.2). A virtual member function is sometimes called a method.
A virtual function must be defined for the class in which it is first declared (unless it is declared
to be a pure virtual function; see §12.3). For example:
...

В русском переводе оно находится точно в этом же месте. И слово метод там выделено курсивом. smile.gif

Еще, кажется, мне это встречалось в его же "Дизайн и эволюция С++", но где именно в ней, не помню, а под рукой ее сейчас нет.
Maddy
2 dxp - Thanks wink.gif
SasaVitebsk
Цитата(dxp @ Sep 19 2007, 16:21) *
C++ - это принципиально иной подход к программированию. При внешней схожести С и С++ очень разные языки. Прежде всего разные тем, что на них думать надо совсем по-разному (если, конечно, от ++ хочется получить то, что они дают, а не остаться в рамках процедурного С).


Да вот именно с этим у меня и проблемы пока. sad.gif

Книги перечитывал уже не раз. И с Delfi работаю давно. Вроде всё понимаю и основные понятия объектного программирования чётко знаю, но пока на душу не легло.

Как zltigo где-нибудь в графике применить бы смог. Там это понятно. Но создание объекта "фильтр" пока из души не идёт и кажется притянутым за уши. (мне естественно. Я совершенно не утверждаю что это неправильно). Вот, к слову (извините за отступление, но уж если начали) прислал мне один молодой программист текст проги. Я ему слегка завидую, потому что я бы такое не написал не в жизь. Прошу прощение что на дельфях, но я думаю для большинства это не проблема. Главное не реализация а само чуство объекта. Итак может ли мне кто-нибудь наглядно пояснить в чём (в приведенном примере) объектная реализация удобнее (или какие преимущества имеет).

Приведу текст. (вырывать кусок не буду по понятным причинам)
1) Объектный
Код
unit CRCUnit;

interface

type
    TByteArray = array of Byte;

    // Вычислитель CRC8
    TCRC8Calc = class
    private
        FValue : Byte;
    public
        constructor Create;
        function Evaluate(B : Byte) : Byte; overload;
        function Evaluate(B : TByteArray) : Byte; overload;
        property CurrentValue : Byte read FValue write FValue;
    end;

    // Вычислитель CRC16
    TCRC16Calc = class
    private
        FValue : Word;
    public
        constructor Create;
        function Evaluate(B : Byte) : Word; overload;
        function Evaluate(B : TByteArray) : Word; overload;
        function Evaluate(B : TByteArray; Position, Count : Integer) : Word; overload;
        property CurrentValue : Word read FValue write FValue;
    end;

implementation

const
    crc8InitValue = $DE;
    crc8tab : array[0..255] of byte = (
        $00, $5E, $BC, $E2, $61, $3F, $DD, $83, $C2, $9C, $7E, $20, $A3, $FD, $1F, $41,
        $9D, $C3, $21, $7F, $FC, $A2, $40, $1E, $5F, $01, $E3, $BD, $3E, $60, $82, $DC,
        $23, $7D, $9F, $C1, $42, $1C, $FE, $A0, $E1, $BF, $5D, $03, $80, $DE, $3C, $62,
        $BE, $E0, $02, $5C, $DF, $81, $63, $3D, $7C, $22, $C0, $9E, $1D, $43, $A1, $FF,
        $46, $18, $FA, $A4, $27, $79, $9B, $C5, $84, $DA, $38, $66, $E5, $BB, $59, $07,
        $DB, $85, $67, $39, $BA, $E4, $06, $58, $19, $47, $A5, $FB, $78, $26, $C4, $9A,
        $65, $3B, $D9, $87, $04, $5A, $B8, $E6, $A7, $F9, $1B, $45, $C6, $98, $7A, $24,
        $F8, $A6, $44, $1A, $99, $C7, $25, $7B, $3A, $64, $86, $D8, $5B, $05, $E7, $B9,
        $8C, $D2, $30, $6E, $ED, $B3, $51, $0F, $4E, $10, $F2, $AC, $2F, $71, $93, $CD,
        $11, $4F, $AD, $F3, $70, $2E, $CC, $92, $D3, $8D, $6F, $31, $B2, $EC, $0E, $50,
        $AF, $F1, $13, $4D, $CE, $90, $72, $2C, $6D, $33, $D1, $8F, $0C, $52, $B0, $EE,
        $32, $6C, $8E, $D0, $53, $0D, $EF, $B1, $F0, $AE, $4C, $12, $91, $CF, $2D, $73,
        $CA, $94, $76, $28, $AB, $F5, $17, $49, $08, $56, $B4, $EA, $69, $37, $D5, $8B,
        $57, $09, $EB, $B5, $36, $68, $8A, $D4, $95, $CB, $29, $77, $F4, $AA, $48, $16,
        $E9, $B7, $55, $0B, $88, $D6, $34, $6A, $2B, $75, $97, $C9, $4A, $14, $F6, $A8,
        $74, $2A, $C8, $96, $15, $4B, $A9, $F7, $B6, $E8, $0A, $54, $D7, $89, $6B, $35
        );
    crc16InitValue = $00DE;
    crc16tab : array[0..255] of Word = (
        $0000, $1189, $2312, $329B, $4624, $57AD, $6536, $74BF,
        $8C48, $9DC1, $AF5A, $BED3, $CA6C, $DBE5, $E97E, $F8F7,
        $1081, $0108, $3393, $221A, $56A5, $472C, $75B7, $643E,
        $9CC9, $8D40, $BFDB, $AE52, $DAED, $CB64, $F9FF, $E876,
        $2102, $308B, $0210, $1399, $6726, $76AF, $4434, $55BD,
        $AD4A, $BCC3, $8E58, $9FD1, $EB6E, $FAE7, $C87C, $D9F5,
        $3183, $200A, $1291, $0318, $77A7, $662E, $54B5, $453C,
        $BDCB, $AC42, $9ED9, $8F50, $FBEF, $EA66, $D8FD, $C974,
        $4204, $538D, $6116, $709F, $0420, $15A9, $2732, $36BB,
        $CE4C, $DFC5, $ED5E, $FCD7, $8868, $99E1, $AB7A, $BAF3,
        $5285, $430C, $7197, $601E, $14A1, $0528, $37B3, $263A,
        $DECD, $CF44, $FDDF, $EC56, $98E9, $8960, $BBFB, $AA72,
        $6306, $728F, $4014, $519D, $2522, $34AB, $0630, $17B9,
        $EF4E, $FEC7, $CC5C, $DDD5, $A96A, $B8E3, $8A78, $9BF1,
        $7387, $620E, $5095, $411C, $35A3, $242A, $16B1, $0738,
        $FFCF, $EE46, $DCDD, $CD54, $B9EB, $A862, $9AF9, $8B70,
        $8408, $9581, $A71A, $B693, $C22C, $D3A5, $E13E, $F0B7,
        $0840, $19C9, $2B52, $3ADB, $4E64, $5FED, $6D76, $7CFF,
        $9489, $8500, $B79B, $A612, $D2AD, $C324, $F1BF, $E036,
        $18C1, $0948, $3BD3, $2A5A, $5EE5, $4F6C, $7DF7, $6C7E,
        $A50A, $B483, $8618, $9791, $E32E, $F2A7, $C03C, $D1B5,
        $2942, $38CB, $0A50, $1BD9, $6F66, $7EEF, $4C74, $5DFD,
        $B58B, $A402, $9699, $8710, $F3AF, $E226, $D0BD, $C134,
        $39C3, $284A, $1AD1, $0B58, $7FE7, $6E6E, $5CF5, $4D7C,
        $C60C, $D785, $E51E, $F497, $8028, $91A1, $A33A, $B2B3,
        $4A44, $5BCD, $6956, $78DF, $0C60, $1DE9, $2F72, $3EFB,
        $D68D, $C704, $F59F, $E416, $90A9, $8120, $B3BB, $A232,
        $5AC5, $4B4C, $79D7, $685E, $1CE1, $0D68, $3FF3, $2E7A,
        $E70E, $F687, $C41C, $D595, $A12A, $B0A3, $8238, $93B1,
        $6B46, $7ACF, $4854, $59DD, $2D62, $3CEB, $0E70, $1FF9,
        $F78F, $E606, $D49D, $C514, $B1AB, $A022, $92B9, $8330,
        $7BC7, $6A4E, $58D5, $495C, $3DE3, $2C6A, $1EF1, $0F78
        );


//==============================================================================
{ TCRC8Calc }

constructor TCRC8Calc.Create;
begin
    FValue := crc8InitValue;
end;
//------------------------------------------------------------------------------

function TCRC8Calc.Evaluate(B : Byte) : Byte;
begin
    FValue := crc8tab[FValue xor B];
    Result := FValue;
end;
//------------------------------------------------------------------------------

function TCRC8Calc.Evaluate(B : TByteArray) : Byte;
var
    i : Integer;
begin
    for i := Low(B) to High(B) do
        Evaluate(B[i]);
    Result := FValue;
end;
//==============================================================================
{ TCRC16Calc }

constructor TCRC16Calc.Create;
begin
    FValue := crc16InitValue;
end;
//------------------------------------------------------------------------------

function TCRC16Calc.Evaluate(B : Byte) : Word;
begin
    FValue := crc16tab[Byte(FValue) xor B] xor Byte(FValue shr 8);
    Result := FValue;
end;
//------------------------------------------------------------------------------

function TCRC16Calc.Evaluate(B : TByteArray) : Word;
var
    i : Integer;
begin
    for i := Low(B) to High(B) do
        Evaluate(B[i]);
    Result := FValue;
end;
//------------------------------------------------------------------------------

function TCRC16Calc.Evaluate(B : TByteArray; Position, Count : Integer) : Word;
var
    i : Integer;
begin
    for i := 0 to Count - 1 do
        Evaluate(B[i + Position]);
    Result := FValue;
end;
//==============================================================================
end.


Стандартное приводить не буду - оно очевидно
singlskv
Цитата(SasaVitebsk @ Sep 21 2007, 22:26) *
Прошу прощение что на дельфях, но я думаю для большинства это не проблема.
На дельфи никогда не писал, но паскаль вроде знал неплохо...
Цитата
Главное не реализация а само чуство объекта.
Вызывает сомнение "чуство объекта" у автора.
Цитата
Итак может ли мне кто-нибудь наглядно пояснить в чём (в приведенном примере) объектная реализация удобнее (или какие преимущества имеет).
Ничем,
собственно классы здесь притянуты за уши.
Все функции члены которые ссылаются на глобалные массивы и глобальные начальные
переменные должны будут переписаны в производном классе если он будет иметь
другой полином...
А если этот класс не предполагает наследования, нафига там функции члены определены
как перегружаемые ?

P.S. Если чего напутал в дельфях, прошу прощения...
singlskv
Цитата(dxp @ Sep 21 2007, 09:50) *
Под джиттером, связанным с прерываниями, в МК обычно понимают разброс латентности перехода к прерываниям. Какая связь с объктами классов, да еще и вызываемых из прерываний (т.е. когда переход к прерыванию уже произошел), мне не понятно.
джиттер это не толко разброс min/max тактов MCU при входе в прерывание,
для конкретной задачи куда более важным является разброс в min/max тактов
между возникновением события и реакцией на него.
В общем случае, этот разброс для проги на С++ ,будет больше...,
а в частных случаях, если не пользоваться C++ функциональностью(то есть пишем на C++ как на С),
результат будет точно таким же как в С.
Тока тогда нужно говорить примерно так: Пишу проги С++... на С.
dxp
Цитата(singlskv @ Sep 22 2007, 03:44) *
джиттер это не толко разброс min/max тактов MCU при входе в прерывание,
для конкретной задачи куда более важным является разброс в min/max тактов
между возникновением события и реакцией на него.
В общем случае, этот разброс для проги на С++ ,будет больше...,
а в частных случаях, если не пользоваться C++ функциональностью(то есть пишем на C++ как на С),
результат будет точно таким же как в С.
Тока тогда нужно говорить примерно так: Пишу проги С++... на С.

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

Цитата(SasaVitebsk @ Sep 22 2007, 01:26) *
Да вот именно с этим у меня и проблемы пока. sad.gif

Книги перечитывал уже не раз. И с Delfi работаю давно. Вроде всё понимаю и основные понятия объектного программирования чётко знаю, но пока на душу не легло.

Как zltigo где-нибудь в графике применить бы смог. Там это понятно. Но создание объекта "фильтр" пока из души не идёт и кажется притянутым за уши. (мне естественно. Я совершенно не утверждаю что это неправильно). Вот, к слову (извините за отступление, но уж если начали) прислал мне один молодой программист текст проги. Я ему слегка завидую, потому что я бы такое не написал не в жизь. Прошу прощение что на дельфях, но я думаю для большинства это не проблема. Главное не реализация а само чуство объекта. Итак может ли мне кто-нибудь наглядно пояснить в чём (в приведенном примере) объектная реализация удобнее (или какие преимущества имеет).

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

Попробуйте думать над программой на уровне объектов реального мира, это совсем не сложно, тогда реализация ляжет на классы почти сама собой. smile.gif Например, упомянутый вами фильтр является вполне законченным и целостным объектом реального мира, даже не вникая в его суть. У него есть вход, есть выход, есть (опционально) настроечные параметры-функции. Исходя из этого, даже не вникая во внутренности, можно написать:

Код
typedef TFilterIn ...;   // входной объект для фильтра
typedef TFilterOut ...;  // выходной объект фильтра

class TFilter
{
public:
   TFilter();    // конструктор по умолчанию, создает объект фильтра с параметрами по умолчанию
   TFilter(...); // 1-й конструктор с параметрами - создает объект фильтра с какими-то спец. свойствами
    ...
   TFilter(...); N- й конструктор с параметрами - создает объект фильтра с какими-то спец. свойствами

    void in(TFilterIn x); // принимает объект для фильтрации
    TFilterOut out();     // отдает результат фильтрации  
    void execute();       // выполняет собственно фильтрацию


private:
   ...
}


Конечно, это псевдокод, в реале все может быть гораздо скромнее (не иметь обилия конструкторов) или наоборот гораздо обширнее (скажем, иметь пачку функций для настройки параметров фильтра). В частности, вместо трех функций in(), out(), execute() скорее всего можно оставить одну:

Код
TFilterOut execute(TFilterIn);

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


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

Код
TFilter Filter;
TFilterIn fi = ...;
...
TFilterOut fo = Filter << fi;


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

Развивая вышесказанное, следует отметить, что С++ позволяет легко сместить акцент на проектирование программы. Если хорошо представляете себе предметную область, в контексте которой разрабатывается программа, то можно взять лист бумаги (хотя лучше это делать в каком-нить редакторе вроде Visio smile.gif ) и нарисовать на нем структурную схему, в которой квадратиками обозначить все имеющиеся прототипы объектов реального мира, реализуемых в программе - это будут классы, а связи (стрелки) между квадратиками - это не что иное, как открытые (public) функции-члены этих классов, т.е. их интерфейс. Далее можно уже просто спокойно по этой схеме набивать "скелет" программы, т.к. он фактически на этой структурной схеме представлен в графическом виде. Таким образом, подход к проектированию программы во значительной мере является формализованным, что весьма облегчает процесс ее разработки.

Безусловно, все это можно (и нужно) делать и при использовании С и даже ассемблера, но делать там это значительно труднее, т.к. языки не поддерживают напрямую концепцию объектов, и там придется прикладывать усилия для того, чтобы логически держать разрозненные переменные встроенных типов и функции, с ними связанные, вместе. При некотором объеме программы делать это станет фактически невозможным, хотя чем больше программа, тем более актуальным становится объектный подход.
SasaVitebsk
Большое спасибо за развёрнутый совет. Похоже надо просто садится и писать. Во всяком случае пробовать. А то по книжкам всё равно не дойдёт. smile.gif В книгах детали. Перебороть себя и начинать работать.

Рыбаки говорят, что для того чтобы поймать рыбу на мормышку без мотыля, его просто нельзя брать с собой. Чтобы соблазна/выбора не было. Ну а поймав первую....
smile.gif
singlskv
Цитата(dxp @ Sep 22 2007, 20:52) *
, давайте посмотрим на конкретном примере.
Давайте,
код на Gcc для AVR:
Код
// регистровые переменные для работы прерывания системного таймера

register BYTE ADCL_            asm("r2");
register BYTE ADCH_            asm("r3");
register BYTE ADMUX_           asm("r4");
register BYTE ADCSRA_          asm("r5");
register BYTE SYSTICKSTART_    asm("r6");
register BYTE PINB_            asm("r7");
register BYTE PINC_            asm("r8");
register BYTE PIND_            asm("r9");

//-------------------------------------------------------------------
// Прерывание системного таймера
//   используются только регистровые переменные
//   никакие регистры не сохраняются
//   SREG не сохраняется
//-------------------------------------------------------------------

void TIMER2_COMP_vect(void) __attribute__((signal)) __attribute__((naked));
void TIMER2_COMP_vect()
{
  ADCL_  = ADCL;       // читаем результат последнего преобразования
  ADCH_  = ADCH;
  ADMUX  = ADMUX_;     // новый канал
  ADCSRA = ADCSRA_;    // запускаем новое преобразование
                       // информируем о начале нового цикла
  SYSTICKSTART_= ADCSRA_;     // записью значения ADCSRA в SYSTICKSTART_
                              // сбрасывается в 0 в основном цикле
  PINB_  = PINB;       // читаем порты
  PINC_  = PINC;
  PIND_  = PIND;
  __asm__ __volatile__ ("reti" ::);  // выходим
}
8 команд + вход и выход, джиттер 1 такт
Конечно это не голый C, но для микроконтртроллеров он и не бывает без
всяких примочек типа прагм, указаний где лежат данные, и т.д.

Цитата
Безусловно, все это можно (и нужно) делать и при использовании С и даже ассемблера, но делать там это значительно труднее, т.к. языки не поддерживают напрямую концепцию объектов, и там придется прикладывать усилия для того, чтобы логически держать разрозненные переменные встроенных типов и функции, с ними связанные, вместе. При некотором объеме программы делать это станет фактически невозможным, хотя чем больше программа, тем более актуальным становится объектный подход.
Интересно, по Вашему линух это достаточно большая программа для того что бы ее
уже необходимо было писать на С++ ?
Почему авторы предпочли писать линух на С ?
zltigo
Цитата(singlskv @ Sep 22 2007, 20:49) *
Конечно это не голый C...

Это вообще не 'C'. По сравнению с этими телодвижениями заколачивание гвоздей микроскопом выглядят более чем разумно sad.gif.
Цитата
но для микроконтртроллеров он и не бывает без
всяких примочек типа прагм, указаний где лежат данные, и т.д.

Позвольте об этом судить тем, кто умеет пользоваться инструментом.
dxp
Цитата(singlskv @ Sep 23 2007, 00:49) *
Давайте,
код на Gcc для AVR:
Код
// регистровые переменные для работы прерывания системного таймера

register BYTE ADCL_            asm("r2");

[...]

  PINC_  = PINC;
  PIND_  = PIND;
  __asm__ __volatile__ ("reti" ::);  // выходим
}
8 команд + вход и выход, джиттер 1 такт
Конечно это не голый C, но для микроконтртроллеров он и не бывает без
всяких примочек типа прагм, указаний где лежат данные, и т.д.

Во-первых, как уже сказано, это не С. Это асм, обернутый в синтаксис С. В таком стиле проще это все написать на ассемблере - писанины меньше, возни с расширениями С никакой.

Во-вторых, я не понял из этого примера, что вы хотели показать по сравнению с С++? Где и на чем тут С++ должен был "слить"? Пример не то, чтобы неудачный, просто никакой (мимо вопроса).

Цитата(singlskv @ Sep 23 2007, 00:49) *
Интересно, по Вашему линух это достаточно большая программа для того что бы ее
уже необходимо было писать на С++ ?
Почему авторы предпочли писать линух на С ?

Уверены, что современные линухи целиком написаны на С? smile.gif Поинтересуйтесь, какая доля голого С там присутствует, даже если драйвера уже давно пишут на плюсах. А то, что начинался он на С, так это понятно - тогда ни Стандарта еще не было на С++, ни компиляторов приличных (стабильных и эффективных).
singlskv
Цитата(zltigo @ Sep 22 2007, 22:11) *
Это вообще не 'C'.
Это С для микроконтроллеров, в частности Gcc для AVR.
Цитата
По сравнению с этими телодвижениями заколачивание гвоздей микроскопом выглядят более чем разумно sad.gif.
Вы хотели сказать что неоптимальненько написан данный кусок ? biggrin.gif
Ждем ваших решений.... 07.gif на С++ biggrin.gif

Код Си библиотек написаный на С смотрели ?
Там таких микроскопов завались smile.gif

З.Ы. этот код это единственное непереносимое место в проге, зато все очень быстро и без
заметного джиттера.

Цитата(zltigo @ Sep 20 2007, 10:10) *
Для батарейных девайсов на первый план выходит время вхождение в прерывание? Даже не подозревал sad.gif.
Нет, но вскоре видимо буду. Предстоит замена-Upgrade одного старинного электомеханического девайса. Причем AVR там стоять не будет - слишком в полуспящем режиме батарейка маленькая и задачи опять-таки не битики сбрасывать, а все-таки немножко в цифре фильтровать, LCD управлять опят-таки нужно.....

Цитата
Позвольте об этом судить тем, кто умеет пользоваться инструментом.
Вот именно, ТЕМ КТО УМЕЕТ пользоваться инструментом и выжать из него
все что только можно.

немножко в цифре фильтровать...
LCD опять-таки управлять...
Несомненно для этой этой задачки нужен ARM мегагерц на 200 и С++ lol.gif


Цитата(dxp @ Sep 22 2007, 22:56) *
Во-первых, как уже сказано, это не С. Это асм, обернутый в синтаксис С. В таком стиле проще это все написать на ассемблере - писанины меньше, возни с расширениями С никакой.
Ну я же сразу же предупредил что это С в приложении к микроконтроллеру.
На асм не проще по той причине что дальше в проге идет переработка данных
полученных в прерывании и выгодно их хранить в виде С переменных но хранящихся в регистрах.
Цитата
Во-вторых, я не понял из этого примера, что вы хотели показать по сравнению с С++? Где и на чем тут С++ должен был "слить"? Пример не то, чтобы неудачный, просто никакой (мимо вопроса).
Ну лень было придумывать и писать какой-нить пример где С++ сольет,
вот и взял первое что под руку подвернулось smile.gif
Тока давайте сразу же уточним что под С++ я подразумеваю хотя бы
использование классов, разницу в процедурной реализации на С и С++ рассматривать просто
безсмысленно...
Цитата
Уверены, что современные линухи целиком написаны на С? smile.gif Поинтересуйтесь, какая доля голого С там присутствует, даже если драйвера уже давно пишут на плюсах. А то, что начинался он на С, так это понятно - тогда ни Стандарта еще не было на С++, ни компиляторов приличных (стабильных и эффективных).

Эээ..., ну как Вам сказать ....
Нет, ну конечно Вы правы, там еще довольно много платформозависимого кода на асм.

З.Ы. Время от времени пересобираю линух для AVR32, кода на С++ пока не обнаружил smile.gif
zltigo
Цитата(singlskv @ Sep 22 2007, 22:00) *
Вот именно, ТЕМ КТО УМЕЕТ пользоваться инструментом и выжать из него
все что только можно.

Если Вы намекаете на свои исходники, то это не умение, это нагромождение голых трюков ясно показывающее неумение нормально продумывать решение задачи. Об этом-же говорят еще и полученные Вами дикие (2mks) цифры реакции на прерывание (да и еще до кучи к нему нечто называемое Вами джиттером того-же порядка), при попытке написать что-то более серьезное, нежели перекладывание байтиков.
Цитата
этот код это единственное непереносимое место в проге,

Отнюдь, использование регистров глобально, наносит неизгладимый отпечаток на весь код.
Цитата
Несомненно для этой этой задачки нужен ARM мегагерц на 200 и С++

Нет, хватит микромощного 16-ти битовика, С (С++ по настроению) и немного чистого ASM, там, где нужно.
singlskv
Цитата(zltigo @ Sep 22 2007, 23:33) *
Если Вы намекаете на свои исходники, то это не умение, это нагромождение голых трюков ясно показывающее неумение нормально продумывать решение задачи. Об этом-же говорят еще и полученные Вами дикие (2mks) цифры реакции на прерывание (да и еще до кучи к нему нечто называемое Вами джиттером того-же порядка)
Вы похоже меня с кем то перепутали, читайте внимательнее посты,
у меня джиттер несколко получше, 1 такт процессора, при 16Мгц это ~60нс
Цитата
, при попытке написать что-то более серьезное, нежели перекладывание байтиков.
Ну если говорить об этой проге, то там не только "перекладывание байтиков".
Там еще и 16 входных цифровых каналов с фильтрацией практически произвольно перемешанные
по PINB, PINC и PIND, 2 канала ADC с фильтрацией, 1-2 PWM, SPI, ну и еще
обмен на 400КГц по I2C (реальная скорость передачи ~25Кбайт/с.
Конечно перекладывание байтиков, но байтиков было много... smile.gif
Цитата
Отнюдь, использование регистров глобально, наносит неизгладимый отпечаток на весь код.
Ну как Вам сказать, реально задействованными оказались только R0-R9 и R14-R31,
R10-R13 компилятору просто не понадобились.
Цитата
Нет, хватит микромощного 16-ти битовика, С (С++ по настроению) и немного чистого ASM, там, где нужно.
MSP430 ?
Правильная AVR при правильном использовании тоже бы подошла,
тока Вы их похоже просто принципиально не используете, даже если для
конкретной задачки их использование будет выгоднее... smile.gif
zltigo
Цитата(singlskv @ Sep 22 2007, 23:09) *
Вы похоже меня с кем то перепутали,

Простите, это действительно так sad.gif
Цитата
у меня джиттер несколко получше, 1 такт процессора, при 16Мгц это ~60нс

Не все команды AVR за один такт выполняются, без вызовов/возвратов Вы едва-ли обошлись, по этой причине 4 такта
процессора будут реальной величиной.
Цитата
Правильная AVR при правильном использовании тоже бы подошла,

Нет.
- Режимы энергосбережения много лучше;
- Контроллер LCD;
- 16bit много предпочтительнее для работы с 12bit значениями.
Посколько я не связан обязательствами использовать какой-либо один контроллер и способен достаточно легко переходить, то могу выбирать не только среди "правильных", но и "самых правильных" smile.gif.
singlskv
Цитата(zltigo @ Sep 23 2007, 00:39) *
- 16bit много предпочтительнее для работы с 12bit значениями.
Посколько я не связан обязательствами использовать какой-либо один контроллер и способен достаточно легко переходить, то могу выбирать не только среди "правильных", но и "самых правильных" smile.gif.
Если нужно много достаточно быстрых ADC преобразований, то тогда
MSP c его 16 регистрами памяти ADC конечно рулит,
тока асм там точно будет нужен...

Обязательствами я тоже не очень связан. smile.gif


Цитата(zltigo @ Sep 23 2007, 00:39) *
Не все команды AVR за один такт выполняются, без вызовов/возвратов Вы едва-ли обошлись, по этой причине 4 такта
процессора будут реальной величиной.
Да, Вы правы, RET действительно присутствует, так что джиттер 4-1=3 такта.
Просто в первоначальной реализации, в момент возникновения прерывания, у меня были
команды максимум в 2 такта, но потом по некоторым причинам пришлось переделать и
вставить вызов функции.
zltigo
Цитата(singlskv @ Sep 23 2007, 00:13) *
Да, Вы правы, RET действительно присутствует, так что джиттер 4-1=3 такта.

И без "-1", ибо при удачном стечении обстоятельств ждать окончания текущей команды вообще не придется, а при неудачном все 4 такта.


Цитата(singlskv @ Sep 23 2007, 00:13) *
тока асм там точно будет нужен...

ASM проблем нет, если надо. А из нужного, еще неплохая аналоговая начинка присутствует.
singlskv
Да, далеко мы уже удалились... от темы,
единственно что радует, модератор вряд ли забанит за оффтоп smile.gif
Цитата(zltigo @ Sep 23 2007, 01:36) *
И без "-1", ибо при удачном стечении обстоятельств ждать окончания текущей команды вообще не придется, а при неудачном все 4 такта.
ASM проблем нет, если надо. А из нужного, еще неплохая аналоговая начинка присутствует.
Т.к прерывание синхронное (таймер), джиттер имеет смысл рассчитывать только при
попадании на середину команды, то есть максимумтактов-1.
Если бы все команды были однотактовые то джиттера небыло бы вобще.
zltigo
Цитата(singlskv @ Sep 23 2007, 00:47) *
Т.к прерывание синхронное (таймер)..

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


Цитата(singlskv @ Sep 23 2007, 00:47) *
Да, далеко мы уже удалились... от темы,

Но не от "джиттера" smile.gif.
Продолжения про "джиттер" вносимый плюсами будет?
dxp
Цитата(singlskv @ Sep 23 2007, 02:21) *
Это С для микроконтроллеров, в частности Gcc для AVR.

С - это С. Не бывает С для микроконтроллеров. Бывают расширения компилятора для конкретной платформы.

Цитата(singlskv @ Sep 23 2007, 02:21) *
Вы хотели сказать что неоптимальненько написан данный кусок ? biggrin.gif
Ждем ваших решений.... 07.gif на С++ biggrin.gif

Звучит примерно так: есть код

out PORTB, r16

Ждем реализацию на С++. biggrin.gif

Цитата(singlskv @ Sep 23 2007, 02:21) *
Ну я же сразу же предупредил что это С в приложении к микроконтроллеру.

См выше.

Цитата(singlskv @ Sep 23 2007, 02:21) *
На асм не проще по той причине что дальше в проге идет переработка данных
полученных в прерывании и выгодно их хранить в виде С переменных но хранящихся в регистрах.

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

Цитата(singlskv @ Sep 23 2007, 02:21) *
Ну лень было придумывать и писать какой-нить пример где С++ сольет,
вот и взял первое что под руку подвернулось smile.gif
Тока давайте сразу же уточним что под С++ я подразумеваю хотя бы
использование классов, разницу в процедурной реализации на С и С++ рассматривать просто
безсмысленно...

Так мы сравниваем С vs. C++ или асм (хотя и в фантике из синтаксиса С) vs. С++? Давайте нормальный С код, а не хаки.


Цитата(SasaVitebsk @ Sep 23 2007, 00:17) *
Похоже надо просто садится и писать. Во всяком случае пробовать. А то по книжкам всё равно не дойдёт. smile.gif В книгах детали. Перебороть себя и начинать работать.

Истинно так! Сразу, конечно, не так все гладко будет получаться, как на словах, тут некоторая практика нужна (как и в любом деле), но через короткое время все придет. То самое "чувство объекта". После этого по-старому (по процедурному) уже думать вряд ли захочется. smile.gif

Цитата(singlskv @ Sep 23 2007, 04:13) *
Если нужно много достаточно быстрых ADC преобразований, то тогда
MSP c его 16 регистрами памяти ADC конечно рулит,
тока асм там точно будет нужен...

Не более, чем для AVR.

Цитата(singlskv @ Sep 23 2007, 04:13) *
Обязательствами я тоже не очень связан. smile.gif
Да, Вы правы, RET действительно присутствует, так что джиттер 4-1=3 такта.
Просто в первоначальной реализации, в момент возникновения прерывания, у меня были
команды максимум в 2 такта, но потом по некоторым причинам пришлось переделать и
вставить вызов функции.

Я что-то не пойму - в программе только одно прерывание? Других нет? Если есть еще прерывания, то про какие один-два-три-четые такта джиттера входа в данное прерывание может идти речь?
dxp
Цитата(SasaVitebsk @ Sep 23 2007, 00:17) *
А то по книжкам всё равно не дойдёт. smile.gif В книгах детали.

Дополнение по поводу книг. Обязательно почитайте Гради Буча (Grady Booch) "Объектно-ориентированные анализ и проектирование", там глава 2 "Объектная модель" и глава 3 "Классы и объекты" прямо как раз для вас написаны. Там все то, что я сказал расписано подробнейшим образом с хорошими примерами. Книжка хорошая, написана конкретно, читаеся легко.

А в книжках по С++ действительно львиная доля посвящена деталям языка. Видимо имеется в виду, что читатель с общими концепциями объектного и объектно-ориентированного программирования уже знаком. Хотя в основополагающем труде Б.Страуструпа "Язык программирования С++" целый раздел посвящен вопросам проектирования и вообще подходу к разработке ПО. Очень интересная глава даже если не быть большим специалистом по ++. Глава, кажется, 11, называется "Проектирование и развитие". Там тоже рассматриваются все эти вопросы, причем непосредственно в контексте С++. Настоятельно рекомендую.
singlskv
Цитата(dxp @ Sep 23 2007, 12:59) *
А если писать на С, то надо предоставить компилятору оптимизировать, включив соответствующую оптимизацию.
Хорошо, почти убедили, конечно это в некоторой степени хак,
НО, без этого хака производительность всей проги будет существенно ниже.
Компилятору таки нужно помагать делать правильный код.
Цитата
Так мы сравниваем С vs. C++ или асм (хотя и в фантике из синтаксиса С) vs. С++? Давайте нормальный С код, а не хаки.
Давайте поступим чуть по другому, Вы приведете пару примеров своего кода
на С++(как минимум с классами) для обслуживания прерываний(я так понимаю что Вы этим пользуетесь ?), ну и мы обсудидим как это можно было бы написать на С.
Дело в том что в прерываниях я никогда не использовал С++ код и мне сложно даже
предположить вариант такого использования при котором я получу какой-то выигрыш...
Цитата
Не более, чем для AVR.
Честно говоря не понял что Вы имели в виду под "не более".
Цитата
Я что-то не пойму - в программе только одно прерывание? Других нет? Если есть еще прерывания, то про какие один-два-три-четые такта джиттера входа в данное прерывание может идти речь?

Да, в этой проге только одно прерывание, все остальное реализуется поллингом,
т.е. обмен с внешней переферией по SPI, запись в EEPROM и быстрый обмен по i2c в режиме slave...
Так что с джиттерами там все точно smile.gif

P.S. Да, и если не сложно, дайте ссылочку про использование С++ для драйверов и
кода ядра линух, интересно оценить почему и зачем они там С++ использовали.


Цитата(zltigo @ Sep 23 2007, 02:22) *
Синхронность в данном случае дело абсолютно темное, поведение будет зависеть от сдвига и без знания потрохов судить о поведении невозможно.
zltigo, не юлите, даташит нам дает однозначный ответ на этот вопрос, джиттер
начнет возникать тока в момент непопадания момента прерывания на однотактовую команду.
dxp
Цитата(singlskv @ Sep 23 2007, 21:35) *
Хорошо, почти убедили, конечно это в некоторой степени хак,
НО, без этого хака производительность всей проги будет существенно ниже.
Компилятору таки нужно помогать делать правильный код.

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

Цитата(singlskv @ Sep 23 2007, 21:35) *
Давайте поступим чуть по другому, Вы приведете пару примеров своего кода
на С++(как минимум с классами) для обслуживания прерываний(я так понимаю что Вы этим пользуетесь ?), ну и мы обсудим как это можно было бы написать на С.

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

1) определена она в пространстве имен класса;
2) имеет доступ к закрытым (private) членам своего класса.

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

Цитата(singlskv @ Sep 23 2007, 21:35) *
Дело в том что в прерываниях я никогда не использовал С++ код и мне сложно даже
предположить вариант такого использования при котором я получу какой-то выигрыш...

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

Короче. Дискуссия вокруг да около меня что-то уже утомила, поэтому "раскрою карты" (скажу прямо):
  1. основой эффективного писания на С является интенсивное использование составных типов данных (массивы, структуры), указателей, адресной арифметики;
  2. исходя из этого, для того чтобы С код эффективно "ложился" на аппаратуру процессора, от последнего требуются средства поддержки косвенной адресации и адресной арифметики (аппаратные указатели, индексные регистры, режимы обращения со смещением и т.д.);
  3. ключевым понятием С++ является класс;
  4. механизм работы с представлением объектов классов осуществляется на основе указателя this, который содержит адрес "структуры" членов-данных класса;
  5. из этого следует, что на любом процессоре, который имеет сколько-нибудь достаточно развитые средства косвенной адресации и адресной арифметики, код С++ получится по эффективности таким же, как и на С - близким к написанному вручную (имеется в виду код общего назначения - разного рода процессорно-зависимые навороты (типа multifunction instructions), реализуемые на асме, не рассматриваются);
Т.е. класс как таковой не несет в себе ничего такого, что бы нивелировало эффективность кодогенерации по сравнению с С. Вот что я и имел в виду на протяжении всего своего участия в данной теме. Вплоть до того, что если, скажем, компилятору известен адрес объекта класса на этапе компиляции, то ничего ему не мешает сгенерировать прямое (а не косвенное) обращение к члену-данному, если оное в конкретном случае оказывается эффективнее.


Цитата(singlskv @ Sep 23 2007, 21:35) *
Честно говоря не понял что Вы имели в виду под "не более".

Ну, вы упомянули, что для MSP430 потребуется асм для достижения эффективности, на это я и высказался, что не более, чем для AVR. При равной тактовой MSP430 на обработке данных быстрее, чем AVR. У MSP430 медленнее обращение по шинам из-за его фон Неймановости, но за счет 16-разрядности и более прямому ядру он обходит AVR. Только прошу тут тему AVR vs MSP430 не развивать, не топик тут это, да и высказывался я уже подробнейшим образом на эту тему, анализируя "кривые" моменты ядра AVR. Повторять не хочется.

Цитата(singlskv @ Sep 23 2007, 21:35) *
Да, в этой проге только одно прерывание, все остальное реализуется поллингом,
т.е. обмен с внешней переферией по SPI, запись в EEPROM и быстрый обмен по i2c в режиме slave...
Так что с джиттерами там все точно smile.gif

Ясно. Случай опять же вырожденный. Все построено в угоду одному источнику поступления данных. IMHO, если в программе нужно ловить каждый такт (т.е. это значимо), то тут явно что-то не то на уровне проекта. Исключением является случай, когда тираж изделий составляет десятки тысяч и при этом стоимость процессора заметно "весит" в BOM, т.ч. оптимизация "производительность/цена" выходит на первый план т.к. экономия нескольких центов уже дает заметный экономический эффект.

Цитата(singlskv @ Sep 23 2007, 21:35) *
P.S. Да, и если не сложно, дайте ссылочку про использование С++ для драйверов и
кода ядра линух, интересно оценить почему и зачем они там С++ использовали.

В линухе не силен, но встречал людей на просторах FIDO (в частности в эхах su.c-cpp и ru.cpp), которые успешно писали драйвера для видны (vxd и sys) и линукса на С++. Это не удивило, т.к. объективных препятствий этому нет.
singlskv
Цитата(dxp @ Sep 23 2007, 20:10) *
То, что компилятору надо помогать, согласен. Но не с помощью хаков.
Ну давайте четко определимся, настоящий хак там был только в одном моменте, это
несохранение SREG за ненадобностью в этом коде...
Цитата
Если без хака не обойтись, это серьезный повод задуматься о применении асма в данном случае. А компилятору надо помогать предназначенными для этого средствами - рулить оптимизацией, а самое главное - использовать эффективные алгоритмы. Низкоуровневую оптимизацию надо предоставить делать компилятору. Программисту следует заниматься оптимизацией на уровне алгоритма - это то, что компиляторы делать достаточно эффективно пока не очень умеют. А уж по регистрам распихать и оптимизировать работу с ними - прямая прерогатива компилятора.
Пример ?
Цитата
Да что вы привязались к этим прерываниям? smile.gif Свет на них, что-ли, клином сошелся? smile.gif Ну, использую, ну и что? По эффективности это примерно так же, как и на С, и нет причин почему бы было по-другому. Если обработчик прерываний входит в класс в качестве функции-члена, то он является статической функцией-членом, ибо по-другому просто и быть не может. А статическая функция-член класса - это по сути и по уровню реализации та же самая обычная глобальная функция с той лишь разницей, что:
Да вроде с прерываний то все и началось smile.gif
Цитата
Я просто заметил что в прерываниях функциональность С++ обычно не используется...
Т.е. удобство главным образом состоит в поддержке инкапсуляции и абстракции - объект, содержащий внутри себя средства обработки событий, поступающих через это прерывание, остается целостным, его реализация снаружи не видна, общение с ним реализуется только через интерфейс (public члены). Реализация же прерывания в виде статической функции-члена по эффективности ровно та же, что и при реализации с помощью обычной функции.
Вот собственно об этом и была речь, и отсутствие С++ кода для работы в прерываниях тоже
о чем то говорит, нету кода, не о чем спорить...

Цитата
Ну, вы упомянули, что для MSP430 потребуется асм для достижения эффективности, на это я и высказался, что не более, чем для AVR. При равной тактовой MSP430 на обработке данных быстрее, чем AVR. У MSP430 медленнее обращение по шинам из-за его фон Неймановости, но за счет 16-разрядности и более прямому ядру он обходит AVR. Только прошу тут тему AVR vs MSP430 не развивать, не топик тут это, да и высказывался я уже подробнейшим образом на эту тему, анализируя "кривые" моменты ядра AVR. Повторять не хочется.
дык вроде никто и не спорит, про асм было упоминание тока в том смысле, что если
нужно бысто, то и асм там будет...
Цитата
В линухе не силен, но встречал людей на просторах FIDO (в частности в эхах su.c-cpp и ru.cpp), которые успешно писали драйвера для видны (vxd и sys) и линукса на С++. Это не удивило, т.к. объективных препятствий этому нет.
Дык может и писали, тока в майнстрим исходниках такого почему-то не наблюдал,
таки все таки почему???
zltigo
Цитата(singlskv @ Sep 23 2007, 17:35) *
zltigo, не юлите, даташит нам дает однозначный ответ на этот вопрос, джиттер
начнет возникать тока в момент непопадания момента прерывания на однотактовую команду.

Такие интимные подробности описаны? Типа, что фронт прерывания от встроеного таймера чисто конкретно приходится на конец выполнения команды а не на ее начало? Честно говоря совсем не верю. Цитату не дадите?
singlskv
Цитата(zltigo @ Sep 23 2007, 23:21) *
Такие интимные подробности описаны? Типа, что фронт прерывания от встроеного таймера чисто конкретно приходится на конец выполнения команды а не на ее начало? Честно говоря совсем не верю. Цитату не дадите?
Zltigo, вы знаете, собирался по честному процитировать Вам даташит, но уже не буду,
потому как видимо бессмысленно...
Чего то щелкнуло в голове и я решил пересмотреть Ваши высказывания:
Цитата(zltigo @ Sep 20 2007, 18:09) *
.....
Такие длинные групповые команды можно просто не использовать, тогда самая длинная это 8 тактов. Итого, при необходимости быстрой реакции на прерывание получаем 15 тактов.
......
Ну а дальше можно считать сколько времени займет вход в обработчик FIQ у типичного 60MHz ARM. Считаем. Максимальная задержка = 250ns, ну джиттер соответственно 7 тактов (причем независимо от IRQ/FIQ) = 117ns.

Вот мне и интересно, 8 - (8-1=7)тактов для АРМ это норма 07.gif а для AVR Вам нужна цитата ?08.gif
Как минимум, интересное замечание.
Если захотите оправдаться, не буду Вам мешать, но попробуйте сделать это как минимум достойно....
SasaVitebsk
Попробую я вмешаться. Я тоже делал много изделий с требованиями минимизации джиттера.

Если рассматривать джиттер входа в прерывание, то он зависит по сути только от разброса времени исполнения комманд МК. И больше не от чего. Иными словами для AVR это 3(4-1) такта без учёта работы с внешней памятью. Если рассматривается скорость реакции на прерывание (я бы это джиттером не назвал), то в рамках Си это будет зависеть от реализации п/п обработки прерывания. В частности от количества сохраненных регистров. А это напрямую зависит от сложности написания самого прерывания в том числе, что также очевидно, и применения программирования более высокого уровня C/C++, естественно при сравнении проги одного программиста. Если рассматривать, к примеру, дрожание принятия какого нибудь решения внутри прерывания относительно его начала, то это будет зависеть от линейности написания программы от начала прерывания до места принятия решения.

Так определитесь о каком джиттере идёт речь? Иначе дальнейшее обсуждение данного вопроса эээ... малоинформативно.
zltigo
Цитата(singlskv @ Sep 24 2007, 00:00) *
Zltigo, вы знаете, собирался по честному процитировать Вам даташит, но уже не буду,

Дело Ваше, можете и не по честному, мне без разницы.
Цитата
Вот мне и интересно, 8 - (8-1=7)тактов для АРМ это норма 07.gif а для AVR Вам нужна цитата ?08.gif

Да, это норма, ибо у ARMов имеются семь тактов до начала процесса вклинивания

Цитата(SasaVitebsk @ Sep 24 2007, 00:36) *
для AVR это 3(4-1) такта

Прерывание возникло за 1ns до окончания команды. Прерывание возниколо через одну наносекунду после начала 4x тактовой команды. Какая разница будет? Утверждается, что где-то документировано возниковение прерывания от внутреннего таймера сразу после начала исполнения команды, нежели такое документировано, то тогда действительно для случая внутренего таймера разница будет в 3 такта. В любом другом - 4.
Цитата
Иначе дальнейшее обсуждение данного вопроса эээ... малоинформативно.

Оно уже абсолютно не информативно для хоть какой-либо невырожденной системы, ибо наличие более одного прерывания, или критической секции, времена ммеют совершенно другой порядок.
Еще более неинформативны, точнее 100% бессысленны, рассуждения о "джиттере" из-за использования C++ затеяные singlskv.
singlskv
Цитата(zltigo @ Sep 24 2007, 01:52) *
Дело Ваше, можете и не по честному, мне без разницы.

Да, это норма, ибо у ARMов имеются семь тактов до начала процесса вклинивания.
Прерывание возникло за 1ns до окончания команды. Прерывание возниколо через одну наносекунду после начала 4x тактовой команды. Какая разница будет? Утверждается, что где-то документировано возниковение прерывания от внутреннего таймера сразу после начала исполнения команды, нежели такое документировано, то тогда действительно для случая внутренего таймера разница будет в 3 такта. В любом другом - 4.
Хм, а для АРМ это конечно все не так? да ?
Бред будем нести лишь бы не оказаться не правым?
ну-ну, и танк на встречу...


Цитата(zltigo @ Sep 24 2007, 01:52) *
после начала 4x тактовой команды. Какая разница будет? Утверждается, что где-то документировано возниковение прерывания от внутреннего таймера сразу после начала исполнения команды, нежели такое документировано, то тогда действительно для случая внутренего таймера разница будет в 3 такта. В бессысленны, рассуждения о "джиттере" из-за испо
льзования C++ затеяные singlskv.
Я вам советую очень сильно расслабится, я могу все команды обращения к внешним портам перевести на сттандартаные командыи доступа к портам
dxp
Цитата(singlskv @ Sep 24 2007, 00:20) *
Ну давайте четко определимся, настоящий хак там был только в одном моменте, это
несохранение SREG за ненадобностью в этом коде...

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

Цитата(singlskv @ Sep 24 2007, 00:20) *
Пример ?

Что "пример"?

Цитата(singlskv @ Sep 24 2007, 00:20) *
Да вроде с прерываний то все и началось smile.gif
Вот собственно об этом и была речь, и отсутствие С++ кода для работы в прерываниях тоже
о чем то говорит, нету кода, не о чем спорить...

На что вам код? Ну вот, нате, смотрите (написано было еще году в 2001-м, как только появились первые компиляторы ЕС++ от IAR для AVR):

Код
//------------------------------------------------------------------------------
class TUartCmd
{
public:
    TUartCmd(byte Baud_rate)
    {
        UBRR = Baud_rate;
        SetBits(UCR, ( (1 << RxEnable) | (1 << TxEnable)) );
        enableRxInt();
        Cmd = &fptrUartCmd;
        MaxCmdCode = 3;
    }

    enum TBlockCode
    {
        bcMsg,
        bcRead,
        bcWrite,
        bcLogOp,
        bcUser,
        bcDataFlash
    };

    static bool send(const byte* ptr, const byte count, TBlockCode block_code = bcUser);
    static void mgr();

private:
    #pragma vector = UART_UDRE_vect
    static __interrupt void UART_UDRE_interrupt();

    #pragma vector = UART_RX_vect
    static __interrupt void UART_Rx_interrupt();


    static __monitor byte lockUDREInt(); //  { byte ucr = TUartCmd::Control_Reg; disableUDREInt(); return ucr; }
    static __monitor void unlockUDREInt(byte ucr); // { TUartCmd::Control_Reg = ucr; }

protected:
    static byte IsFrameError()   { return USR & FrameError; }
    static byte IsOverRun()      { return USR & Rx_OverRun; }
    static byte IsUartRxErrors() { return USR & (Rx_OverRun | FrameError); }
    static void resetRx();

    static void msg(byte* ptr, byte count);
    static void read(byte* ptr, byte count);
    static void write(byte* ptr, byte count);
    static void log_op(byte* ptr, byte count);

protected:
    static void (*__flash(*Cmd)[])(byte* ptr, byte count);
    static byte MaxCmdCode;

private:
    static struct Flags
    {
        bool RxTimeout  : 1;
    } status;
    static __flash void (*fptrUartCmd[])(byte* ptr, byte count);

    static byte   RxTimer;
    static byte   RxBuf[C_BufferSize];
    static byte   RxIndex;
    static byte   TxBuf[C_BufferSize];
    static TCbuf  Tx;


private:
    enum
    {
        Rx_OverRun    = (1 << 0),
        Rx_FrameError = (1 << 1),
        Rx_BufOverRun = (1 << 2)
    };

    enum UART_Status_Register_Bits
    {
        RxComplete = 7,
        TxComplete = 6,
        UDREmpty   = 5,
        FrameError = 4,
        Overrun    = 3
    };

    enum UART_Control_Register_Bits
    {
        RxCompleteIE = 7,
        TxCompleteIE = 6,
        UDREmptyIE   = 5,
        RxEnable     = 4,
        TxEnable     = 3,
        Char9        = 2,
        RxBit8       = 1,
        TxBit8       = 0
    };

    enum TOpCodes
    {
        opcodeAND,
        opcodeOR,
        opcodeXOR
    };
};
//------------------------------------------------------------------------------
__interrupt void  TUartCmd::UART_UDRE_interrupt()
{
    if(TUartCmd::Tx.get_count())
    {
        UDR = TUartCmd::Tx.get();
    }
    else
        disableUDREInt();
}
//------------------------------------------------------------------------------
__interrupt void  TUartCmd::UART_Rx_interrupt()
{
    byte Symbol = UDR;
    //__enable_interrupt();

    if(IsUartRxErrors())
    {
        resetRx();
        return;
    }

    byte t = get_system_tick();
    byte dt = t - RxTimer;

    if(RxIndex)
    {
        if(dt > C_MaxRxSymbolTransferTime)
        {
            resetRx();
            return;
        }
    }

    RxTimer = t;
    RxBuf[RxIndex++] = Symbol;

    byte header = RxBuf[0];
    byte size   = (header & 0x0f) + 2;

    if(size == RxIndex)
    {
        byte code = header >> 4;

        RxIndex = 0;
        if(get_checksum(RxBuf, size))
        {
            resetRx();
            return;
        }

        if(code == 7) code = TUartCmd::bcUser; // patch
        if(code > TUartCmd::MaxCmdCode) return;

        (*(*TUartCmd::Cmd)[code])(RxBuf+1, size-2);
    }
}
//------------------------------------------------------------------------------

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

Код
class TUartCmdUsr : public TUartCmd
{
public:
    TUartCmdUsr(byte Baud_rate) : TUartCmd(Baud_rate)
    {
        Cmd = &fptrUartCmd;
        MaxCmdCode = 4;
    }
    static void user(byte* ptr, byte count);

private:
    static __flash void (*fptrUartCmd[])(byte* ptr, byte count);
};

Здесь добавлена еще одна функция-обработчик пакетов.

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

Цитата(singlskv @ Sep 24 2007, 00:20) *
Дык может и писали, тока в майнстрим исходниках такого почему-то не наблюдал,
таки все таки почему???

В бытность, когда я пасся на su.c-cpp, там был один очень квалифицированный чел по С/С++, я не помню случая, чтобы он чего-то не знал до мельчайших тонкостей. Так вот он грил, что у них есть большой проект, который они ведут полностью на С исключительно из-за портабельности - не на всех платформах, которые им надо, имеется компилятор С++, либо имеющийся компилятор низкого качества. Кроме того, проект практически закончен и на тот момент просто сопровождался, поэтому переписывать работающий и отлаженный код нет ни малейшего смысла. Было это сказано где-то в районе 2000-го года. С линухом, предполагаю, ситуация примерно та же самая - когда он интенсивно разрабатывался, достойных компиляторов С++ еще не было, да и будущее С++ в те годы было еще достаточно туманным. Ну, а сегодня переписывать стабильный код ради идеи просто неразумно.
Rst7
Лично я в данном моменте (обмен по RSу, например) предпочитаю использовать костыль собственного изобретения примерно такого плана:
Код
#include "setjmp.h"

#define    CONTEXT_SWT_vect SPM_RDY_vect

#pragma vector=CONTEXT_SWT_vect
__interrupt void CNTX_SWT(void);

#define IN_CSWT ACSR_ACIS1

jmp_buf main_task;
jmp_buf rs_task;
char rs_rstack[8];
char rs_cstack[64];

char rxbuf[16];

#pragma vector=USART0_RXC_vect
__interrupt __raw void rs_rx(void)
{
  UCSR0B_RXCIE0=0;
  if (!IN_CSWT)
  {
    IN_CSWT=1;
    __enable_interrupt();
    ((void(*)(void))CNTX_SWT)();
    __disable_interrupt();
    IN_CSWT=0;
  }
}

#pragma vector=USART0_UDRE_vect
__interrupt __raw void rs_tx(void)
{
  UCSR0B_UDRIE0=0;  //Запретили прерывания
  if (!IN_CSWT)
  {
    IN_CSWT=1;
    __enable_interrupt();
    ((void(*)(void))CNTX_SWT)();
    __disable_interrupt();
    IN_CSWT=0;
  }
}

#pragma vector=USART0_TXC_vect
__interrupt __raw void rs_txc(void)
{
  //UCSR0A=1<<TXC0; //Сбросили флаг
  if (!IN_CSWT)
  {
    IN_CSWT=1;
    __enable_interrupt();
    ((void(*)(void))CNTX_SWT)();
    __disable_interrupt();
    IN_CSWT=0;
  }
}

#pragma vector=TIMER1_OVF_vect
__interrupt __raw void rs_timeout(void)
{
  UCSR0B_RXCIE0=0; //Запретили прерывания от приемника
  if (!IN_CSWT)
  {
    IN_CSWT=1;
    __enable_interrupt();
    ((void(*)(void))CNTX_SWT)();
    __disable_interrupt();
    IN_CSWT=0;
  }
}

#pragma vector=CONTEXT_SWT_vect
__interrupt void CNTX_SWT(void)
{
  if (!setjmp(main_task)) //Запомнили контекст осн. задачи
  {
    longjmp(rs_task,1); //Перешли в контекст RS_TRX
  }
}

void wait_int()
{
  if (!setjmp(rs_task)) //Запомнили контекст RS_TASK
  {
    longjmp(main_task,1); //Перешли в контекст осн. задачи
  }
}

char rxbuf[16];

//txsize - количество байт без контрольной суммы
__x char DO_RS(char *out, char txsize, char n_rs, char CRC8RX)
{
  char CRC8;
  char c;
  char rxerr=0;
  UCSR0A=1<<TXC0; //Сбросили флаг от предыдущего прерывания
  CRC8=CRC8tab[c=(*out++)]; //ПУ всегда начинает с нуля
  __disable_interrupt();
  UDR0=c;
  while(PINE_Bit1); //Ждем начала стартового бита
  if (n_rs) EnaTX2; else EnaTX0; //Разрешаем передатчик
  __enable_interrupt();
  while(--txsize)
  {
    CRC8=CRC8tab[CRC8^(UDR0=(*out++))];
    UCSR0B_UDRIE0=1; //Разрешили прерывания от UDRIE
    wait_int(); //Ждем освобождения буфера
  }
  UDR0=CRC8;
  UCSR0B_TXCIE0=1; //Разрешили прерывание от TXC
  wait_int(); //Байт контрольной суммы - последний байт
  UCSR0B_TXCIE0=0; //Запретили прерывания от TXC
  DisTX2; //Выключили каналы передачи
  DisTX0;
  if (n_rs) EnaRX2; else EnaRX0; //Разрешили приемник (пока только на драйвере)
  TCNT1=Time1000;
  TIFR=1<<(TOV1);
  TIMSK_TOIE1=1; //Разрешили прерывание от таймера
  wait_int(); //Ждем 1 мс пока переходные процессы
  UCSR0B_RXEN0=1; //Разрешили приемник
  //txsize здесь равен 0
  CRC8RX=CRC8tab[CRC8RX]; //Нач. значение
  out=rxbuf;
  TCNT1=Time2200; //Время ожидания первого байта
  for(;;)
  {
    UCSR0B_RXCIE0=1; //Разрешили прерывания от приемника
    wait_int();
    if (!UCSR0A_RXC0)
    {
      //Выход по таймауту
      if (CRC8RX) txsize=0; //Не срослась контрольная сумма
      break; //Выходим из цикла
    }
    //Приняли байт
    if (UCSR0A_FE0||UCSR0A_DOR0)
    {
      rxerr=1; //Ошибки
    }
    c=UDR0;
    if (txsize<sizeof(rxbuf))
    {
      CRC8RX=CRC8tab[CRC8RX^(*out=c)]; // Читаем принятый байт в буфер приема и перерасчет CRC
      txsize++;
      out++;
    }
    else
    {
      rxerr=1; //Слишком много байт
      break;
    }
    TCNT1=Time1600;
  }
  UCSR0B_RXEN0=0; //Запретили приемник
  TIMSK_TOIE1=0; //Запретили таймер
  DisRX0;
  DisRX2;
  if (rxerr) txsize=0;
  return(txsize); //Количество принятых байт, если 0 - нет ответа
}


__task __noreturn RS_TRX(void)
{
  struct
  {
    char PPKP;
    char Cmd;
  };
  char abon=0;
  char c;
  char CurRS=0;
  for(;;)
  {
    c=DO_RS(&PPKP,1,CurRS,abon);
    switch(c)
    {
    case 4:
      //Есть событие
      //Записываем событие в стек
      Event2FIFO(abon,rxbuf[1],rxbuf[2],0);
    case 2:
      //Нет события
      //.......
      break;
    default:
      //Ошибка
      //.............
      break;
    }
    if (!(abon=(abon+1)&0x7F))
    {
      CurRS^=1;
    }
  }
}
    
void InitRS(void)
{
  __disable_interrupt();
  DDRE=0xFE;
  PORTE=0xFF;
  UBRR0L=25;
  UCSR0B_TXEN0=1; //Разрешили передатчик
  TCCR1B=2;
  ((unsigned int *)rs_task)[10]=((unsigned int)rs_rstack)+7; //SP
  ((unsigned int *)rs_task)[8]=((unsigned int)rs_cstack)+64; //Y
  ((unsigned int *)rs_task)[9]=(unsigned int)RS_TRX; //Адрес перехода
  if (!setjmp(main_task)) longjmp(rs_task,1); //Переходим в RS_TRX
  __enable_interrupt();
}


Зато все просто - переменные вообще локальные, код простой до боли, не надо организовывать кучу обработчиков, передавать руками данные между ними (все в локальных переменных, там все автоматически получается), время работы с запрещенными прерываниями - минимально. Конечно, это занимает немного больше ручками вылизаного кода, но зато насколько все проще.
Я понимаю, что мне сейчас скажут - "А почему бы ОСь не использовать". На это отвечу - ну вот это и есть своя собственная ось без кода на асме smile.gif

Вот этот код (как заготовка) кочует из проекта в проект, с минимальными изменениями. Очень удобно.

Теперь хотелось бы услышать от адептов C++, что мне даст применение плюсов в данном месте? Чем мне облегчит жизнь? Положить все переменные в класс? А смысл?
Непомнящий Евгений
Цитата(Rst7 @ Sep 24 2007, 11:05) *
Теперь хотелось бы услышать от адептов C++, что мне даст применение плюсов в данном месте? Чем мне облегчит жизнь? Положить все переменные в класс? А смысл?


Лично у меня подход примерно следующий: есть класс, назовем его TCanal. У него есть методы типа getByte и onByteRecieve, которые вызываются из прерываний УАРТа. Этот класс реализует логику посылки запроса и получения ответа (или не получения - по таймауту ожидания). Логика обработки принятых байт лежит в производном классе (она своя для каждого протокола), где подключается соответствующий обработчик.

Преимущества: есть совершенно не меняющийся файл canal.h / canal.cpp, общий для всех проектов. Есть также куча не меняющихся файлов canalXXX.h / canalXXX.cpp, которые реализуют логику получения пакета для конкретного протокола.

Далее для конкретного проекта просто создаются объекты нужных классов, пишутся обработчики прерываний (равно 1 строка в каждом) и ВСЕ! Никакой ручной подгонки кода! Любое количество уартов!

То же самое вполне можно писать на чистом C. Примерно так:

Код
typedef struct _TCanalData
{
       ..........
} TCanalData;

byte canal_getByteForSend(TCanalData *pData);
void canal_onByteRecieved(TCanalData *pData, byte dt);


Преимущества C++ при таком подходе - код выглядит красивее.
Кстати, куча функций в WinAPI (чистый C) написаны примерно в таком духе - первым аргументом идет указатель на специфическую структуру данных, а затем - собственно аргументы ф-ции.
Rst7
Цитата(Непомнящий Евгений @ Sep 24 2007, 11:35) *
Лично у меня подход примерно следующий: есть класс, назовем его TCanal. У него есть методы типа getByte и onByteRecieve, которые вызываются из прерываний УАРТа. Этот класс реализует логику посылки запроса и получения ответа (или не получения - по таймауту ожидания). Логика обработки принятых байт лежит в производном классе (она своя для каждого протокола), где подключается соответствующий обработчик.


Не понятен мне смысл навешивать классы на код (аналогичный код на приеме)
Код
while(--txsize)
{
   CRC8=CRC8tab[CRC8^(UDR0=(*out++))];
   UCSR0B_UDRIE0=1; //Разрешили прерывания от UDRIE
   wait_int(); //Ждем освобождения буфера
}


Ради чего?

Цитата
Преимущества C++ при таком подходе - код выглядит красивее.


Т.е. делать обработчики, передавать параметры, указатели и т.д - не важно, это делается через классы или структуры. Непонятна мне такая красота. Куда уж красивее? Цикл и все. В соответствии с идеологией цпп я должен тут обязятельно навесить кучу методов. Но зачем??? Неужели будет красивее и понятнее?
Непомнящий Евгений
Мой класс TCanal посылает запрос, ждет получения ответа определенное время, при отсутствии ответа повторяет запрос указанное кол-во раз и т.д. При этом получается отнюдь не цикл из трех строк, как вы привели...

При объектном подходе (не важно на С или на С++) мне надо сделать это ровно один раз, а потом использовать без изменений. При этом в одном проекте у меня может быть любое количество таких объектов.
Если не выделить эту функциональность в отдельные ф-ции + структуру С или класс С++, то максимум - у меня будет "рыба", которую я буду копи\пастить на N проектов и в рамках проекта на M уартов - в результате получится целая куча примерно одинакового кода, при адаптации которого я всякий раз должен буду вспоминать как он работает.
Еще вариант - написать по такой "рыбе" соответствующие макросы и поиметь кучу нюансов, с ними связанных.
Однако макросы будут довольно сложными (если учесть требование нескольких обработчиков в рамках проекта) + это фактически "скрытая" генерация дополнительного кода. Так не проще ли использовать классы в С++ или эмулировать их структурами + функциями в С?
При этом всего два недостатка:
1. Надо тщательно продумать и разработать эту общую функциональность
2. Некоторая потеря в скорости по сравнению с копи\пастом \ макросами. Однако если учесть, что скорость по УАРТу обычно не слишком высокая, то этими потерями в десятков\сотен тактов вполне можно пренебречь.
alexander55
Цитата(Rst7 @ Sep 24 2007, 12:51) *
Неужели будет красивее и понятнее?

Можете не верить, да будет и не только ... Но это уже бег по кругу, а я не хомяк.
Rst7
Цитата(Непомнящий Евгений @ Sep 24 2007, 12:25) *
Мой класс TCanal посылает запрос, ждет получения ответа определенное время, при отсутствии ответа повторяет запрос указанное кол-во раз и т.д. При этом получается отнюдь не цикл из трех строк, как вы привели...


Из пяти. Во внешней функции, напрмер так (хотя у меня совсем по другому, там не сразу повторяется запрос, а на следующем круге)

Код
char r=3;
do
{
c=DO_RS(&PPKP,1,CurRS,abon);
if ((c!=4)||(c!=2)) break; //Ну или другое условие проверим
}
while(--c);


Или тут тоже проще написать кучу методов, повызывать их? Или все-таки почеловечески, пробуем 3 раза, если обмен свершился, прекращаем попытки?

Цитата
При объектном подходе (не важно на С или на С++) мне надо сделать это ровно один раз, а потом использовать без изменений. При этом в одном проекте у меня может быть любое количество таких объектов.


Уменьшение писанины. С этим согласен.

Цитата
Если не выделить эту функциональность в отдельные ф-ции + структуру С или класс С++, то максимум - у меня будет "рыба", которую я буду копи\пастить на N проектов и в рамках проекта на M уартов - в результате получится целая куча примерно одинакового кода, при адаптации которого я всякий раз должен буду вспоминать как он работает.


Мой опыт говорит, что всегда надо что-то точить. Не получается универсальности.

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


Не доходит до меня. У меня есть код (приведенный выше), который я использую (конечно, внося изменения) во всех проектах. Что мне даст изготовление этого кода в соответствии с парадигмами цпп?

Цитата
При этом всего два недостатка:
1. Надо тщательно продумать и разработать эту общую функциональность


На Си тоже надо. Не думаете ли вы, что я не думаю над своим софтом? Так что это не недостаток.

Цитата
2. Некоторая потеря в скорости по сравнению с копи\пастом \ макросами. Однако если учесть, что скорость по УАРТу обычно не слишком высокая, то этими потерями в десятков\сотен тактов вполне можно пренебречь.


Далеко не всегда.
Непомнящий Евгений
Вдогонку
Цитата(Rst7 @ Sep 24 2007, 12:51) *
В соответствии с идеологией цпп я должен тут обязятельно навесить кучу методов. Но зачем??? Неужели будет красивее и понятнее?


1. В соответствии с идеологией не цпп, а ООП. Это совершенно разные понятия smile.gif
2. Нужен баланс - каждый класс должен иметь конкретную, вполне определенную функциональность. Если эта функциональность мизерна - можно удалить класс, перенеся его функциональность в тем места, где он использовался. Если класс имеет очень большую функциональность - хорошо бы его разбить на несколько более мелких. Цель - сделать программу понятнее, ее куски - независимыми от других (по возможности) и повторно используемыми.
Это вполне работает и в чистом С - иметь миллион мелких функций плохо, равно как и иметь несколько гигантских...
Rst7
Цитата(Непомнящий Евгений @ Sep 24 2007, 12:51) *
Вдогонку
1. В соответствии с идеологией не цпп, а ООП. Это совершенно разные понятия smile.gif


Безусловно. Я, конечно, имел в виду ООП. С++ как среду реализации идей ООП.


Цитата
2. Нужен баланс - каждый класс должен иметь конкретную, вполне определенную функциональность. Если эта функциональность мизерна - можно удалить класс, перенеся его функциональность в тем места, где он использовался. Если класс имеет очень большую функциональность - хорошо бы его разбить на несколько более мелких. Цель - сделать программу понятнее, ее куски - независимыми от других (по возможности) и повторно используемыми.
Это вполне работает и в чистом С - иметь миллион мелких функций плохо, равно как и иметь несколько гигантских...


Программа понятна. Читабельна. Обозрима. Какой плюс мне даст переделка этого кода в соответствии с ООП? Как бы вы порекомендовали разложить это по классам/методам? Какой выигрыш я получу?
Непомнящий Евгений
Цитата(Rst7 @ Sep 24 2007, 13:39) *
Или тут тоже проще написать кучу методов, повызывать их? Или все-таки почеловечески, пробуем 3 раза, если обмен свершился, прекращаем попытки?

Еще раз smile.gif . У меня есть объект, который умеет передать запрос, получить ответ, повторить передачу при необходимости, сообщить об отсутствии ответа.
Чтобы его использовать в конкретном проекте, я пишу обработчики 3-х прерываний примерно по 3 строки в каждом + пару 3-х строчных функций, которые в(ы)ключают приемник\передатчик УАРТа.
Еще нужна ф-ция, которая выставляет флаг получения ответа - для его последующей обработки.

Класс TCanal (а точнее производные от него) используется в других классах, которые содержит логику управления внешними устройствами.

Преимущества моего TCanal перед несколькими вашими функциями:
1. Легко тиражировать - если у меня в устройстве 4 уарта, я просто создаю четыре объекта + пишу обвязку для каждого (элементарную). Для ваших функций мне потребуется руками растиражировать каждую используемую переменную (а это как минимум, указатель на буфер, длина, число повторов максимальное и текущее, время ожидания и т.д.) и каждую функцию.
2. Мне не надо вообще ничего менять в базовом классе при написании нового протокола. Мне надо просто написать производный класс, в котором будет подключаться обработчик полученных байт + сам этот обработчик.
3. У меня логика посылки\повтора посылки отделена от логики "выцепления" пакета из потока байт (начало\конец пакета, длина, crc и т.д.). У вас - нет. Если в новом протоколе формат пакетов будет другой, в вашем случае придется по живому резать весь код; в моем - просто написать новый обработчик.
4. Параметры - время ожидания, число повторов и т.д. задаются в одном месте. Их четко видно в коде и легко поменять на другие. У вас они вшиты в ваши функции.

Цитата
Мой опыт говорит, что всегда надо что-то точить. Не получается универсальности.

Если написать грамотно (и потом раз несколько повторно переписать smile.gif ), точить придется в производном классе, не меняя код в базовом. А возможно - вообще не придется, если предусмотреть соответствующие параметры.

Цитата
Не доходит до меня. У меня есть код (приведенный выше), который я использую (конечно, внося изменения) во всех проектах. Что мне даст изготовление этого кода в соответствии с парадигмами цпп?

см выше.
Цитата
На Си тоже надо. Не думаете ли вы, что я не думаю над своим софтом? Так что это не недостаток.

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

Если критично быстродействие - есть встраивание ф-ций, шаблоны и т.д.

Цитата(Rst7 @ Sep 24 2007, 14:14) *
Программа понятна. Читабельна. Обозрима. Какой плюс мне даст переделка этого кода в соответствии с ООП? Как бы вы порекомендовали разложить это по классам/методам? Какой выигрыш я получу?


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

Лично мне удобнее организовывать код как несколько кирпичиков, которые можно складывать друг с другом в разных вариантах без переделки самих кирпичей, а только за счет "разного цемента" smile.gif . Т.е., я написал такой кирпичик, отладил его в рамках какого-нибудь проекта, а затем без изменений (вообще!) использую в другом. За счет ООП я могу, имея один кирпич, несколькими строками кода наклепать несколько подобных. Постепенно я улучшаю кирпичи, при этом они автоматически улучшаются и в старых проектах после перекомпиляции (или старые проекты становятся полностью неработоспособными smile.gif, но это уже проблема осторожного улучшения и использования системы контроля версий).
Rst7
Цитата
3. У меня логика посылки\повтора посылки отделена от логики "выцепления" пакета из потока байт (начало\конец пакета, длина, crc и т.д.). У вас - нет. Если в новом протоколе формат пакетов будет другой, в вашем случае придется по живому резать весь код; в моем - просто написать новый обработчик.


У меня тоже отделена. Функция посылки пакета и приема (DO_RS) - одна, а вызов во внешнем цикле (RS_TRX), там и думаем, чего делать при ошибках. Пример реализации я написал выше. Что не так?

Цитата
4. Параметры - время ожидания, число повторов и т.д. задаются в одном месте. Их четко видно в коде и легко поменять на другие. У вас они вшиты в ваши функции.


Нет проблем. Я делаю #define и все видно. Какие вопросы? Тоже легко поменять...



Цитата
Лично мне удобнее организовывать код как несколько кирпичиков, которые можно складывать друг с другом в разных вариантах без переделки самих кирпичей, а только за счет "разного цемента" . Т.е., я написал такой кирпичик, отладил его в рамках какого-нибудь проекта, а затем без изменений (вообще!) использую в другом. За счет ООП я могу, имея один кирпич, несколькими строками кода наклепать несколько подобных. Постепенно я улучшаю кирпичи, при этом они автоматически улучшаются и в старых проектах после перекомпиляции (или старые проекты становятся полностью неработоспособными , но это уже проблема осторожного улучшения и использования системы контроля версий).


Слова. Именно такими словами тут попрекают тех, кто говорит - "мне не нужен С++". Хорошо, допустим, я в своей уверенности, что цпп - от лукавого, не прав. Я привел пример кода, расскажите, чем бы ооп украсило (или улучшило) данный код? Не забудьте рассмотреть проблему в контексте оптимальности выходного кода.
dxp
Цитата(Rst7 @ Sep 24 2007, 18:14) *
Слова. Именно такими словами тут попрекают тех, кто говорит - "мне не нужен С++". Хорошо, допустим, я в своей уверенности, что цпп - от лукавого, не прав. Я привел пример кода, расскажите, чем бы ооп украсило (или улучшило) данный код? Не забудьте рассмотреть проблему в контексте оптимальности выходного кода.

Я что-то не очень уловил, где тут (в вашем с оппонентом обсуждении) полиморфизм проглядывает?
Rst7
Цитата(dxp @ Sep 24 2007, 14:28) *
Я что-то не очень уловил, где тут (в вашем с оппонентом обсуждении) полиморфизм проглядывает?


Ну да.... Я же забыл... wink.gif (далее из википедии)
Цитата
Полиморфизм — один из трёх важнейших механизмов объектно-ориентированного программирования (наряду с инкапсуляцией и наследованием).
Полиморфизм позволяет писать более абстрактные программы и повысить коэффициент повторного использования кода.


Как выразился кто-то в другой ветке - "Математическую абстракцию тестером не измерить" (там где было сказано и по-поводу - фраза весьма спорна, ну да не суть). Вот и я не могу понять, что есть "более абстрактные программы". Умом то я понимаю, что можно так извратиться, что выполнить

Цитата
взаимозаменяемость объектов с одинаковым интерфейсом.


но мне кажется, что это только самоцель. Т.е. использование ООП ради самого ООП. Моя практика показывает, что в любом проекте есть момент, когда надо исправить что-то, что лежит достаточно на глубоком уровне. И при этом результат должен быть доступен и на верху (и пару раз на средних уровнях). И не просто доступен, а должны выполнится какие-то действия. И нафиг не нужен такой костыль в других проектах. Чем мне поможет ООП? Нести в другой проект этот костыль, пусть будет, ведь мы же стремимся к взаимозаменяемости объектов с одинаковым интерфейсом?

Скажете - плохо планировали структуру программы? А у вас всегда безошибочные планы? Не верю wink.gif


Лично мое мнение - вид всех программ на цпп, которые мне попадались под нож (например, для сопровождения или использования в своих целях), просто ужасен - огромное количество классов, необозримые многоуровневые наследования, в каждом методе - пара действий и все. Я вижу, что такая программа состоит из сплошного оверхеда, носить this между методами... Где разум и логика? И тут уже количество писанины только увеличивается, а не уменьшается, потому как нормальный код на Си выходит на две странички.
Непомнящий Евгений
Цитата(Rst7 @ Sep 24 2007, 15:14) *
У меня тоже отделена. Функция посылки пакета и приема (DO_RS) - одна, а вызов во внешнем цикле (RS_TRX), там и думаем, чего делать при ошибках. Пример реализации я написал выше. Что не так?


У вас ф-ция DO_RS намертво привязана к конкретному уарту, конкретному таймеру и конкретному формату пакетов. При изменении любой из составляющих, вам придется менять ф-цию DO_RS.

Если в программе два канала связи с внешними устройствами, вам потребуется ровно две функции DO_RS, даже если протокол одинаков. А если 4 - то 4 ф-ции. А если 8 ... smile.gif

Цитата
Нет проблем. Я делаю #define и все видно. Какие вопросы? Тоже легко поменять...

Опять же, если "каналов" несколько, то ...

Цитата
Слова. Именно такими словами тут попрекают тех, кто говорит - "мне не нужен С++". Хорошо, допустим, я в своей уверенности, что цпп - от лукавого, не прав. Я привел пример кода, расскажите, чем бы ооп украсило (или улучшило) данный код? Не забудьте рассмотреть проблему в контексте оптимальности выходного кода.


Тут похоже религиозная война начинается biggrin.gif
Чем украсило - уже рассказал в постах выше. Если переписать это с использованием ООП, то вы получаете легко тиражируемый (без изменений) код. А если у вас много протоколов, то суммарный объем написанного кода уменьшается, вместе с числом потенциальных ошибок.
Что касается оптимальности. Да, если использовать общий код, вы получите оверхед по сравнению со специализированным. Там, где этот оверхед принципиален, возможно от общего кода придется отказаться и переписать его руками для конкретного случая. Но таких мест в реальной программе крайне мало, и обычно связь с внешними устройствами по RS-485 (232) таким местом не является. Если используется сколь-нибудь длинная линия, то скорости там порядка 9600.

Вы напираете на "ненужность с++", но говорите фактически о ненужности ООП. Никто не мешает использовать ООП и на чистом С. Еще раз обращаю ваше внимание на winAPI. Не знаю, что в Линуксе, но подозреваю - что-то в этом духе...
dxp
Цитата(Rst7 @ Sep 24 2007, 18:53) *
Ну да.... Я же забыл... wink.gif (далее из википедии)

Вы на какой вопрос отвечаете? Я спросил не "что такое полиморфизм", это я знаю и без википедии. Я спросил, где в обсуждаемом вами с оппонентом примере просматривается оный полиморфизм?
Непомнящий Евгений
Цитата(Rst7 @ Sep 24 2007, 15:53) *
Моя практика показывает, что в любом проекте есть момент, когда надо исправить что-то, что лежит достаточно на глубоком уровне. И при этом результат должен быть доступен и на верху (и пару раз на средних уровнях). И не просто доступен, а должны выполнится какие-то действия. И нафиг не нужен такой костыль в других проектах. Чем мне поможет ООП? Нести в другой проект этот костыль, пусть будет, ведь мы же стремимся к взаимозаменяемости объектов с одинаковым интерфейсом?


Берете эту функциональность и реализуете в виде отдельной функции \ класса. В нужном месте подключаете нужную функцию \ объект. Да, есть оверхед. Чтобы его не было - у нас есть шаблоны. Если шаблонов нам не хватает - макросы.

Альтернатива - каждый проект имеет свой, полностью уникальный код. У меня 50 проектов. И 50 папок с полностью уникальным кодом. Если в рыбе, на базе которой был вручную создан весь этот уникальный код, обнаружилась ошибка, мне надо руками поправить все эти 50 проектов... А до этого мне пришлось 50 раз копи\пастить рыбу, править ее и проверять свои правки...

Цитата
Лично мое мнение - вид всех программ на цпп, которые мне попадались под нож (например, для сопровождения или использования в своих целях), просто ужасен - огромное количество классов, необозримые многоуровневые наследования, в каждом методе - пара действий и все. Я вижу, что такая программа состоит из сплошного оверхеда, носить this между методами... Где разум и логика? И тут уже количество писанины только увеличивается, а не уменьшается, потому как нормальный код на Си выходит на две странички.


Почитайте что-нибудь типа "Искусства программирования" (к стыду своему забыл автора).
Там на эту тему очень подробно написано.
И посмотрите, в каком направлении движется индустрия - общие библиотеки, каркасы ...

Что касается мелких методов - так ведь есть встраивание и оптимизация компилятором.

Объем кода при использовании ООП и росте проекта сокращается ... Проверил на паре унаследованных проектов. Как сокращается и объем программы.
Rst7
Цитата(Непомнящий Евгений @ Sep 24 2007, 14:53) *
У вас ф-ция DO_RS намертво привязана к конкретному уарту, конкретному таймеру и конкретному формату пакетов. При изменении любой из составляющих, вам придется менять ф-цию DO_RS.


А вам - необходимый метод. Не забываем, что таймера тоже обычно разные. А если мы сделаем таймер програмный, то надо просто озаботиться, чтобы к нему был нормальный интерфейс (тут правда, вы можете подумать, что я заговорил словами, употребляемыми при ООП, однако это не так. Это ООП подхватило слово интефейс wink.gif )

Цитата
Если в программе два канала связи с внешними устройствами, вам потребуется ровно две функции DO_RS, даже если протокол одинаков. А если 4 - то 4 ф-ции. А если 8 ... smile.gif
Опять же, если "каналов" несколько, то ...


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

Если у меня будет много однотипных каналов, я переделаю этот код так, чтобы и рыбку съесть (т.е. будет одна функция) и остальное сделать (хоть 32)

Цитата
Тут похоже религиозная война начинается biggrin.gif


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

Цитата
Чем украсило - уже рассказал в постах выше.
Если переписать это с использованием ООП, то вы получаете легко тиражируемый (без изменений) код. А если у вас много протоколов, то суммарный объем написанного кода уменьшается, вместе с числом потенциальных ошибок.
Что касается оптимальности. Да, если использовать общий код, вы получите оверхед по сравнению со специализированным. Там, где этот оверхед принципиален, возможно от общего кода придется отказаться и переписать его руками для конкретного случая. Но таких мест в реальной программе крайне мало, и обычно связь с внешними устройствами по RS-485 (232) таким местом не является. Если используется сколь-нибудь длинная линия, то скорости там порядка 9600.


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

Цитата
Вы напираете на "ненужность с++", но говорите фактически о ненужности ООП. Никто не мешает использовать ООП и на чистом С. Еще раз обращаю ваше внимание на winAPI. Не знаю, что в Линуксе, но подозреваю - что-то в этом духе...


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

И просьба. Не надо говорить, что я не делал больших проектов, или мал опыт, или... Ну вообщем, давайте не будем приводить аргументы этого класса.


Цитата(dxp @ Sep 24 2007, 15:06) *
Вы на какой вопрос отвечаете? Я спросил не "что такое полиморфизм", это я знаю и без википедии. Я спросил, где в обсуждаемом вами с оппонентом примере просматривается оный полиморфизм?


Не просматривается. Однако, предвидя один из возможных дальнейших ходов беседы (вот полиморфизм - это да!) ответил в воздух. Извините, поспешил. Могу ли я узнать, будет ли поворот в сторону обсуждения данной проблемы? Если да, то, может быть вы прокомментируете мой ответ (или скажем так, позицию).
dxp
Цитата(Rst7 @ Sep 24 2007, 19:23) *
Не просматривается. Однако, предвидя один из возможных дальнейших ходов беседы (вот полиморфизм - это да!) ответил в воздух. Извините, поспешил. Могу ли я узнать, будет ли поворот в сторону обсуждения данной проблемы? Если да, то, может быть вы прокомментируете мой ответ (или скажем так, позицию).

Мне кажется, что вы слишком напряжены. Прошу прощения, если ошибся. Как говорят "там" "take it easy". smile.gif Я не ставлю себе целью кого-то на чем-то ловить и "ставить подножку". smile.gif

Я про полиморфизм вот почему спросил. Тут на протяжении уже десятка постов постоянно звучит термин ООП. А ООП без полиморфизма не бывает. Вот я и не понял, где там ООП начинается. Классов и наследования тут (для ООП) недостаточно. Если полиморфизма нет, то и ООП нет, а есть только ОП, сиречь объектное программирование.

Кстати, тождество С++ === ООП совсем не верно. С++ - язык гибридный, мультипарадигменный, как его называют. Т.е. в нем вполне гармонично уживаются и процедурная, и объектная, и объектно-ориентированная парадигмы. Всякая из них к своему месту. Надо их применять так, чтоб удобно было и эффективно, спорить, какая лучше, достаточно безсмысленно.
Непомнящий Евгений
Цитата(Rst7 @ Sep 24 2007, 16:23) *
А вам - необходимый метод. Не забываем, что таймера тоже обычно разные. А если мы сделаем таймер програмный, то надо просто озаботиться, чтобы к нему был нормальный интерфейс (тут правда, вы можете подумать, что я заговорил словами, употребляемыми при ООП, однако это не так. Это ООП подхватило слово интефейс wink.gif )

А вот тут вы не правы. Мне надо будет написать внешнюю функцию, которая будет включать и выключать этот таймер. А в прерывании таймера дернуть метод объекта TCanal.
Ф-ция включения и выключения таймера же состоит всего из пары строк и ее написание не сравнимо с копированием всего DO_RS и выкусыванием всех обращений к таймеру. А если таймеры не симметричные - типа 0 и 1/3 в Атмеге128, то геморроя прибавится.

Цитата
Да и хрен с ними, с двумя. Все равно будет примерно одинаково, что класс с разными методами, что две функции.

Еще раз. Класс TCanalXXX у меня не меняется вообще никогда. Почему? Потому что он использует таймер, вызывая две функции, которые реализует "цемент" - а именно -
ext_startTimer(byte timerID, uint interval)
ext_stopTimer(byte timerID)
УАРТ он использует, также вызывая внешние функции.
При добавлении нового экземпляра канала, мне надо добавить вполне конкретный маленький кусочек кода в каждую из этих внешних функций. И все.
Цитата
Да и в моем способе две функции будут меньше - потому что результирующий код работает с регистрами, а не с памятью для хранения переменных.

Да. Более того, ваш код быстрее. В нем таймер включается непосредственно, а не вызовом внешней функции. В данном случае, имхо, этот оверхед не принципиален. Если он будет принципиален - я воспользуюсь шаблоном или макросом.
Цитата
Если у меня будет много однотипных каналов, я переделаю этот код так, чтобы и рыбку съесть (т.е. будет одна функция) и остальное сделать (хоть 32)

Ну это и будет ООП в чистом виде. Вы сложите все, что касается канала в структуру и будете передавать всем функциям либо указатель, либо ее номер в некотором глобальном массиве.
Вопрос: почему не сделать так сразу?

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

А кто знает, что понадобится, а что не понадобится. Не хотите оверхеда - настраивайте директивами препроцессора...

Цитата
Скажем так, я против повального увлечения ООП. И особенно против высказываний класса "А вот на плюсах (ооп) будет круто, а на чистом си - это плохо, немодно, нет классов, я не могу код использовать в другом проекте, я не могу исправить две строчки/дефайна" и т.д.

ООП надо использовать разумно и там, где нужно (как и все остальное smile.gif ). Местами он сильно облегчает жизнь.

Цитата
И просьба. Не надо говорить, что я не делал больших проектов, или мал опыт, или... Ну вообщем, давайте не будем приводить аргументы этого класса.

Да я вроде ничего такого не утверждал... Или это тоже "в воздух"? biggrin.gif
Цитата
Не просматривается. Однако, предвидя один из возможных дальнейших ходов беседы (вот полиморфизм - это да!) ответил в воздух. Извините, поспешил. Могу ли я узнать, будет ли поворот в сторону обсуждения данной проблемы? Если да, то, может быть вы прокомментируете мой ответ (или скажем так, позицию).

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

Цитата(dxp @ Sep 24 2007, 16:49) *
Кстати, тождество С++ === ООП совсем не верно. С++ - язык гибридный, мультипарадигменный, как его называют. Т.е. в нем вполне гармонично уживаются и процедурная, и объектная, и объектно-ориентированная парадигмы. Всякая из них к своему месту.


Насчет гармоничности - я на delphikingdom иногда захожу - так там это смешение парадигм называют скорее свалкой и считают это большим минусом C++ по сравнению с паскалем\дельфи. Впрочем, это уже наверное злостный офтоп.
dxp
Цитата(Непомнящий Евгений @ Sep 24 2007, 19:59) *
Насчет гармоничности - я на delphikingdom иногда захожу - так там это смешение парадигм называют скорее свалкой и считают это большим минусом C++ по сравнению с паскалем\дельфи.

А чем им еще своего главного "конкурента" уделать? Только свалкой и обозвать. smile.gif На самом деле, мультипарадигменность - неотъемлемое качество ЯП, если он претендует на звание языка общего назначения, т.е. достаточно универсального для решения широкого круга задач. Поскольку круг широк, то и инструментов в арсенале должно быть адекватно.

И в этом смысле делфа ничем особым от С++ не отличается - в ней тоже уживаются все эти три парадигмы. biggrin.gif Она с С++ имеет гораздо больше общего, чем со своим прародителем - ЯП Паскаль. От него у нее только синтаксис.
Для просмотра полной версии этой страницы, пожалуйста, пройдите по ссылке.
Invision Power Board © 2001-2025 Invision Power Services, Inc.