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

С++. Обработчик прерывания и класс - как связать?

Понял. Спасибо.

Самое главное понять суть.

Я пишу на чистых сях, но вот эти высокоуровневые штуки мне из спортивного интереса интересны.

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


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

51 минуту назад, Arlleex сказал:

Короче, как я понял, это синтаксически засахаренная вот такая штука

Я ещё поясню, на примере. Типа, если на си, то такая конструкция:

// тип - указатель на пользовательский обработчик
typedef void (* VectorHandler)(void *context);

// данные одного обработчика - указатель на функцию и контекст
typedef struct
{
    VectorHandler handler;
    void* context;    
}VectorStruct;

// массив обработчиков (по одному на каждый вектор в таблице векторов. Тоже "немножко" накладных расходов)
#define VECTORS_COUNT (256)
VectorStruct handlers[VECTORS_COUNT] {};

void I2C_Handler(void) // это обработчик, адрес которого помещён в таблицу векторов
{
#define I2C_VECTOR (5)
    VectorStruct* vs = &handlers[I2C_VECTOR];
    vs->handler(vs->context);
}

Виртуальные функции городить не стал - очень муторно это делать на сях.

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


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

18 minutes ago, AHTOXA said:

Так что товарищ Forger слегка слукавил, когда показал ассемблерный листинг :-)

Никакого лукавства, я привел реальный листинг. Свои намеки бросьте!

 

Quote

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

Нет, виртуальный он нафик не нужен. От слова совсем!

Шаблоны никто не отменял, поскольку без них никак не соорудить быстрый и компактный (по ОЗУ) делегата .

Пробейте в гугле по фразе "быстрый делегат C++", сверхбыстрый и т. п.

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

 

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

 

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


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

3 минуты назад, Forger сказал:

Никакого лукавства, я привел реальный листинг. Свои намеки бросьте!

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

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


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

7 minutes ago, Arlleex said:

Я пишу на чистых сях

Раньше тоже ездил на "классике" и на "палке", но это было давно и если когда-то сяду туда, то если вокруг ничего другого не будет )))

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


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

2 минуты назад, AHTOXA сказал:

Я ещё поясню, на примере. Типа, если на си, то такая конструкция...

Ага. Видел такое добро в драйверах под Cortex-A9 для Xilinx Zynq. Там также все сделано (примерно).

 

5 минут назад, AHTOXA сказал:

Виртуальные функции городить не стал - очень муторно это делать на сях.

А я когда-то даже интереса ради пытался уловить их суть и эмулировал на сях:wink2:

Спойлер

#include <stdio.h>

struct figure;
struct triangle;
struct rectangle;
struct circle;

// дескриптор таблицы виртуальных функций
typedef struct
{
    void (*get_name)(void *);
    void (*get_square)(void *);
}VTABLE;

// базовый класс
typedef struct figure
{
    VTABLE *VTable;
    
    void (*figure)(struct figure *);
}figure;

// класс треугольника
typedef struct triangle
{
    figure base;
    
    float a;
    float b;
    
    void (*triangle)(struct triangle *);
}triangle;

// класс прямоугольника
typedef struct rectangle
{
    figure base;
    
    float a;
    float b;
    
    void (*rectangle)(struct rectangle *);
}rectangle;

// класс круга
typedef struct circle
{
    figure base;
    
    float r;
    
    void (*circle)(struct circle *);
}circle;

// реализации виртуальных функций
void get_name_figure(void *This)
{
    printf("This is Figure\n");
    return;
}
void get_name_triangle(void *This)
{
    printf("This is Triangle\n");
    return;
}
void get_name_rectangle(void *This)
{
    printf("This is Rectangle\n");
    return;
}
void get_name_circle(void *This)
{
    printf("This is Circle\n");
    return;
}

void get_square_figure(void *This)
{
    printf("Square for Figure is not may be calculated!\n\n");
    return;
}
void get_square_triangle(void *This)
{
    printf("S triangle = %.3f\n\n", ((triangle *)This)->a*((triangle *)This)->b*0.5);
    return;
}
void get_square_rectangle(void *This)
{
    printf("S rectangle = %.3f\n\n", ((rectangle *)This)->a*((rectangle *)This)->b);
    return;
}
void get_square_circle(void *This)
{
    printf("S circle = %.3f\n\n", ((circle *)This)->r*((circle *)This)->r*3.14);
    return;
}

// таблицы виртуальных функций
VTABLE VTableFigure    = {get_name_figure, get_square_figure};
VTABLE VTableTriangle  = {get_name_triangle, get_square_triangle};
VTABLE VTableRectangle = {get_name_rectangle, get_square_rectangle};
VTABLE VTableCircle    = {get_name_circle, get_square_circle};

// конструкторы
void figureConstructor(figure *This)
{
    This->VTable = &VTableFigure;
    return;
}

void triangleConstructor(triangle *This)
{
    This->base.figure = figureConstructor;
    This->base.figure((figure *)This);
    This->base.VTable = &VTableTriangle;
    This->a           = 0;
    This->b           = 0;
    return;
}

void rectangleConstructor(rectangle *This)
{
    This->base.figure = figureConstructor;
    This->base.figure((figure *)This);
    This->base.VTable = &VTableRectangle;
    This->a           = 0;
    This->b           = 0;
    return;
}

void circleConstructor(circle *This)
{
    This->base.figure = figureConstructor;
    This->base.figure((figure *)This);
    This->base.VTable = &VTableCircle;
    This->r           = 0;
    return;
}


int main(void)
{
    figure figure1;
    figure1.figure = figureConstructor;
    figure1.figure(&figure1);
    
    triangle triangle1;
    triangle1.triangle = triangleConstructor;
    triangle1.triangle(&triangle1);
    triangle1.a = 5;
    triangle1.b = 6;
    
    rectangle rectangle1;
    rectangle1.rectangle = rectangleConstructor;
    rectangle1.rectangle(&rectangle1);
    rectangle1.a = 5;
    rectangle1.b = 6;
    
    circle circle1;
    circle1.circle = circleConstructor;
    circle1.circle(&circle1);
    circle1.r = 5;
    
    figure *mass[] = {&figure1, (figure *)&triangle1, (figure *)&rectangle1, (figure *)&circle1};
    
    for(int i = 0; i < 4; ++i)
    {
        mass[i]->VTable->get_name((void *)mass[i]);
        mass[i]->VTable->get_square((void *)mass[i]);
    }
    
    while(1);
}

:bb:

 

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


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

7 minutes ago, AHTOXA said:

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

Нет, это все. Всего 4 этих команды. Разумеется, с включенной оптимизацией.

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

После BX R1 происходит переход именно на тот метод, который был загружен в делегат, причем в R0 уже загружен соотв. this.

Это довольно хитро сделано, но при этом вполне очевидно. Я сам удивился простоте решения.

 

Чтобы не вдаваться в дебри, поясню на пальцах:

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

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

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

Наверно мудрено объяснил, но меня удивили, насколько все тут просто ))

 

6 minutes ago, Arlleex said:

и эмулировал на сях

все когда-то в юности совершали подобные глупости и потом их стыдились :)

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


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

8 минут назад, Forger сказал:

все когда-то в юности совершали подобные глупости и потом их стыдились :)

Ну почему сразу глупости:acute:

Я вот, например, люблю добираться со сути вещей. И на практике это неоднократно играло мне на руку.

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

 

P.S. C++ не учу по причине банальной лени, скорее всего. А зная механизмы ООП, какая разница на чем писать?

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


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

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

при этом нет никаких взаимных перекрестных инклудов, т.е. эти два класса вообще ничего друг о друге не знают.

При этом вызов происходит напрямую c указанным смехотворным оверхедом.

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

Плата за все это - пямять, ведь делегаты нужно хранить еще и выше по уровню классов, и также выше по уровню их инициализировать.

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

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

 

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


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

15 минут назад, Forger сказал:

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

И как же этот первый параметр попадёт в R0? Святым духом? Или всё-таки будет несколько команд, которые берут адрес переменной, где хранится адрес объекта, загружают оттуда этот адрес и заносят его в R0?

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


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

12 minutes ago, Arlleex said:

Ну почему сразу глупости:acute:

Ключевое слово - эмуляция более высокоуровеного языка. В данном случае это костыль.

Вот в С++ нет встроенного делегата, которые есть как таковой в C#.

Поэтому приходится применять эмуляцию. Стыдно, но на МК нету C# (((

 

9 minutes ago, AHTOXA said:

И как же этот первый параметр попадёт в R0? Святым духом? Или всё-таки будет несколько команд, которые берут адрес переменной, где хранится адрес объекта, загружают оттуда этот адрес и заносят его в R0?

А вы смотрю никак не уйметесь, вот прямо надо таки найти косяк у меня, иначе покоя не будет ))

 

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

LDRD (Load and Store Double instructions), она грузит одновременно R0 и R1. В R0 - this, в R1 - адрес нашего метода.

Компилятор, очевидно ни разу не глупый.

 

Повторю картинку для наглядности:

 

Вызов делегата.jpg

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


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

2 hours ago, Forger said:

Стыдно, но на МК нету C# (((

.Net microframework, но сдаётся мне, MS её как-то не особо развивают (как и многие другие интересные проекты).

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


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

6 minutes ago, alexunder said:

.Net microframework, но сдаётся мне, MS её как-то не особо развивают (как и многие другие интересные проекты).

Давно накрылась медным тазом, страшно тормозная

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


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

11 часов назад, Forger сказал:

А вы смотрю никак не уйметесь, вот прямо надо таки найти косяк у меня, иначе покоя не будет ))

Да просто привык, что у вас постоянные косяки, вот и ожидаю их. В этот раз оказалось не так, поздравляю :-)

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


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

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

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


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

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

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

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

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

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

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

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

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

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