Проектирование вокруг большого числа дискретных функций в C - PullRequest
4 голосов
/ 08 сентября 2011

Приветствия и приветствия,

Я ищу информацию о новых моделях проектирования для работы с большим количеством функций в C99.

Фон:
IЯ работаю над полным интерпретатором G-Code для моего любимого проекта - настольной мельницей с ЧПУ.В настоящее время команды отправляются через последовательный интерфейс на микроконтроллер AVR.Затем эти команды анализируются и выполняются для перемещения фрезерной головки.типичный пример строки может выглядеть следующим образом:

N01 F5.0 G90 M48 G1 X1 Y2 Z3

, где G90, M48 и G1 являются кодами «действия», а F5.0, X1, Y2, Z3 являются параметрами (N01 является необязательным номером строки иигнорируется).В настоящее время синтаксический анализ проходит плавно, но теперь пришло время заставить машину фактически двигаться.

Для каждого из кодов G и M необходимо выполнить определенное действие.Это варьируется от контролируемого движения до активации / деактивации охлаждающей жидкости до выполнения постоянных циклов.С этой целью в моем текущем проекте есть функция, которая использует переключатель, чтобы выбрать правильную функцию и вернуть указатель на эту функцию, который затем можно использовать для вызова функции отдельного кода в нужное время.

Вопросы:
1) Есть ли лучший способ разрешить произвольный код для соответствующей функции, чем оператор switch?Обратите внимание, что это реализуется на микроконтроллере, и память очень ограничена (всего 2K).Я рассмотрел таблицу поиска, но, к сожалению, распределение кода невелико, что приводит к значительному расходу пространства.Существует ~ 100 различных кодов и подкодов.

2) Как можно обращаться к указателям на функции в C, когда имена (и, возможно, сигнатуры) могут измениться?Если сигнатуры функций различны, возможно ли это вообще?

3) Предполагая, что функции имеют одинаковую сигнатуру (на которую я опираюсь), есть ли способ типизировать общий тип этой сигнатуры, чтобыобошли и позвонили с?

Мои извинения за разрозненные вопросы.Заранее благодарю за помощь.

Ответы [ 3 ]

1 голос
/ 08 сентября 2011

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

2) Вам не нужны перегруженные / разнородные функции.Возможны дополнительные аргументы.

3) ваш единственный выбор - использовать varargs, IMHO

1 голос
/ 29 сентября 2013

Есть ли лучший способ ... чем оператор switch?

Составьте список всех допустимых кодов действий (константа в памяти программы, чтобы она не использовала мало вашей оперативной памяти) и последовательно сравнивайте каждый из них с полученным кодом. Возможно, зарезервировать индекс «0» для обозначения «неизвестный код действия».

Например:

// Warning: untested code.
typedef int (*ActionFunctionPointer)( int, int, char * );
struct parse_item{
    const char action_letter;
    const int action_number; // you might be able to get away with a single byte here, if none of your actions are above 255.
    // alas, http://reprap.org/wiki/G-code mentions a "M501" code.
    const ActionFunctionPointer action_function_pointer;
};
int m0_handler( int speed, int extrude_rate, char * message ){ // M0: Stop
    speed_x = 0; speed_y = 0; speed_z = 0; speed_e = 0;
}
int g4_handler ( int dwell_time, int extrude_rate, char * message ){ // G4: Dwell
    delay(dwell_time);
}
const struct parse_item parse_table[] = {
    { '\0', 0, unrecognized_action }  // special error-handler 
    { 'M', 0, m0_handler }, // M0: Stop
    // ...
    { 'G', 4, g4_handler }, // G4: Dwell
    { '\0', 0, unrecognized_action }  // special error-handler 
}
ActionFunctionPointer get_action_function_pointer( char * buffer ){
    char letter = get_letter( buffer );
    int action_number = get_number( buffer );
    int index = 0;
    ActionFunctionPointer f = 0;
    do{
        index++;
        if( (letter == parse_table[index].action_letter ) and
            (action_number == parse_table[index].action_number) ){
            f = parse_table[index].action_function_pointer;
        };
        if('\0' == parse_table[index].action_letter ){
            index = 0;
            f = unrecognized_action;
        };
    }while(0 == f);
    return f;
}

Как можно поступить с указателями на функции в C, когда имена (и возможно подписи) может поменяться? Если функции подписи иначе, это вообще возможно?

Можно создать указатель на функцию в C, которая (в разное время) указывает на функции с более или менее параметрами (разными сигнатурами), используя varargs.

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

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

Есть ли способ ввести определение общего типа этой подписи быть переданным и вызванным от?

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

См. Приведенный выше пример и Wikibooks: программирование на C: подробные указатели на функции .

p.s .: Есть ли какая-то причина, по которой вы заново изобретаете колесо? Может быть, может быть, один из следующих уже существующих интерпретаторов G-кода для AVR подойдет вам, возможно, с небольшими изменениями? проигрывал , Sprinter , Marlin , Прошивка Teacup , sjfw , Makerbot , или же Grbl ? (См. http://reprap.org/wiki/Comparison_of_RepRap_Firmwares).

1 голос
/ 08 сентября 2011

Я не специалист по встраиваемым системам, но у меня есть опыт работы с VLSI. Извините, если я констатирую очевидное.

Подход с использованием указателя на функцию, вероятно, является наилучшим. Но вам нужно либо:

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

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

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

EDIT: В любом случае, я думаю, что две таблицы поиска (1 для указателей на функции и одна для декодера) все еще будут намного меньше, чем большой оператор switch. Для переменных параметров используйте «фиктивные» параметры, чтобы все они были согласованными. Я не уверен, какие последствия принудительного приведения всех к указателям void к структурам будут на встроенном процессоре.

РЕДАКТИРОВАТЬ 2: На самом деле, декодер не может быть реализован только с помощью таблицы поиска, если пространство кода операции слишком велико. Моя ошибка там. Так что 1 действительно единственный жизнеспособный вариант.

...