Перейти к содержанию
    

Как писать на С++ при создание приложений под ARM

Использование шаблонов С++ сильно тормозит компиляцию

Вместо двух секунд аж все десять! :biggrin:

Не моголи бы Вы подсказать, по какой литературе обучались?

Ну как минимум ссылка на Александреску уже висит :)

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

(наследование от интерфейса и поточная обработка), то тут же вся система станет крайне неустойчивой.
Почему? В чём причины неустойчивости?

 

Конкретно: множественное наследование сразу ставит крест на проекте,

Поясните? И причём тут множественное наследование? О нём вообще ни слова не было сказано.

 

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

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

 

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

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

 

Использование паттернов не имеет отношения к С++, но в микроконтроллерах не имеет особого смысла. Куда можно в AVR засунуть синглтон?!

Да как обычно - чтобы избавиться от зависимости порядка создания объектов при раздельной компиляции файлов проекта. Затраты на него копеечные.

 

Использование шаблонов С++ сильно тормозит компиляцию

Да, несколько секунд лишних, обычно, приходится потратить. :)

 

и плохо контролируется по расходу памяти.

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

 

В общем, это неправда, что С++ оправдан везде, где оправдан С.

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

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

В общем, это неправда, что С++ оправдан везде, где оправдан С.

 

Не согласен категорически, во всяком случае, C++ в программировании можно использовать и «ограничено», не зарываясь в тонкости ООП, при этом не платишь за то, что не используешь.

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

Не моголи бы Вы подсказать, по какой литературе обучались? Или это опыт?

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

 

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

 

На самом деле уровень проблем, который мы тут подняли, это в среде профессиональных программистов просто "асфальтированное шоссе", ко которому ехать легко и просто, это обычные штатные вещи, можно сказать тривиальные для них. Просто в embedded области в силу того, что С++ ещё только занимает позиции, и эти понятия, приёмы и средства языка идут в новинку. Но это и хорошо - то, что профессиональным РС программерам кажется рутиной и скукотой, для нас является прикольными фишками, которые вызывают искренний интерес и мотивируют работу. Второй момент: для нас это всё близко к железу, когда embedded программист пишет код, он всегда представляет, во что выльется реализация (если он хороший программист, конечно), поэтому чёткое понимание связи кода на С++ и его аппаратной реализации тоже пробуждает интерес. РС программисту в этом смысле хуже - от него реализация сидит слишком далеко. Кроме того, эффективное написание программ для настольных машин давно требует использования какого-нить развитого фреймворка, и программист оказывается заключённым в определённые рамки, выйти за которые он по сути не может - иначе сразу потеряет эффективность работы. Embedded программист в значительной степени свободен от этого, что предоставляет куда большие возможности для творчества. А именно творческое начало в любой работе делает эту работу интересной и желанной. Вот как-то так.

 

P.S. может в контексте этой темы и ваших постов Страуструпа выложить?

В смысле? Книжку его выложить или что?

 

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

Неудивительно, что у меня нет к этому тяги: в моих задачах это редко встречается :-)

Ну, это ведь от целей зависит. И конечно, можно результат получать разными способами. Когда я ещё находился в процессе изучения С, был период, когда конструкции языка типа операторов if/for/while/switch уже были уверенно освоены, а концепция указателей на функции ещё не была - там один синтаксис выражения, задающего массив указателей на функции, просто пугал своей замороченностью и сложностью. И когда нужно было вызывать функции по условию, то городился switch, в котором перебором вызывался нужный код. Когда прогресс достиг и понимания концепции указателей на функции и возникла привычка к разбору кода "изнутри-наружу" в выражениях, то массивы указателей на функции стали применяться широчайшим образом для решения тех же задач, которые до этого решались исключительно с помощью операторов ветвления. Т.ч. возможно стоит пересмотреть существующие стереотипы, может это оказаться и не напрасным.

 

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

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

 

* * *

 

Кстати, вспомнил ещё пример, где рулит ООП. Реально используется в текущем проекте. Уже как-то собирался выложить реализацию (правда, с другим акцентом), но всё руки не доходили. Раз уж тут пошло обсуждение, то пусть будет до кучи.

 

Речь идёт о передаче сообщений. Есть программа, в которой в разных её местах возникают события и нужно их как-то передавать на обработку. Причём, нужно сделать так, чтобы при необходимости добавить новый тип сообщения не нужно было перелопачивать существующий код. Иными словами, нужно, чтобы та часть программы, к которой попадает сообщение, как-то сама распознавала тип объекта-сообщения и обрабатывала его, если сообщение предназначено для неё, и игнорировала в противном случае. При этом и создание типов (классов) сообщений, и их посылка, и их приём были простыми и прозрачными для пользователя. Это чем-то похоже на технологию RTTI (Run Time Type Identification), поддерживаемую С++ и реализуемую на основе dynamic_cast<>, но в RTTI является довольно "тяжёлым" механизмом и не очень катит в embedded (к слову, не все компиляторы для МК и поддерживают это).

 

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

    //--------------------------------------------------------------------------
    class TObject
    {
    public:
        virtual void on_message(TBaseMsgWrapper *msg_wrp) = 0;

    };
    //--------------------------------------------------------------------------

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

 

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

 

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

//------------------------------------------------------------------------------
inline uint16_t generate_id() { static uint16_t id; return ++id; }

class TBaseMsgWrapper
{
public:
    virtual uint16_t get_id() = 0;
};

template<typename T> class msg_wrapper : public TBaseMsgWrapper
{
public:
    msg_wrapper(T *msg) : body(msg), TBaseMsgWrapper() { }
    
    static  uint16_t get_class_id() { static const uint16_t id = generate_id(); return id; }
    virtual uint16_t get_id()       { return get_class_id(); }
    
    T *get_msg() const { return body; }
    
private:
    T *body;
};

template<typename T> T *check_msg(TBaseMsgWrapper *msg_wrp)
{
    if( msg_wrp->get_id() == msg_wrapper<T>::get_class_id() )
    {
        return (static_cast<msg_wrapper<T> *>(msg_wrp))->get_msg();
    }
    else 
    {    
        return 0;
    }
}

template<typename T> bool send_message(T *msg, gui::TObject *dst)
{
    msg_wrapper<T> msg_wrp(msg);
    dst->on_message(&msg_wrp);
    
    return true;
}
//------------------------------------------------------------------------------

С целью достижения как можно более простой и дешёвой реализации в качестве типа идентификатора сообщения был выбран целочисленный тип (в данном случае 16-битное беззнаковое целое). Для каждого типа сообщений генерируется уникальный идентификатор - целочисленное значение. Генератором идентификаторов служит функция generate_id(), которая на каждый вызов возвращает новое значение.

 

Далее, сообщение технически представляет собой тело сообщения - это тип, определяемый пользователем, который может быть произвольным - классом (или встроенным типом, но понятно, что идеологически правильно использовать класс или перечислимый тип - в общем, чтобы тип был уникальным), и класс-обёртку для тела сообщения - на каждый тип сообщения генерируется своя обёртка. Генерируется она на основе шаблона с аргументом в виде типа тела сообщения - шаблон msg_wrapper<>. Как видно из кода выше, этот шаблон генерирует классы, которые все являются производными от базового класса TBaseMsgWrapper, который является абстрактным базовым классом и определяет метод get_id(), который переопределяется в производных. Цель этого - чтобы в каждом производном классе эта функция возвращала уникальный идентификатор только своего класса, т.е. только соответствующего своему типу сообщений.

 

Ну, и до кучи ещё определена пара шаблонных функций (шаблонные потому, что типы сообщений разные), которые отвечают за отправку сообщения и проверку его типа. Функция send_message() принимает указатель на тело сообщения (сам объект тела сообщения должен быть создан до этого - где и как, это компетенция пользователя - можно в стеке, можно в статической памяти, а можно хоть и в свободной - как удобно и приемлемо) и указатель на объект, которому сообщение отсылается, т.е. объект назначения. Внутри функции создаётся объект-обёртка сообщения, в которой есть функция, возвращающая уникальный идентификатор для данного типа сообщения, затем делается вызов метода on_message() объекта назначения с передачей ему сообщения.

 

На приёме остаётся только проверить, что сообщение "свое", и если это так, то обработать его, в противном случае проигнорировать.

 

Использование. В приборе, для которого написана программа есть несколько кнопок управления (конкретно их 4 штуки). При нажатии на любую из них генерируется соответствующее событие, по котором формируется сообщение, посылаемое текущему активному объекту (указатель TObject *focus содержит адрес активного объекта).

//------------------------------------------------------------------------------
     // где-то в заголовочном файле
    enum TKeyMessage
    {
        kmNONE,
        kmSEL,
        kmPOWER,
        kmPLUS,
        kmMINUS
    };

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

        gui::TKeyMessage key_code = gui::kmNONE;
        if( dev_key_events.sel.IsClick() )   // если кнопка "Select" нажата
        {
            key_code = gui::kmSEL;
            send_message<gui::TKeyMessage>(&key_code, focus);
        }
        if( power_key_event.IsClick() )
        {
            key_code = gui::kmPOWER;    // если кнопка "Питание" нажата
            send_message<gui::TKeyMessage>(&key_code, focus);
        }
        ...
//------------------------------------------------------------------------------

//------------------------------------------------------------------------------
//
//  На приёме сообщения виджетами:
//
void TWidget::on_message(TBaseMsgWrapper *msg_wrp)
{
    gui::TKeyMessage *p = check_msg<gui::TKeyMessage>(msg_wrp);

    if(p)
    {
        switch(*p)
        {
        case gui::kmSEL:    handle(); break;
        case gui::kmPOWER:  back();   break;
        case gui::kmPLUS:   next();   break;
        case gui::kmMINUS:  prev();   break;
        default: print("Error");
        }
    }  
}
//------------------------------------------------------------------------------

Тут первым делом проверяется, соответствует ли тип сообщения ожидаемому. В частности, тут мы ждём сообщение типа TKeyMessage. Если реально сообщение будет другого типа, то функция check_msg() вернёт 0, и сообщение будет проигнорировано. Проверка сводится к банальному сравнению двух целых, что выполняется очень эффективно. Если тип сообщения соответствует ожидаемому, то будет возвращён указатель на тело сообщения, которое дальше может быть использовано по назначению. В частности, тут производится вызов функций в соответствии с информацией о нажатой кнопке прибора. Функции handle(), back(), next() и prev() являются виртуальными, изначально определены в TWidget и переопределяются в виджетах-потомках - это как раз тот случай, который был описан в предыдущем посте.

 

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

 

В общем, тут можно расширять и вверх, и вширь - т.е. создать столько типов сообщений, сколько нужно, и это всё без изменения остального (уже нередко стабильного) кода без опасности что-то поломать в нём. При этом не надо заботиться о правильности обработки - пользователь только создаёт тело сообщения, делает send_message() и check_message() в точке приёма. Всё остальное делает компилятор и сгенерированный код на рантайме.

 

* * *

Этот же приём был портирован на фреймворк Qt. Там особенность в том, что уже есть механизм передачи событий (QEvent) и при портировании было желание использовать его. Задумка у авторов Qt в части событий такова, что там события делятся на два типа - системные (всякие события от клавиатуры, мыши и прочего) и пользовательские. И предусмотрен идентификатор событий - целое. :) Разделено там так: события от 0 до 1000 - это системные события, а от 1000 до 65535 - пользовательские. Реализация:

#include <QApplication>
#include <QEvent>

//------------------------------------------------------------------------------
inline int generate_id() { static int id = QEvent::User; return ++id; }
//------------------------------------------------------------------------------
template<typename T> class msg_wrapper : public QEvent
{
public:
    msg_wrapper(T msg)
        : QEvent( static_cast<QEvent::Type>( get_class_id() ) )
        , body(msg)
    {
    }
    
    static  int get_class_id() { static const int id = generate_id(); return id; }
    
    T *get_msg() { return &body; }
    
private:
    T body;
};
//------------------------------------------------------------------------------
template<typename T> T *check_msg(QEvent *msg_wrp)
{
    if( msg_wrp->type() == msg_wrapper<T>::get_class_id() )
    {
        return (static_cast<msg_wrapper<T> *>(msg_wrp))->get_msg();
    }
    else 
    {    
        return 0;
    }
}
//------------------------------------------------------------------------------
template<typename T> bool send_message(QObject *dst, T msg)
{
    msg_wrapper<T> msg_wrp(msg);
    return QApplication::sendEvent(dst, &msg_wrp);
}
//------------------------------------------------------------------------------

Использование. Тело сообщения - событие переименования элемента комбобокса:

class TComboBoxRenameEvent
{
public:
    TComboBoxRenameEvent(QString NewValue): Value(NewValue){ }

    QString value() const { return Value; }

private:
    QString Value;
};

 

Сам класс комбобокса - в нём нужно переопределить функцию-обработчик события customEvent() (виртуальная).

class TComboBox : public QComboBox
{
    Q_OBJECT

public:
    explicit TComboBox(QWidget *parent = 0) : QComboBox(parent) { }

    TComboBox & operator=(const TComboBox & );

protected:
    virtual void customEvent(QEvent *Message);
};

 

Генерация и отсылка сообщения (по кнопке Ok в диалоге переименования генерируется сигнал, к которому подконнекчен слот, представленный ниже):

void TRenameDialog::on_pbOk_clicked()
{
    TComboBoxRenameEvent msg_body(ui->leName->text());
    send_message<TComboBoxRenameEvent>(Dst, msg_body);
}

 

И, наконец, приём и обработка сообщения:

void TComboBox::customEvent(QEvent *Message)
{
    TComboBoxRenameEvent *msg = check_msg<TComboBoxRenameEvent>(Message);
    if( msg )
    {
         ...; // обработка сообщения
    }
}
//------------------------------------------------------------------------------

В общем-то, сама по себе идеология передачи событий в Qt очень похожа на описанную выше для embedded системы, она проста и логична, будучи основанной на передаче события в виде указателя на базовый класс. Но тут есть недостаток - приходится руками генерировать идентификатор типа, руками проверять на соответствие и делать явное преобразование типов (а явное преобразование типов - это, как правило, не гуд, и как утверждает тов. Страуструп: "...обычно указывает на ошибки проектирования", поэтому всегда желательно каждый такой случай рассматривать внимательно и при малейшей возможности прятать с глаз долой; в любом случае нехорошо, когда пользовательский код пестрит явными преобразования типов). Описанный приём просто все эти действия автоматизирует и предоставляет программисту более простой и безопасный интерфейс для использования.

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

Поясните? И причём тут множественное наследование? О нём вообще ни слова не было сказано.

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

 

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

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

 

Да как обычно - чтобы избавиться от зависимости порядка создания объектов при раздельной компиляции файлов проекта. Затраты на него копеечные.

Ага, сначала создавать себе проблемы, используя С++, а потом мужественно их решать :)

 

А то, что голову надо включать, так это универсальное правило для любого ЯП.

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

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

В общем, это неправда, что С++ оправдан везде, где оправдан С.

Похоже, что назревает очередной спор (VS).

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

А неаккуратная работа с памятью более вероятна как раз в С++, потому что там куда чаще встречается динамическое размещение объектов.

 

Набрался терпения и решил немного поработать писателем и корректором. Да, действительно адресная арифметика и динамическое размещение памяти при неопытности «да и опытности» тянет за собой источники потенциальных ошибок. Microsof, например, для решения этой проблемы и не только, разработал платформу .NET, под которую можно писать приложения на многих языках программирования, в частности применять C++ с расширением управляемости. (С++.Net). Код здесь, выполняется под управлением обще языковой среды выполнения CLR (Common Language Runtime), называется «управляемым кодом». Программы состоят из кода и данных, и обще языковая среда выполнения CLR обеспечивает поддержку и управляемым данным, и управляемому коду. Управляемые данные размещаются в управляемой динамически распределяемой области памяти (куче), которая имеет весьма ценную особенность — автоматическую сборку мусора (garbage collection). Если при создании программ на обычном C++ программист должен сам создавать средства управления динамически распределяемой областью памяти, то обще языковая среда выполнения CLR реализует этот процесс, отслеживая ссылки на объекты и автоматически освобождая ресурсы, занятые объектами, которые стали недоступны программе. Фактически, управляемые и неуправляемые (к которому мы все так привыкли) код и данные на C++ могут быть определены в одном и том же исходном файле, и в некоторой степени эти два мира могут взаимодействовать. Хотя использование управляемого кода и данных имеет много преимуществ (пример выход за пределы массива, на этапе run-time сразу выбросит исключение) оно может привести к снижению производительности и потере гибкости. Поэтому во многих случаях неуправляемый C++, при опытности и знаниях, оказывается лучшим выбором для создания программ.

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

По сравнению с уважаемым dxp, я вообще не программист :crying: Но, тем не менее, позволю изложить и свою точку зрения.

 

Адресная арифметика - это также и чисто микроконтроллерная область

А к чему это уточнение? С адресами на любой платформе работать приходится.

А неаккуратная работа с памятью более вероятна как раз в С++, потому что там куда чаще встречается динамическое размещение объектов.

Не обязательно на Си++ размещать объекты динамически. Я не размещаю. А если размещаете, то нужно делать это корректно.

На С++ голову уж слишком сильно нужно включать.

Ну так это инструмент профессиональный :rolleyes:

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

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

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

Такие примеры, чтобы показывали удобство и силу использование ООП, и тянуло сразу садится, вникать и кодить.

 

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

Основная фича - источник сообщения ничего не знает о количестве и наличии получателей. Получатели - ничего не знают о источнике. Получателей сообщения определяет тип сообщения.

 

Вот реальный пример использования. Системный сервис рассылает всем существующим сервисам (потомкам base_service) какие-то синхронизационные сообщения. Системный сервис ничего не знает о количестве потомков базового. Базовые - ничего о системном. Единственное, что их связывает - тип сообщения (ClSystemMessage), который один может отправить, а другой - получить

 

 

base_service.h
///@class ClBaseService
///@brief Base class for services.
/// All classes, which inherited from ClBaseService will receive message from SystemService
class ClBaseService: public TClMulticastHandler<ClSystemMessage>
{
* * *
    ///@brief Called when receive message from SystemService
    ///@param clSend_ - message from SystemService
    int ProcessMulticast(ClSystemMessage& clSend_);
* * *
};

 

 

system_service.h

///@class ClSystemService
///@brief System service of environment.
class ClSystemService: public TClSingleton<ClSystemService>
{
  friend class TClSingleton<ClSystemService>;
* * * 
  public:
    ///@brief Send INIT signal to all services
    ///@return Count of detected errors
    int InitServices()
    {
      DebugOut("[SystemService] Init services:\n");

      // Send INIT message
      ClSystemMessage clInitMessage_(ClSystemMessage::E_INIT);
      int i32ServiceCount_ = TClMulticastEventMech<ClSystemMessage>::EmitEvent(clInitMessage_);
      
      DebugOut("**[Init %i services, %i errors]\n\n", i32ServiceCount_, clInitMessage_.u32ErrorCounter_m);
      return i32ServiceCount_;
    }
* * *
};

 

Прошу прощения за "портянку", почему-то не удается ни архив, ни заголовочник прикрепить

#ifndef __MULTICAST_EVENT_HEADER_H__
#define __MULTICAST_EVENT_HEADER_H__

#include "sync_obj.h"
#include "singleton.h"

#include <stdint.h>

namespace ENV
{

template <class SEND_MSG>
class TClMulticastEventMech;

///@class TClMulticastHandler
///@brief Parent class for objects, which want use multicast messages
template <class SEND_MSG>
class TClMulticastHandler
{
  friend class TClMulticastEventMech<SEND_MSG>;
  protected:
    /// Protected constructor
    TClMulticastHandler();
    /// Protected virtual destructor
    virtual ~TClMulticastHandler();
    
    ///@brief Abstract method for message receiption. 
    /// Will be called when someone send message with appropriate types
    /// @param clSend_ - parameter type SEND_MSG. Read only
    /// @return can't defined. Value may be used in users purposes
    virtual int ProcessMulticast(SEND_MSG& clSend_)= 0;
};


///@class TClMulticastEventMech
///@brief Pool of requestors for one-type messages
template <class SEND_MSG>
class TClMulticastEventMech:public TClSingleton<TClMulticastEventMech<SEND_MSG> >
{
  typedef       TClMulticastHandler<SEND_MSG> THandler;
  friend class  TClSingleton<TClMulticastEventMech<SEND_MSG> >;
  friend class  TClMulticastHandler<SEND_MSG>;

  public:
    ///@brief Max count of handlers for one-type message
    static const uint32_t MAX_HANDLERS = 32;
    
    ///@brief Send message to all registered requestors
    ///@param pclSend_ - pointer to const message
    static int EmitEvent(SEND_MSG& clSend_)
    {
      return TClMulticastEventMech<SEND_MSG>::Instance()->EmitEvent_nonstatic(clSend_);
    }                      
    
    ///@brief Send message to all registered requestors, except sender
    ///@param pclSend_ - pointer to const message
    ///@param pclSender_ - pointer to sender
    static int EmitEvent(SEND_MSG& clSend_, THandler* pclSender_)
    {
      return TClMulticastEventMech<SEND_MSG>::Instance()->EmitEvent_nonstatic(clSend_, pclSender_);
    }
    
    ///@brief Send message to all registered requestors, except sender
    ///@param pclSend_ - pointer to const message
    ///@param pclSender_ - pointer to sender
    static int EmitEvent(SEND_MSG& clSend_, void* pclSender_)
    {
      return TClMulticastEventMech<SEND_MSG>::Instance()->EmitEvent_nonstatic(clSend_);
    }

       
  private:
    ///Pool of requestors
    THandler* apclHandlers_m[MAX_HANDLERS];

    ///Count of registered requestors
    uint32_t      u32CountHandlers_m;
  
    ///Private constructor. Create object can only TClSingleton 
    TClMulticastEventMech():u32CountHandlers_m(0){}
    
    ///Register reqiestor
    ///@param pclHandler_ - pointer to requestor object
    int Register(THandler* pclHandler_);

    ///Unregister requestor
    ///@param pclHandler_ - pointer to requestor object
    int UnRegister(THandler* pclHandler_);

    ///Hide copy constructor   
    TClMulticastEventMech(const TClMulticastEventMech&){}               
    
    ///@brief Send message to all registered requestors
    ///@param pclSend_ - pointer to const message
    ///@param pclRecv_ - pointer to message (usually use as reply) 
    inline int EmitEvent_nonstatic(SEND_MSG& clSend_)
    {
      //Process all requestors 
      for (uint32_t i=0; i<u32CountHandlers_m; ++i)
        apclHandlers_m[i]->ProcessMulticast(clSend_);
      return u32CountHandlers_m;
    }                  
    
    ///@brief Send message to all registered requestors, except sender
    ///@param pclSend_ - pointer to const message
    ///@param pclSender_ - pointer to sender 
    inline int EmitEvent_nonstatic(SEND_MSG& clSend_, THandler* pclSender_)
    {
      //Process all requestors 
      for (uint32_t i=0; i<u32CountHandlers_m; ++i)
        if (apclHandlers_m[i] != pclSender_)
          apclHandlers_m[i]->ProcessMulticast(clSend_);
          
      return u32CountHandlers_m;
    }
};


////////////////////////////////////////////////////////////////////////////////    
template <class SEND_MSG>    
  int TClMulticastEventMech<SEND_MSG>::Register(TClMulticastEventMech::THandler* pclHandler_)
  {
    TClCriticalSection<true> clCS_;

    //Check overfull requestors list
    if (u32CountHandlers_m == MAX_HANDLERS) 
      Halt("TClMulticastEventMech::Register(): Count of handlers are big");

    apclHandlers_m[u32CountHandlers_m] = pclHandler_;
    return ++u32CountHandlers_m;
  }

////////////////////////////////////////////////////////////////////////////////
template <class SEND_MSG>
  int TClMulticastEventMech<SEND_MSG>::UnRegister(TClMulticastEventMech::THandler* pclHandler_)
  {
    Halt("TClMulticastEventMech::UnRegister: Unsupported!\n");
    return 0;
  }      


////////////////////////////////////////////////////////////////////////////////
template <class SEND_MSG>
  TClMulticastHandler<SEND_MSG>::TClMulticastHandler()
  {
    TClMulticastEventMech<SEND_MSG>::Instance()->Register(this);
  }
  

////////////////////////////////////////////////////////////////////////////////
template <class SEND_MSG>
  TClMulticastHandler<SEND_MSG>::~TClMulticastHandler()
  {
    TClMulticastEventMech<SEND_MSG>::Instance()->UnRegister(this);
  }


} //namespace ENV

#endif //__MULTICAST_EVENT_HEADER_H__

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

С со статической аллокацией - вот правильный выбор программиста микроконтроллеров.

 

С ОЗУ до 4kBytes - да, но новые МК имеют уже под сотню kBytes, как не вертись, тут увы, Вы не правы!

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

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

 

 

Ага, сначала создавать себе проблемы, используя С++, а потом мужественно их решать :)

 

 

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

 

 

Все ваши высказывания от недостаточного знания и опыта работы с С++.

 

1. По поводу статической аллокации в С. И что тут такого сильно удобного? А если памяти не хватает все сразу и навсегда разместить? Не сталкивались с таким?

 

2. В С++ ошибки при работе с динамической памятью как раз резко сократились за счет того, что многие вещи делает компилятор автоматически (конструктор/деструктор).

 

3. Сам потратил много сил на перевод фирменных изделий с концепции С со статическим размещением переменных на С++ со всеми его вкусностями. Да, требуется время для продумывания концепции, реализации и отладки своих библиотек классов. Зато потом наступает полная лепота при необходимости расширения функционала устройства. Быстро и просто.

 

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

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

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

Потому что разработчики компиляторов умные, а программист только-только Hello World освоил. :) Pascal - это хорошо, но лучше Java или .NET. Вообще во всём мире С++ верно помирает, а вы наоборот в контроллеры его суете.

 

Все ваши высказывания от недостаточного знания и опыта работы с С++.

На С++ я успел поработать и мне не понравилось. И шаблоны, и ОО, и перегрузки - основные фичи С++ - разводят страшную грязь в коде. Да, при хорошем опыте программист научается ходить по этим граблям, но новый программист в проекте будет разбираться очень долго. :)

 

1. По поводу статической аллокации в С. И что тут такого сильно удобного? А если памяти не хватает все сразу и навсегда разместить? Не сталкивались с таким?

 

2. В С++ ошибки при работе с динамической памятью как раз резко сократились за счет того, что многие вещи делает компилятор автоматически (конструктор/деструктор).

Один говорит, что статическая аллокация хороша при малом объеме ОЗУ. Другой наоборот. Да, бывает, что без динамической аллокации не обойтись - тогда приходится идти на компромисс.

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

 

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

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

А я так думаю наоборот - прячут с глаз долой всё лишнее и второстепенное, мешающее целостному восприятию кода.

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

... но лучше Java или .NET...

 

... И шаблоны, и ОО, и перегрузки - основные фичи С++ - разводят страшную грязь в коде. Да, при хорошем опыте программист научается ходить по этим граблям, но новый программист в проекте будет разбираться очень долго. :)

Так и шаблоны и перегрузки и ОО есть и в вашем любимом .NET. Получается, что в С++ эти фичи разводят грязь, а в .NET все чистенько? Это при том, что синтаксис C# очень близок к С++.

 

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

Присоединяйтесь к обсуждению

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

Гость
К сожалению, ваш контент содержит запрещённые слова. Пожалуйста, отредактируйте контент, чтобы удалить выделенные ниже слова.
Ответить в этой теме...

×   Вставлено с форматированием.   Вставить как обычный текст

  Разрешено использовать не более 75 эмодзи.

×   Ваша ссылка была автоматически встроена.   Отображать как обычную ссылку

×   Ваш предыдущий контент был восстановлен.   Очистить редактор

×   Вы не можете вставлять изображения напрямую. Загружайте или вставляйте изображения по ссылке.

×
×
  • Создать...