Войти
ФлеймФорумПрограммирование

Что не есть "говнокод" ? (8 стр)

Страницы: 17 8 9 1012 Следующая »
#105
21:34, 9 фев. 2012

Кстати вот такой вопрос :
Как лучше представить конструкцию с switch - case'ом , чтобы не слишком говнокодить там ?
Если это касается сообщений обработки клавиш (не комманда для общего GUI , а отдельная клавиша) в WINAPI , то использовать HANDLE_MSG уместнее здесь (так говна вроде меньше :) ) ?


#106
1:45, 10 фев. 2012

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

#107
5:17, 10 фев. 2012

Vendein_RaZoR
> switch - case
свитч? не, не слышал...

Пользуюсь вместо него if-elseif

^)

#108
6:34, 10 фев. 2012

Vendein_RaZoR
> Как лучше представить конструкцию с switch - case'ом , чтобы не слишком
> говнокодить там ?
> Если это касается сообщений обработки клавиш

Я для этих целей глянул в сторону обобщенного программирования. Разработал универсальный механизм "реагирования на нажатия клавиш"

Список участников механизма:

1. Описание нажатой клавиши. Структура, содержащая поля: виртуальный код клавиши, состояние клавиши (включен ли капс, был ли зажат шифт, и тп)

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

3. Собственно сам реактор. Реактор - суть устройство ввода вывода. На входе получает описание клавиши, на выходе - дергает соответствующий метод агента.

Особенности архитектуры:

3.1 Реактор может запускать внутри себя другие реакторы. Допустим, первый реактор обрабатывает только стрелочки (влево/вправо/ввверх/вниз), а второй реактор - символьные клавиши (обычные клавиши, для печати текста + таб/бакспейс). Если клавиша - буковка "а", то реактор стрелочек вернёт "я такое не обрабатываю", тогда будет запущен реактор символьных клавиш, в надежде, что он справится. В общем паттерн "цепочка обязанностей".

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

Юзабилити:

1. Использовать уже готовые мелкие реакторы, кои идут в поставке с библиотекой реакторов.
2. Эти мелкие реакторы можно комбинировать друг с другом как угодно, получая из них один большой новый реактор.
3. Можно создать объект класса "цепочка", указав эти мелкие реакторы, и получить аналогичный большой новый реактор.

4. В документации отражен рекомендованный способ изготовления нового большого реактора, как комбинацию из мелких.
5. В документации отражен рекомендованный способ изготовления нового большого реактора, как комбинацию из мелких, являющихся участниками контейнера "цепочка".
6. В документации отражен рекомендованный способ изготовления мелкого реактора, на случай если понадобилось нечто уникальное, что не вошло в поставку с библиотекой.

7. В документации подробно расписана идея создания, и принцип работы реактора клавиш. Это может пригодится тому, кто будет сопровождать продукт, применивший данный механизм.

8. Документация содержит подробное описание с примерами  "как это можно создать", "как этим пользоваться в целевом проекте", "как устроена библиотека изнутри".

Пример создания большого реактора, содержащий дочерние:

Gen_ReactorKey_Begin(SStandartDirect); //здесь SStandartDirect это тип-имя реактора
    Gen_Reactor_OnlyPressed;                   //обработает только нажатия клавиш (отпускания клавиш будут проигнорированы)
    Gen_ExitEnter;                                      //если была нажата ентер - прекратит обработку, вернув "получил ентер"
    Gen_StartSoReactor(SCharKey);           //запуск дочернего реактора, обрабатывающего символьные клавиши
    Gen_StartSoReactor(SShiftPressed);    //обработка клавиш с одновременном зажатием шифт
    Gen_StartSoReactor(SLeftRightKey);    //обработка стрелочек влево-вправо
    Gen_StartSoReactor(SCtrlKey);            //обработка клавиш с одновременном зажатием кнтрл (не различает левый/правый)
    Gen_StartSoReactor(SDirectKey);        //обработает любые не символьные клавиши
Gen_ReactorKey_End; 

Пример создания мелкого реактора:

//обработчик шифт + стрелочки
Gen_ReactorKey_Begin(SShiftPressed);
    Gen_NeedShiftPressed;                //реактор начнет работу, только если был зажат шифт
    Gen_RKSwitch_Beg;
        Gen_ReactorKey_case(eLeft,   Shift_Left()   );         //запустит агенту метод Shift_Left, если была зажата стрелочка влево
        Gen_ReactorKey_case(eRight,  Shift_Right()  );
        Gen_ReactorKey_case(eHome,   Shift_Home()   );
        Gen_ReactorKey_case(eEnd,    Shift_End()    );
        Gen_ReactorKey_case(eInsert, Shift_Insert() );
    Gen_RKSwitch_End;
Gen_ReactorKey_End;
//обработчик символьных клавиш
Gen_ReactorKey_Begin(SCharKey);
    Gen_NeedCharKey;       // Будет запущен, только если клавиша "символьная"
    Gen_RKSwitch_Beg;
        Gen_ReactorKey_case(eBackSpace, BackSpace() ); 
        Gen_ReactorKey_case(eTab,  Tab() );
    Gen_RKSwitch_EndFunc( PutChar(key.myChar) );  //если клавиша не таб, и не бакспейс, то запустит метод по дефолту
Gen_ReactorKey_End;


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

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

Вот так может выглядеть агент:

class CReactorKey 
{ 
public: 
    CONTRACT(BersKeyboard); //Агент заключает контракт.
                                // Наличие этого контракта гарантирует реактору клавиш, что агент "располагает всем необходимым для работы"
                               //Это чистая формальность. Защита от дурака, что бы в качестве агента не пихали в реактор все подряд
public:
    void PutChar(unsigned char ch)    { ViewInfo(ch); }

    void DoNothing()    { STD; cout<< "DoNothing"    <<endl; } //если вдруг понадобится заглушка. Хотя у меня такого ещё не встречалось
    void Shift_Left()   { STD; cout<< "Shift_Left"   <<endl; }
    void Shift_Right()  { STD; cout<< "Shift_Right"  <<endl; }
    void MoveLeft()     { STD; cout<< "MoveLeft"     <<endl; }
    void MoveRight()    { STD; cout<< "MoveRight"    <<endl; }
    void Shift_End()    { STD; cout<< "Shift_End"    <<endl; }
    void Shift_Home()   { STD; cout<< "Shift_Home"   <<endl; }
    void Shift_Insert() { STD; cout<< "Shift_Insert" <<endl; }
    void BackSpace()    { STD; cout<< "BackSpace"    <<endl; }
    void Tab()          { STD; cout<< "Tab"          <<endl; }
    void End()          { STD; cout<< "End"          <<endl; }
    void Home()         { STD; cout<< "Home"         <<endl; }
    void Delete()       { STD; cout<< "Delete"       <<endl; }
    void Ctrl_Insert()  { STD; cout<< "Ctrl_Insert"  <<endl; }
};

Вот так это может быть использовано в боевом проекте:

void Worker::Work()       
{ 
    ReadyBuf(); SetCursor();   //подготовка буфера ввода.

    typedef Gen_ReactorType(SStandartDirect, Worker) tRKey; // описание чуть ниже
    

    //Gen_ReactorType() создаст конкретный тип данных: 
    //реактор, имеющий тип-имя SStandartDirect ("стандартное управление")
    //а в качестве агента будет использован тип Worker

    //Таким образом, будет создан реактор, заточенный под работу непосредственно с объектами класса Worker
    //И умеющий обрабатывать клавиши "стандартным способом"

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

    while (  tRKey::Work(*this, m_pDeviceIO->Wait_Inkey() )!=tRKey::KeyEndEnter ){   ReDrawField();    } 
}

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

#109
19:15, 10 фев. 2012

Kartonagnick
Любишь говнокодить? Зачем плодить непонятные классы и макросы для такой простой задачи?

>Как лучше представить конструкцию с switch - case'ом , чтобы не слишком говнокодить там ?
Попробуй что-то типа этого:

void OnSpace() {...}
void OnEnter() {...}
//и т. д.

...
int key_table[]={VK_SPACE, VK_ENTER, ...};
(void(*func_table)())[]={OnSpace, OnEnter};
for(int i=0; i<sizeof(key_table)/sizeof(int); i++)
    if(key_table[i]==key) func_table[i]();
Не уверен, что код компилябельный, так как писал в браузере.

#110
20:11, 10 фев. 2012

gammaker
> Любишь говнокодить? Зачем плодить непонятные классы и макросы для такой простой
> задачи?

Люблю сделать 1 раз. Что бы потом затратив минимум усилий, привинтить обработку ЛЮБОЙ клавиши на ЛЮБОЙ обработчик этой клавиши.
В том числе ЛЮБЫЕ сочетания этих клавиш, помноженные на любые возможные "внешние обстоятельства".

Например, мне вдруг очень понадобилось сделать вот так: пользователь нажимает клавиши. И если будет нажата особая комбинация ( IDDQD например), должна сработать особая обработка. Время на изготовление/внедрение такого реактора ушло порядка 5 минут.
И кстати, самому агенту не пришлось ничего "фиксировать". Обо всем позаботился умный реактор. У агента просто дёрнулся метод, который отреагировал на нажатие комбинации.

Ты, учитывая, что у тебя нет никакой архитектуры в твоём локально-топорном решении скукожишься такую обработку прикручивать посредине проекта, со своим не_оптимальным циклом.

gammaker
> Попробуй что-то типа этого:

1. Лямбу не все компили держат.

2. И вот так каждый раз "пробовать что-то типа", как только понадобится очередной обработчик да?
Рожать таблицу клавиш, рожать массив указателей (в с++ указатели на функцию - признак ущербной архитектуры, да да).
И ещё при этом нужно его чем то заполнить. То есть, нужны сведения о реальных адресах функций обработчиков. Где их каждый раз брать?
Где каждый раз брать информацию о имеющихся обработчиках той, или иной клавиши?
Транспортировку данных ты тоже каждый раз заново делать предлагаешь?
А где именно будет располагаться механизм определения клавиши? Каждый раз заново об это думать? И пилить под каждую отдельную ситуацию отдельно, да?

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

А если функция-обработчик в состоянии обработать не один конкретный кей, а сразу целый набор клавишь? Допустим, одна функция-обработчик на все "символьные" клавиши.
А если нужна обработка не просто клавишь, а их комбинаций? Каких то особых состояний?
А если нужна обработка определенных последовательностей?
А если нужна обработка клавишь, одновременно зажатых с другими? Например левый или правый контрл+f1?

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

предлагаешь выполнять всю эту гремучую процедуру каждый раз заново под каждый конкретный случай, да ещё при возможных модификациях "допиши/перепиши" делать прямо на месте?

Вот эта вот портянка в сферическоом вакууме:

int key_table[]={VK_SPACE, VK_ENTER, ...};
(void(*func_table)())[]={OnSpace, OnEnter};
for(int i=0; i<sizeof(key_table)/sizeof(int); i++)
    if(key_table[i]==key) func_table[i]();

И есть говнокод. Потому что:
1. Не масштабируется.
2. Вынуждает пользователя втыкать в детали реализации.
3. Тяжело переносит возможные модификации.

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

Задача программиста - написать конкретную реакцию на нажатие. Написать метод, который будет реагировать на это нажатие.
А не создавать один и тот же по сути механизм заново.

зы: макросы использованы, что бы замаскировать шаблоны. Пользователя не касается как работает вся эта кухня. Работа с реакторами клавиш удобная, и надежная. Это масштабируемое решение. А главное - очень быстрое.

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

#111
23:42, 10 фев. 2012

Kartonagnick
Реально, твоя схема слишком сложная.

У меня так:

+ Показать

#112
0:42, 11 фев. 2012

Кто сказал swith?

+ swith.h
#include "swith.h"
#include <iostream>

int main()
{
  int x = 8;
  
  Swith( x )
      ( 1,
        [](){
          std::cout <<"x=1" << std::endl;
          },

        8,
        [](){
          std::cout <<"x=8" << std::endl;
          } );

  return 0;
}
#113
0:50, 11 фев. 2012
int a=b=c=d=100;
#114
1:23, 11 фев. 2012

Try
> template < class E, class F, class ... Args >

Вот это вы вообще откуда взяли?
У меня такое не компилируется...

: error C2332: class: отсутствует имя тега

#115
1:24, 11 фев. 2012

Kartonagnick
не можешь в variadic templates?

#116
1:25, 11 фев. 2012

Kartonagnick
google: variadic templates c++0x

#117
2:25, 11 фев. 2012

Try, вот где простору то для творчества!

ГОВНОКОДИЩЕЕЕ плодить будим!!! Долой макросы, да здравствует variadic templates! Гип гип, урра!!!  Гип гип, урра!!! 

Осталась одна проблема, моя 2008 старушка такое не ведает.
Наверное, упоротый вопрос задаю: хотя бы вижал студия 2010 поддерживает variadic templates?

Я так и не смог загуглить этот вопрос.

#118
4:52, 11 фев. 2012

Kartonagnick
> Я так и не смог загуглить этот вопрос.
http://lmgtfy.com/?q=vs10+c%2B%2B0x
Первая ссылка в выдаче.

#119
5:35, 11 фев. 2012

RPGman
> Первая ссылка в выдаче.
А ты хоть сам эту ссылочку то смотрел? Где там хоть полсловечка о variadic templates ?


2010  экспресс поставил, ругается так же, как и 2008 : error C2332: class: отсутствует имя тега
Я делаю вывод - не поддерживает.

Возникает вопрос: какую взять ИДЕ, которая сразу идет с моральным компилятором, который эту возможность поддерживает?

Страницы: 17 8 9 1012 Следующая »
ФлеймФорумПрограммирование

Тема в архиве.