полиморфизм функций
От: Alexander Pazdnikov  
Дата: 01.04.07 11:18
Оценка:
Доброго времени суток.

Реализован полиморфизм функций на С++ в стиле С, не в стиле С++.
Т.е. пришел пакет, определили номер запрошенной функции, получили указатель на реализацию соответсвующей функции и вызвали ее по указателю.

Как можно сделать более красиво абстракцию вызова функции-обработки, заместо
retval = (this->*(mbfPtr->funcPtr))(rcv_buf, rcv_size, snd_buf, snd_size, dev_no);

Использовал и такой способ
swith(<номер функции>){
case 1: session_close(...) break;
case 2: session_open(...) break;
/// и т.д.
default: <создание ответа ошибки>;
}
но как-то уж смотрелось это все некрасиво

Привожу сам вид кода

#include <stdint.h>
// Структура данных функции
class FN_SPEC
{
public:
    uint8_t    func_id;  // номер функции
    int (MyClass::*funcPtr)(uint8_t *, uint32_t *, uint8_t *, uint32_t *, int32_t);
    // и другие параметры
};

FN_SPEC    func_spec[] = {
    {1, &MyClass::session_close,},
    {2, &MyClass::session_open,},
    {3, &MyClass::read_output_status,},
    {4, &MyClass::read_input_status,},
    
    // и т.д. 127 функций
    // End-Terminator
    {0, NULL}
}

// Функция возвращает указатель на структуру данных функции
// по ее  номеру, или NULL если такого номера нет в массиве
// данных функций.
//
FN_SPEC    *get_func_struct(uint8_t func_id)
{
    int    i;

    for(i = 0; func_spec[i].func_id != 0; i++){
        if (func_spec[i].func_id == func_id)
            /* Нашли структуру данных функции, возвращаем указатель на нее */
            return func_spec + i;
    }
    return NULL;
}

class MyClass
{
public:
    /// функция - обертка для вызова соответствующей обработки функций протокола
    int call_function(uint8_t *rcv_buf, uint32_t *rcv_size, uint8_t *snd_buf, uint32_t *snd_size, int32_t dev_no);
public:
    /// \name Функции протокола
    /// Входные параметры для всех функций одинаковые
    /// \param[in] rcv_buf буфер принятого пакета \param[in] rcv_size размер принятого пакета
    /// \param[out] snd_buf буфер пакета ответа \param[out] snd_size размер пакета ответа
    /// \param[in] dev_no номер устройства обработки пакета
    ///@{
    int sessioon_close(uint8_t *rcv_buf, uint32_t *rcv_size, uint8_t *snd_buf, uint32_t *snd_size, int32_t dev_no);
    int session_open(uint8_t *rcv_buf, uint32_t *rcv_size, uint8_t *snd_buf, uint32_t *snd_size, int32_t dev_no);
    int read_output_status(uint8_t *rcv_buf, uint32_t *rcv_size, uint8_t *snd_buf, uint32_t *snd_size, int32_t dev_no);
    int read_input_status(uint8_t *rcv_buf, uint32_t *rcv_size, uint8_t *snd_buf, uint32_t *snd_size, int32_t dev_no);
    // и другие функции
    ///@}
public:
    uint8_t rcv_buf[MAX_PACKET_SIZE], snd_buf[MAX_PACKET_SIZE]
}

int MyClass::call_function(uint8_t *rcv_buf, uint32_t *rcv_size, uint8_t *snd_buf, uint32_t *snd_size, int32_t dev_no)
{
    int retval = RET_OK;
    FN_SPEC *mbfPtr = NULL;
    
    try{
        // получим справочную структуру данных функции
        if (!(mbfPtr = get_func_struct(rcv_buf[1]))){
            throw(ERR_INVALID_FUNCTION);
        }

        // в зависимости от номера функции в пакете
        // запускаем на обработку пакета соответствующую функцию
        // выполним соотвествующую функцию
        // если она задана
        if (mbfPtr->funcPtr){
            /// ВОТ ОНО КЛЮЧЕВОЕ, КОТОРОМУ И ПРИНАДЛЕЖИТ ВОПРОС
            /// Искусственный полиморфизм вызова функции по указателю
            /// Как можно сделать более карасиво и эллегантнее????????????????
            retval = (this->*(mbfPtr->funcPtr))(rcv_buf, rcv_size, snd_buf, snd_size, dev_no);
        }else{
            throw(ERR_FUNCTION_NOT_IMPLEMENTED);
        }
        
        // все в порядке, отправляем ответ
        retval = MB_OK;
        
    }catch(int &e){
        // тут обработка ответа - ошибки
    }

    // выход, чтобы отправить ответ
    return retval;
}
Re: полиморфизм функций
От: Alexander Pazdnikov  
Дата: 01.04.07 11:34
Оценка:
Может быть можно сделать более красиво с использованием шаблонов или других конструкций языка, вместо
(this->*(mbfPtr->funcPtr))(rcv_buf, rcv_size, snd_buf, snd_size, dev_no);
Re[2]: полиморфизм функций
От: NikeByNike Россия  
Дата: 01.04.07 13:32
Оценка: 1 (1)
Здравствуйте, Alexander Pazdnikov, Вы писали:

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

AP>(this->*(mbfPtr->funcPtr))(rcv_buf, rcv_size, snd_buf, snd_size, dev_no);

Я в подобном случае для абстракции использую объекты функции на подобии boost::function. Это то?
Во всяком случае я бы скорее всего сделал обертку над: (this->*(mbfPtr->funcPtr)), для лучшей читабельности.
Нужно разобрать угил.
Re: полиморфизм функций
От: hVostt Россия http://hvostt.ru
Дата: 01.04.07 16:17
Оценка: 2 (1)
Здравствуйте, Alexander Pazdnikov, Вы писали:

AP>Как можно сделать более красиво абстракцию вызова функции-обработки, заместо

AP>retval = (this->*(mbfPtr->funcPtr))(rcv_buf, rcv_size, snd_buf, snd_size, dev_no);

AP>Использовал и такой способ

AP>swith(<номер функции>){
AP> case 1: session_close(...) break;
AP> case 2: session_open(...) break;
AP> /// и т.д.
AP> default: <создание ответа ошибки>;
AP>}
AP>но как-то уж смотрелось это все некрасиво

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

#define MyClassFuncParams uint8_t *rcv_buf, uint32_t *rcv_size, uint8_t *snd_buf, uint32_t *snd_size, int32_t dev_no

class MyClass {
    int (MyClass::*lpfn)(MyClassFuncParams);
public:
    MyClass(int fn_id)
    {
        switch(fn_id)
        {
            case 1: lpfn = session_close;
            ...
        }
    }
    int sessioon_close(MyClassFuncParams);
    int session_open(MyClassFuncParams);
    int read_output_status(MyClassFuncParams);
    int read_input_status(MyClassFuncParams);
    operator int(MyClassFuncParams)
    {
        return this->*lpfn(MyClassFuncParams)
    }
    ...
}
#undef MyClassFuncParams


извращатся.. так чтоб понятно и удобно было!
поливызов абстрагирован
-- fallen in offline... silent
... помни.. ты лишь один из нас
специализация — удел насекомых... (с) Р. Хайнлайн
Re[3]: полиморфизм функций
От: Alexander Pazdnikov  
Дата: 02.04.07 04:12
Оценка:
Здравствуйте, NikeByNike, Вы писали:

NBN>Я в подобном случае для абстракции использую объекты функции на подобии boost::function. Это то?

NBN>Во всяком случае я бы скорее всего сделал обертку над: (this->*(mbfPtr->funcPtr)), для лучшей читабельности.

Здравствуйте, Никита.

Признаюсь, boost'ом пока что не пользовался, хотя с библиотеками ознакомился.
Возможно, действительно, boost::function — это то что надо, посмотрю, Спасибо.
Re[2]: полиморфизм функций
От: Alexander Pazdnikov  
Дата: 02.04.07 04:18
Оценка:
Здравствуйте, Алексей

V>
V>#define MyClassFuncParams uint8_t *rcv_buf, uint32_t *rcv_size, uint8_t *snd_buf, uint32_t *snd_size, int32_t dev_no

V>class MyClass {
V>    int (MyClass::*lpfn)(MyClassFuncParams);
V>public:
V>    MyClass(int fn_id)
V>    {
V>        switch(fn_id)
V>        {
V>            case 1: lpfn = session_close;
V>            ...
V>        }
V>    }
V>    int sessioon_close(MyClassFuncParams);
V>    operator int(MyClassFuncParams)
V>    {
V>        return this->*lpfn(MyClassFuncParams)
V>    }
V>    ...
V>}
V>#undef MyClassFuncParams
V>


Вопрос №1.
Конструктор принимает номер функции и устанавливает соответсвующий указатель.
Т.е. объект может использоваться только для вызова одной функции.
Мне кажется, что это немного расточительно в плане ресурсов, тем более что используется на ARM(не супер быстрый процессор)

Вопрос №2
Как должен быть организован вызов оператора (int) чтобы выполнить функцию. Можно пример?
Re: полиморфизм функций
От: Alexander Pazdnikov  
Дата: 02.04.07 04:27
Оценка:
Еще добавлю небольшую постановку задачи.

Задан протокол с использованием 127-ми функций (причем есть разрывы, т.е. например функции 20-50 не реализованы и зарезервированы)

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

Так вот. Все функции специфицированы, изменения спецификации протокола практически невозможны(в ттеории — да, на практике — нет), возможно только расширение набора функций. Т.е. соответсвие <функция №> — <функция обработчик> задается на уровне компиляции.

Возможно ли использование других языковых конструкций C++ для задания таких связок. Может быть можно использовать какие-то контейнеры с инициализацией на этапе компиляции, чтобы выглядело это как С++, а не как С.
Re[3]: полиморфизм функций
От: hVostt Россия http://hvostt.ru
Дата: 02.04.07 11:12
Оценка: 2 (1)
Здравствуйте, Alexander Pazdnikov, Вы писали:

V>>#define MyClassFuncParams uint8_t *rcv_buf, uint32_t *rcv_size, uint8_t *snd_buf, uint32_t *snd_size, int32_t dev_no

V>>class MyClass {
V>>    int (MyClass::*lpfn)(MyClassFuncParams);
V>>public:
V>>    MyClass(int fn_id)
V>>    {
V>>        switch(fn_id)
V>>        {
V>>            case 1: lpfn = session_close;
V>>            ...
V>>        }
V>>    }
V>>    int sessioon_close(MyClassFuncParams);
V>>    operator int(MyClassFuncParams)
V>>    {
V>>        return this->*lpfn(MyClassFuncParams)
V>>    }
V>>    ...
V>>}
V>>#undef MyClassFuncParams


AP>Вопрос №1.

AP>Конструктор принимает номер функции и устанавливает соответсвующий указатель.
AP>Т.е. объект может использоваться только для вызова одной функции.
Ты же просил полиформизм.. Если ты хотел изменяемое поведение объекта класса, то это уже из другой области.
Кроме того:
1. Создаешь объект на стеке и пользуешься им.. накладки минимальны.
2. Ограничиваешь класс минимальной реализацией нужного те полиформизма.

AP>Мне кажется, что это немного расточительно в плане ресурсов, тем более что используется на ARM(не супер быстрый процессор)

Один лишний указатель. Оптимизацию на выбор нужной функции по её номеру инкапсулируешь непосредстенно в конструкторе класса.
Вызов — одно разыменование, куда быстрее для данной задачи?

AP>Вопрос №2

AP>Как должен быть организован вызов оператора (int) чтобы выполнить функцию. Можно пример?
в подробности твоей задачи не вникал, отвечаю только на поставленный тобой конкретный вопрос:

int Doing(int number_1, int number_2 ...)
{
    MyClass myc_1(number_1), myc_2(number_2);    
    // здесь будет заказанный по номеру вызов.. которым можно пользоваться
    // сколько угодно раз до уничтожения класса..
    int result = myc_1(rcv_buf, rcv_size, snd_buf, snd_size, dev_no);
    int result_2 = myc_2(rcv_buf, rcv_size, snd_buf, snd_size, dev_no);    
    ...    
}
-- fallen in offline... silent
... помни.. ты лишь один из нас
специализация — удел насекомых... (с) Р. Хайнлайн
Re[4]: полиморфизм функций
От: Аноним  
Дата: 02.04.07 11:32
Оценка:
Здравствуйте, hVostt, Вы писали:

Здравствуйте, Алексей.
AP>>Вопрос №1.
AP>>Конструктор принимает номер функции и устанавливает соответсвующий указатель.
AP>>Т.е. объект может использоваться только для вызова одной функции.

V>Кроме того:

V> 1. Создаешь объект на стеке и пользуешься им.. накладки минимальны.
V> 2. Ограничиваешь класс минимальной реализацией нужного те полиформизма.

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

AP>>Мне кажется, что это немного расточительно в плане ресурсов, тем более что используется на ARM(не супер быстрый процессор)

V>Один лишний указатель. Оптимизацию на выбор нужной функции по её номеру инкапсулируешь непосредстенно в конструкторе класса.
V>Вызов — одно разыменование, куда быстрее для данной задачи?
64-128 кб выделения памяти и инициализации объекта (ctor) — это расточительно, класс большой, задержка получается приличная, вплоть до 1 сек. Поэтому такой объект класса создается один раз при подключении и садиться на линию связи клиентом(иерархия классов — т.к. линии бывают разные)

AP>>Вопрос №2

AP>>Как должен быть организован вызов оператора (int) чтобы выполнить функцию. Можно пример?
V>в подробности твоей задачи не вникал, отвечаю только на поставленный тобой конкретный вопрос:

V>
V>int Doing(int number_1, int number_2 ...)
V>{
V>    MyClass myc_1(number_1), myc_2(number_2);    
V>    // здесь будет заказанный по номеру вызов.. которым можно пользоваться
V>    // сколько угодно раз до уничтожения класса..
V>    int result = myc_1(rcv_buf, rcv_size, snd_buf, snd_size, dev_no);
V>    int result_2 = myc_2(rcv_buf, rcv_size, snd_buf, snd_size, dev_no);    
V>    ...    
V>}
V>

Спасибо. Это полезно.

Действительно интересна реализация, как ты и преложил,
V>Ты же просил полиформизм.. Если ты хотел изменяемое поведение объекта класса, то это уже из другой области.
вот это ближе к истине, есть какие-нибудь идеи в этой области, или ссылки может быть кинешь?
Премного благодарен (Кой чему обучился ).
Re[5]: полиморфизм функций
От: Alexander Pazdnikov  
Дата: 02.04.07 11:37
Оценка:
Забыл залогиниться, извиняюсь.
Re[2]: полиморфизм функций
От: hVostt Россия http://hvostt.ru
Дата: 02.04.07 13:39
Оценка: 1 (1)
Здравствуйте, Alexander Pazdnikov, Вы писали:

AP>Задан протокол с использованием 127-ми функций (причем есть разрывы, т.е. например функции 20-50 не реализованы и зарезервированы)

[..skiphead..]
AP>Так вот. Все функции специфицированы, изменения спецификации протокола практически невозможны(в ттеории — да, на практике — нет), возможно только расширение набора функций. Т.е. соответсвие <функция №> — <функция обработчик> задается на уровне компиляции.

AP>Возможно ли использование других языковых конструкций C++ для задания таких связок. Может быть можно использовать какие-то контейнеры с инициализацией на этапе компиляции, чтобы выглядело это как С++, а не как С.


#define FUNC_DECL(name) int name(int i, int* pi, int **ppi)

FUNC_DECL(myfun1)
{
    cout << "i'm first!!!";
}

FUNC_DECL(myfun2)
{
    cout << "i'm second!";
}

FUNC_DECL(myfunc_zero)
{
    throw "wrong call";
}

typedef FUNC_DECL((*func_decl));

func_decl func_list[127] = {
    myfun1,
    myfun2,
    myfunc_zero,
    myfunc_zero,
    myfunc5,
// и поехал...
};


я щитаю, там где можно сделать просто, незачем делать как-то ещё. конечно тут есть свои минусы,
однако всё нативно понятно, и легко программируется. чего ещё желать?
а не реализованные дыры затыкай пустышкой типа myfunc_zero, которая тупо кидает исключение
и будет тебе щастье
-- fallen in offline... silent
... помни.. ты лишь один из нас
специализация — удел насекомых... (с) Р. Хайнлайн
Re[3]: полиморфизм функций
От: Alexander Pazdnikov  
Дата: 02.04.07 13:50
Оценка:
Здравствуйте, hVostt, Вы писали:

Здравствуйте, Алексей.

V>я щитаю, там где можно сделать просто, незачем делать как-то ещё. конечно тут есть свои минусы,

V>однако всё нативно понятно, и легко программируется. чего ещё желать?
V>а не реализованные дыры затыкай пустышкой типа myfunc_zero, которая тупо кидает исключение
V>и будет тебе щастье

Интересна реализация с классом и его оператором (int).
Текущий принцип реализации так и построен на базе виртуалиции аля Си.(или функторов, как их называют)
Вообще, полностью с тобой согласен, лучшее — враг хорошего. Но тяга к знаниям осталась, чувствую — что есть решение более эллегантное и крассивое; как for_each, вместо итерационного обхода массива и контроля его границ вручную.

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

(В чем сила — брат? — Сила в знаниях — брат. )
Re[5]: полиморфизм функций
От: hVostt Россия http://hvostt.ru
Дата: 02.04.07 14:32
Оценка: 2 (1) +1
Здравствуйте, <Аноним>, Вы писали:


V>>Кроме того:

V>> 1. Создаешь объект на стеке и пользуешься им.. накладки минимальны.
V>> 2. Ограничиваешь класс минимальной реализацией нужного те полиформизма.
А>Объект на стеке создать опасно, сам класс занимает примерно 64-128 кб с учетом всех буферов и реализаций ~50-ти функций. (Опасно, т.к. можем обратиться за данными не к следующей странице границы стека, на которой стоит ловушка, а дальше, соотвественно сегментейшн фолт и много времени с дебаггером, это уже проходили, большие стековые переменные создавать нельзя).
небольшой объект таки можно (см. п.2) если в нём заключен только механизм ручного полиформизма..
используй принцип интерфейсов на том факте, что можно так:

MyClass myc(number);
myc = MyClass(number2);

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

AP>>>Мне кажется, что это немного расточительно в плане ресурсов, тем более что используется на ARM(не супер быстрый процессор)

V>>Один лишний указатель. Оптимизацию на выбор нужной функции по её номеру инкапсулируешь непосредстенно в конструкторе класса.
V>>Вызов — одно разыменование, куда быстрее для данной задачи?
А>64-128 кб выделения памяти и инициализации объекта (ctor) — это расточительно, класс большой, задержка получается приличная, вплоть до 1 сек. Поэтому такой объект класса создается один раз при подключении и садиться на линию связи клиентом(иерархия классов — т.к. линии бывают разные)
свой класс с большими данными пусть будет один, пусть он юзает интерфейс MyClass, который из данных ранит всего лишь один указатель (ну и возможно статический массив указателей на функции).

V>>Ты же просил полиформизм.. Если ты хотел изменяемое поведение объекта класса, то это уже из другой области.

А>вот это ближе к истине, есть какие-нибудь идеи в этой области, или ссылки может быть кинешь?
готовое решение скорее всего есть.. возможно в boost::function
но ИМХО это лишнее, тут проблемы никакой нет, чтобы можно было самому всё организовать, это даже велосипедом не назовёшь

А>Премного благодарен (Кой чему обучился ).

я тоже
-- fallen in offline... silent
... помни.. ты лишь один из нас
специализация — удел насекомых... (с) Р. Хайнлайн
Re[6]: полиморфизм функций
От: Alexander Pazdnikov  
Дата: 03.04.07 04:07
Оценка:
Здравствуйте, hVostt, Вы писали:

Здравствуйте, Алексей.

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

Спасибо за развернутые аргументированные и конструктивные ответы.

Удачи.

С уважением,
Александр Паздников
Re[4]: полиморфизм функций
От: Аноним  
Дата: 03.04.07 15:35
Оценка: 2 (1)
Здравствуйте, Alexander Pazdnikov, Вы писали:

AP>Здравствуйте, hVostt, Вы писали:


AP>Здравствуйте, Алексей.


V>>я щитаю, там где можно сделать просто, незачем делать как-то ещё. конечно тут есть свои минусы,

V>>однако всё нативно понятно, и легко программируется. чего ещё желать?
V>>а не реализованные дыры затыкай пустышкой типа myfunc_zero, которая тупо кидает исключение
V>>и будет тебе щастье

AP>Интересна реализация с классом и его оператором (int).

AP>Текущий принцип реализации так и построен на базе виртуалиции аля Си.(или функторов, как их называют)
AP>Вообще, полностью с тобой согласен, лучшее — враг хорошего. Но тяга к знаниям осталась, чувствую — что есть решение более эллегантное и крассивое; как for_each, вместо итерационного обхода массива и контроля его границ вручную.

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


AP>(В чем сила — брат? — Сила в знаниях — брат. )


Ну так сделай std::map<char, MyIntClass>, где char — это ключ с номером функции (0-127), а MyIntClass реализует функтор через оператор()
Если все делать на const-ссылках и инстанциировать std::map<char, MyIntClass> не слишком часто, то все будет ОК, как по памяти, так и по скорости.
 
Подождите ...
Wait...
Пока на собственное сообщение не было ответов, его можно удалить.