Реализован полиморфизм функций на С++ в стиле С, не в стиле С++.
Т.е. пришел пакет, определили номер запрошенной функции, получили указатель на реализацию соответсвующей функции и вызвали ее по указателю.
Как можно сделать более красиво абстракцию вызова функции-обработки, заместо
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;
}
Может быть можно сделать более красиво с использованием шаблонов или других конструкций языка, вместо
(this->*(mbfPtr->funcPtr))(rcv_buf, rcv_size, snd_buf, snd_size, dev_no);
Здравствуйте, Alexander Pazdnikov, Вы писали:
AP>Может быть можно сделать более красиво с использованием шаблонов или других конструкций языка, вместо AP>(this->*(mbfPtr->funcPtr))(rcv_buf, rcv_size, snd_buf, snd_size, dev_no);
Я в подобном случае для абстракции использую объекты функции на подобии boost::function. Это то?
Во всяком случае я бы скорее всего сделал обертку над: (this->*(mbfPtr->funcPtr)), для лучшей читабельности.
Здравствуйте, 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
... помни.. ты лишь один из нас
Здравствуйте, NikeByNike, Вы писали:
NBN>Я в подобном случае для абстракции использую объекты функции на подобии boost::function. Это то? NBN>Во всяком случае я бы скорее всего сделал обертку над: (this->*(mbfPtr->funcPtr)), для лучшей читабельности.
Здравствуйте, Никита.
Признаюсь, boost'ом пока что не пользовался, хотя с библиотеками ознакомился.
Возможно, действительно, boost::function — это то что надо, посмотрю, Спасибо.
Вопрос №1.
Конструктор принимает номер функции и устанавливает соответсвующий указатель.
Т.е. объект может использоваться только для вызова одной функции.
Мне кажется, что это немного расточительно в плане ресурсов, тем более что используется на ARM(не супер быстрый процессор)
Вопрос №2
Как должен быть организован вызов оператора (int) чтобы выполнить функцию. Можно пример?
Задан протокол с использованием 127-ми функций (причем есть разрывы, т.е. например функции 20-50 не реализованы и зарезервированы)
Номер функции определяется по второму байту пришедшего пакета запроса.
На основании номера функции в запросе нужно вызвать на обработку соответсвующую функцию, которая обработает оставшиеся данные запроса и сформирует ответ.
Так вот. Все функции специфицированы, изменения спецификации протокола практически невозможны(в ттеории — да, на практике — нет), возможно только расширение набора функций. Т.е. соответсвие <функция №> — <функция обработчик> задается на уровне компиляции.
Возможно ли использование других языковых конструкций C++ для задания таких связок. Может быть можно использовать какие-то контейнеры с инициализацией на этапе компиляции, чтобы выглядело это как С++, а не как С.
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>Ты же просил полиформизм.. Если ты хотел изменяемое поведение объекта класса, то это уже из другой области.
вот это ближе к истине, есть какие-нибудь идеи в этой области, или ссылки может быть кинешь?
Премного благодарен (Кой чему обучился ).
Здравствуйте, Alexander Pazdnikov, Вы писали:
AP>Задан протокол с использованием 127-ми функций (причем есть разрывы, т.е. например функции 20-50 не реализованы и зарезервированы)
[..skiphead..] AP>Так вот. Все функции специфицированы, изменения спецификации протокола практически невозможны(в ттеории — да, на практике — нет), возможно только расширение набора функций. Т.е. соответсвие <функция №> — <функция обработчик> задается на уровне компиляции.
AP>Возможно ли использование других языковых конструкций C++ для задания таких связок. Может быть можно использовать какие-то контейнеры с инициализацией на этапе компиляции, чтобы выглядело это как С++, а не как С.
я щитаю, там где можно сделать просто, незачем делать как-то ещё. конечно тут есть свои минусы,
однако всё нативно понятно, и легко программируется. чего ещё желать?
а не реализованные дыры затыкай пустышкой типа myfunc_zero, которая тупо кидает исключение
и будет тебе щастье
-- fallen in offline... silent
... помни.. ты лишь один из нас
Здравствуйте, Алексей.
V>я щитаю, там где можно сделать просто, незачем делать как-то ещё. конечно тут есть свои минусы, V>однако всё нативно понятно, и легко программируется. чего ещё желать? V>а не реализованные дыры затыкай пустышкой типа myfunc_zero, которая тупо кидает исключение V>и будет тебе щастье
Интересна реализация с классом и его оператором (int).
Текущий принцип реализации так и построен на базе виртуалиции аля Си.(или функторов, как их называют)
Вообще, полностью с тобой согласен, лучшее — враг хорошего. Но тяга к знаниям осталась, чувствую — что есть решение более эллегантное и крассивое; как for_each, вместо итерационного обхода массива и контроля его границ вручную.
Спасибо за комментарии, про классы узнал достаточно интересного и нового для себя, возьму на заметку.
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
... помни.. ты лишь один из нас
Здравствуйте, 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> не слишком часто, то все будет ОК, как по памяти, так и по скорости.