illumium.org

Главная › Блоги › Блог kayo

Простой декларативный DSL средствами GNU Make

kayo — Сб, 11/06/2016 - 09:15

Мы, программисты, всегда стремимся к простоте, краткости и лаконичности нашего кода. Это связано с тем, что чаще приходится его именно читать, а не писать. Именно по этой причине мы предпочитаем декларативные языки императивным, если постановка задачи даёт нам такую возможность. А если же нет? И всё непременно должно быть написано на языке C? Тогда мы изобретаем различные прикладные языки, так называемые DSL. В этой статье мы реализуем один из таких прикладных декларативных языков для генерации низкоуровневого кода на языке C.

Введение

Для начала краткое введение. Есть у нас в uprojects такая библиотека libparam. Она позволяет управлять устройствами на микроконтроллерах унифицированным способом через различные интерфейсы путём считывания и установки значений параметров. Каждый параметр имеет дескриптор, который представляет собой экземпляр структуры языка C, объявленный как статическая константа. Описывать эти дескрипторы скудными средствами языка C то ещё удовольствие: очень просто что-то забыть или перепутать, тогда как на деле в них представлено не так уж много информации и это должно быть простым механическим процессом без всяких премудростей.

Проблема описаний встала ещё более остро при разработке libparam2, усовершенствованной версии данной библиотеки, которая пишется с нуля. Здесь дескрипторы ещё более хитрые, что связано с потребностью экономии ПЗУ микроконтроллеров и расширением возможностей новой системы типов. Теперь то уж точно было бы куда более приятно описывать параметры на неком DSL, который бы всегда корректно компилировался в код на C.

Решение

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

Конструируем DSL

Способы объявления переменных в Makefile помогут нам в разработке удачного DSL. Их существует как минимум два, и рассмотрим мы их на примере:

# Объявление в одну строку
# краткий способ, более
# подходит для атомарных
# однострочных значений
flat_var=flat value

# Объявление в виде блока
# расширенный способ, более
# подходит для сложных
# многострочных значений
define space_var
  space
  value
endef

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

Итак, набросаем структуру нашего DSL:

define параметры
  define имя_параметра_1
    type=тип_параметра_1
    info=Полное описание параметра 1
    unit=Единицы значений параметра 1
    def=Значение по умолчанию
    field1=значение дополнительного поля 1
    ...
    fieldN=значение дополнительного поля N
  endef
  ...
  define имя_параметра_N
    ...
  endef
endef

Как видим, всё довольно таки кратко и понятно, ничего лишнего. Вот как может выглядеть типичное описание параметров на таком языке:

define params
  define u8_num
    type=uint
    size=8
    cell=num_u8
    flag=getable
    info[en]=The readonly 8-bit unsigned integer with unit
    info[ru]=8-битовое беззнаковое целое только для чтения с единицами
    unit[en]=pcs
    unit[ru]=шт.
    def=11
  endef

  define some_str
    type=cstr
    cell=str
    size=32
    flag=getable setable
    info[en]=The readable/writable null-terminated C-String
    info[ru]=Строка в Си стиле для чтения и записи
    def[en]=Default value
    def[ru]=Значение по-умолчанию
  endef

  define real_num
    type=real
    size=32
    get=real_get
    set=real_set
    flag=getable setable persist
    info=The positive floating point number with getter and setter
    def=0.0
    min=-1.0
    max=1.0
    step=0.1
  endef

  define ctl_mode
    type=enum
    info=Control mode
    info[ru]=Режим управления
    opts=off auto man
    off.info=Disabled
    off.info[ru]=Выключено
    auto.info=Automatic
    auto.info[ru]=Автоматически
    man.info=Manual
  endef

  define aes_key
    type=hbin
    size=16
    def=E3 E5 3E C1 96 E0 3F 28 E3 22 17 30 DD E2 B4 3C
    flag=getable setable persist
  endef

  define ip_addr
    type=ipv4
    def=192.168.1.1
    flag=getable setable persist
  endef

  define mac_addr
    type=mac
    def=12:34:56:AB:CD:EF
    info=ARP mac address of interface.
  endef
endef

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

Реализуем компилятор

Компилятор, а точнее будет сказать препроцессор, получится достаточно прост. Нет необходимости в разборе языка, поскольку он реализован исключительно конструкциями GNU Make. Потребуется разве что некоторая обработка кода перед вычислением. Итак, перед нами стоит задача трансформации кода на нашем импровизированном DSL в код на C, содержащий дескрипторы параметров в порядке их объявления.

Получаем список параметров

В GNU Make как во всяком порядочном функциональном интерпретируемом языке имеется злая функция eval, вычисляющая код на самом себе. Если мы скормим значение переменной params из примера описания выше, то в среде выполнения появятся новые переменные с именами параметров. Существует специальная переменная .VARIABLES, в которой содержится список всех переменных в текущем состоянии среды выполнения. Существует простой способ получить список наших параметров:

# подключаем исходник
include params.lp2

# получаем текущий набор переменных
# и добавляем к нему нашу новую переменную
param.list:=$(.VARIABLES) param.list
# вычисляем параметры
$(eval $(params))
# берём новый набор переменных и убираем те,
# что были известны до вычисления
param.list:=$(filter-out $(param.list),$(.VARIABLES))

# выводим список параметров
$(info $(param.list))

Запускаем и смотрим, что получилось:

$ make -f param_pp.mk
str aes_key u8_num real

Список переменных то мы получили, однако порядок объявлений не сохранился. Такое поведение обусловлено тем, что переменные в GNU Make хранятся в хэш-таблице, и это именно набор, а не список. Так как же быть в таком случае? А у нас всегда есть коварный запасной план:

# подключаем исходник
include params.mk

# заменяем все "define имя_параметра"
# на "define param/имя_параметра"
# затем фильтруем по шаблону "param/%"
# и убираем префикс param/
param.list:=$(patsubst param/%,%,$(strip $(filter param/%,$(subst define ,define param/,$(params)))))

# выводим список параметров
$(info $(param.list))

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

$ make -f test.mk 
u8_num str real aes_key

Вычисление описания параметров

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

Решить проблему можно двумя способами:

  1. Как-то сохранить текущие значения совпадающих переменных и восстановить после обработки
  2. Обеспечить уникальность имён переменных

Мы пойдём вторым путём, просто добавим префиксы/суффиксы к именам в исходнике с помощью функции subst:

# Добавляем к именам параметров префикс param/
# а к именам полей в описаниях суффикс .param
param.text:=$$(subst =,.param=,$$(subst define ,define param/,$$($(1))))

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

После вычисления и обработки каждый раз у нас остаётся переменные, которые нам больше не требуются и могут даже мешать. Поэтому требуется удалить их из контекста. Сделать это можно както так:

# Разобъявление переменной
define undef-var
undefine $(1)
endef

# Разобъявление всех переменных
undef-vars=$(foreach v,$(1),$(eval $(call undef-var,$(v))))

# Удаляем из контекста все поля параметра
$(call undef-vars,$(filter %.param,$(.VARIABLES)))

# Удаляем из контекста все параметры
$(call undef-vars,$(patsubst %,param/%,$(param.list)))

Локализация

Достаточно просто организовать локализацию. В примере выше к каждому полю, которое мы хотели бы перевести, мы добавили суффикс [langcode], с буквенным кодом языка. Теперь, чтобы скомпилировать описания под конкретный язык, достаточно просто убрать этот суффикс для нужного языка, и соответствующая переменная будет использована:

# локализация
ifneq (,$$(param.proc.lang))
param.text := $$(subst [$$(param.proc.lang)]=,=,$$(param.text))
endif

Собираем всё вместе

Осталось собрать все описанные выше идеи воедино. Посему далее пойдёт исходник с комментариями:

# разобъявление переменной
define undef-var
undefine $(1)
endef

# разобъявление переменных
undef-vars = $(foreach v,$(1),$(eval $(call undef-var,$(v))))

# символ запятая
comma:=,

# вывод отладочных сообщений
# по умолчанию включен
param.proc.debug ?= on

# язык для локализации
# по умолчанию en
param.proc.lang ?= en

# поля параметров
param.base.fields := type name flag # основные
param.bind.fields := cell get set # привязка
param.cons.fields := def # ограничения
param.text.fields := info unit item hint # текстовые
param.all.fields := $(foreach g,base bind cons text,$(param.$(g).fields)) # все вместе

# поддерживаемые типы параметров
param.types := uint sint real enum cstr hbin mac ipv4
# поддерживаемые флаги
param.flags := getable setable persist

# переопределения привязок
param.bind.ext = extern param_$(1)_f $($(1).param)
param.cell.ext = extern $(param.$(type.param).ctype) $($(1).param)$(if $(param.$(type.param).ptype),[$(param-csize)])

param.bind.def = param_$(1)_f *$(1)
param.cell.def = $(param.$(type.param).ctype) *$(1)

param.bind.val = $($(1).param)
param.cell.val = &$($(1).param)

# комментарии для генерируемого исходника
param.def.cmt:=parameter definition
param.dat.cmt:=parameter data

param.base.cmt:=basic fields
param.bind.cmt:=binding fields
param.cons.cmt:=constraint fields
param.text.cmt:=textual fields

param.type.cmt:=type
param.name.cmt:=name
param.flag.cmt:=flags
param.size.cmt:=size of value

param.cell.cmt:=pointer to data cell
param.get.cmt:=getter function
param.set.cmt:=setter function

param.def.cmt:=default value
param.min.cmt:=minimum
param.max.cmt:=maximum
param.step.cmt:=step

param.info.cmt:=short description
param.unit.cmt:=measurement units
param.item.cmt:=item for menu
param.hint.cmt:=hint for menu

# типо-специфичные переопределения
param.atom.size = 32$(warning Because no size field is provided for "$(name.param)" using 32 bits by default.)
param.atom.csize = sizeof($(param.$(type.param).ctype))
param.atom.ctype = $(if $(filter $(1),$(size.param)),$(2),$(error Invalid size of param $(name.param)! You can use $(subst $(eval) ,$(comma) , $(1)) bits.))
param.num.cons.fields = $(param.cons.fields) min max step

param.uint.size = $(param.atom.size)
param.uint.csize = $(param.atom.csize)
param.uint.ctype = $(call param.atom.ctype,8 16 32 64,uint$(size.param)_t)
param.uint.cons.fields = $(param.num.cons.fields)

param.sint.size = $(param.atom.size)
param.sint.csize = $(param.atom.csize)
param.sint.ctype = $(call param.atom.ctype,8 16 32 64,int$(size.param)_t)
param.sint.cons.fields = $(param.num.cons.fields)

param.real.size = $(param.atom.size)
param.real.csize = $(param.atom.csize)
param.real.ctype = $(call param.atom.ctype,32 64,$(if $(filter 32,$(size.param)),float,double))
param.real.cons.fields = $(param.num.cons.fields)

param.enum.size = $(param.uint.size)
param.enum.csize = $(param.uint.csize)
param.enum.ctype = $(param.uint.ctype)
param.enum.cons.fields = $(param.cons.fields) max

#param.enum.cons.extra.def = $(opts.param)
param.enum.cons.extra = $(foreach o,$(opts.param),$(o).val)
#param.enum.text.extra.def = $(foreach t,name $(call param-fields,text),$(opts.param))
param.enum.text.extra = $(foreach o,$(opts.param),$(foreach t,name $(call param-fields,text),$(o).$(t)))

# препроцессинг опции перечисления
define param.enum.pre-proc.opt

$(1).name.param=$(1)#
ifeq (,$($(1).val.param))
$(1).val.param=$(1)#
endif
$(foreach t,$(call param-fields,text),\
$(if $($(1).$(t).param),,\
$$(error No text field "$(t)" for option "$(1)" of enum "$(name.param)")))
endef

# препроцессинг перечисления
define param.enum.pre-proc
ifneq (,$(opts.param))
ifeq (1,$(words $(opts.param)))
$$(warning Enumeration "$(name.param)" has only one option!)
endif
max.param=$(words $(opts.param))
$(foreach o,$(opts.param),$(call param.enum.pre-proc.opt,$(o)))
else
$$(error Enumeration "$(name.param)" must have the "opts" field with available values!)
endif
endef

param.comp.size = $(if $(def.param),$(1),$(error Because no default value is provided for "$(name.param)" the size must be set explicitly.))
param.comp.csize = $(if $(param.$(type.param).size),$(param.$(type.param).size),$(error No size is provided for param "$(name.param)"!))
param.comp.ptype = $(param.$(type.param).ctype) *const

param.cstr.size = $(call param.comp.size,(sizeof($(call param-val,def))-1))
param.cstr.csize = ($(param.comp.csize)+1)
param.cstr.ctype = char
param.cstr.ptype = $(param.comp.ptype)
param.cstr.val = "$($(1).param)"

param.hbin.size = $(call param.comp.size,sizeof($(call param.hbin.val,def)))
param.hbin.csize = $(param.comp.csize)
param.hbin.ctype = uint8_t
param.hbin.ptype = $(param.comp.ptype)
param.hbin.def = static const uint8_t param_$(1)_val[] = {$(subst $(eval) $(eval),$(comma),$(patsubst %,0x%,$(strip $(subst $(comma), ,$(subst :, ,$($(1).param))))))};
param.hbin.val = param_$(1)_val

param.mac.csize = sizeof($(param.mac.ctype))
param.mac.ctype = param_mac_t
param.mac.val = {$(subst $(eval) ,$(comma),$(patsubst %,0x%,$(subst :, ,$($(1).param))))}

param.ipv4.csize = sizeof($(param.ipv4.ctype))
param.ipv4.ctype = param_ipv4_t
param.ipv4.val = {$(subst .,$(comma),$($(1).param))}

Q?=@

# Форматирование строки исходника
define param-out

	$(Q)echo$(if $(1), '$(1)',) $(if $(2),>,>>)-@-
endef

# форматирования комментария
param-cmt=$(if $(param.$(1).cmt), /* $(param.$(1).cmt) */,)

# форматирование значения
param-val=$(if $(param.$(type.param).val),\
$(call param.$(type.param).val,$(1)),\
$($(1).param))

# размер типа в байтах
param-csize=$(param.$(type.param).csize)

# форматирование определения и значения привязки
param-bind-def=$(call $(if $(param.$(1).def),param.$(1).def,param.bind.def),$(1))
param-bind-val=$(call $(if $(param.$(1).val),param.$(1).val,param.bind.val),$(1))

# получение списка всех полей описания параметра
param-field-list=$(patsubst %.param,%,$(filter-out $(param.pre.vars) param.pre.vars,$(.VARIABLES)))

# получение полей описания параметра по классу
# например bind, cons и text
param-fields=$(filter $(or $(param.$(type.param).$(1).fields),\
$(param.$(1).fields)),$(param-field-list))

# получение имён элементов полей по классу
param-entries=$(strip $(call param-fields,$(1)) $(param.$(type.param).$(1).extra))

# форматирование исходника на C с описанием параметра
define param-def
define param/$(name.param)
# определения привязок
$(if $(call param-fields,bind),\
$(foreach f,$(call param-fields,bind),\
$(call param-out,$(call param.$(if $(param.$(f).def),$(f),bind).ext,$(f),$(type.param),$(name.param),);))\
$(call param-out))

# определения ограничений
$(if $(and $(param.$(type.param).def),$(call param-fields,cons)),\
$(foreach f,$(call param-fields,cons),\
$(call param-out,$(call param.$(type.param).def,$(f))))\
$(call param-out))

# определение структуры с описанием
$(call param-out,struct {$(call param-cmt,def))

# базовые поля
$(call param-out,  param_def_t base;$(call param-cmt,base))

# поля привязок
$(if $(call param-fields,bind),\
$(call param-out,  struct {$(call param-cmt,bind))\
$(foreach f,$(call param-fields,bind),\
$(call param-out,    $(call param-bind-def,$(f));$(call param-cmt,$(f))))\
$(call param-out,  } bind;))

# поля ограничений
$(if $(call param-entries,cons),\
$(call param-out,  const $(or $(param.$(type.param).ptype),$(param.$(type.param).ctype)) \
cons[$(words $(call param-entries,cons))];$(call param-cmt,cons)))

# текстовые поля
$(if $(call param-entries,text),\
$(call param-out,  const char *const \
text[$(words $(call param-entries,text))];$(call param-cmt,text)))

# наполнение структуры описания
$(call param-out,} param_$(name.param)_def = {$(call param-cmt,dat))

# начало базовых полей
$(call param-out,  {$(call param-cmt,base))

# поле флагов
$(call param-out,    $(subst $(eval) $(eval),|,$(strip \
$(patsubst %,param_bind_%,$(call param-fields,bind)) \
$(patsubst %,param_cons_%,$(call param-fields,cons)) \
$(patsubst %,param_text_%,$(call param-fields,text)) \
$(patsubst %,param_%,$(filter $(param.flags),$(flag.param)))\
))$(comma)$(call param-cmt,flag))

# поле размера
$(call param-out,    $(call param-csize)$(comma)$(call param-cmt,size))

# поле типа
$(call param-out,    &param_$(type.param)$(comma)$(call param-cmt,type))

# поле имени
$(call param-out,    "$(name.param)"$(call param-cmt,name))

# конец базовых полей
$(call param-out,  }$(comma))

# поля привязок (если есть)
$(if $(call param-fields,bind),\
$(call param-out,  {$(call param-cmt,bind))\
$(foreach f,$(call param-fields,bind),\
$(call param-out,    $(call param-bind-val,$(f))$(comma)$(call param-cmt,$(f))))\
$(call param-out,  }$(comma)))

# поля ограничений (если есть)
$(if $(call param-entries,cons),\
$(call param-out,  {$(call param-cmt,cons))\
$(foreach f,$(call param-entries,cons),\
$(call param-out,    $(if $(param.$(type.param).val),\
$(call param.$(type.param).val,$(f)),$($(f).param))$(comma)$(call param-cmt,$(f))))\
$(call param-out,  }$(comma)))

# текстовые поля (если есть)
$(if $(call param-entries,text),\
$(call param-out,  {$(call param-cmt,text))\
$(foreach f,$(call param-entries,text),\
$(call param-out,    "$($(f).param)"$(comma)$(call param-cmt,$(f))))\
$(call param-out,  }))

# конец описания параметра
$(call param-out,};)
$(call param-out)
endef
endef

define param-proc
# подготовка списка полей параметра
param.pre.vars := $$(.VARIABLES)

# добавим поле имени параметра
name.param=$(2)

# вычислим объявление параметра
$$(eval $$(param/$(2)))

# проверим поле типа
ifndef type.param
$$(error No type is provided for "$$(name.param)"!)
endif
$$(if $$(filter $$(type.param),$$(param.types)),,\
$$(error Unsupported type "$$(type.param)" of "$$(name.param)"!))

# проверим поля привязок
$$(if $$(filter getable,$$(flag.param)),\
$$(if $$(or $$(cell.param),$$(get.param)),,\
$$(error Parameter "$$(name.param)" getable but has no cell or getter provided!)))

$$(if $$(filter setable,$$(flag.param)),\
$$(if $$(or $$(cell.param),$$(set.param)),,\
$$(error Parameter "$$(name.param)" setable but has no cell or setter provided!)))

# проверим поле размера
ifndef size.param
ifdef param.$$(type.param).size
size.param=$$(param.$$(type.param).size)
endif
endif

# вызываем крюк препроцессинга
$$(if $$(param.$$(type.param).pre-proc),\
$$(eval $$(param.$$(type.param).pre-proc)))

# выводим список полей для отладки
$$(if $$(param.proc.debug),$$(info Param "$(2)" fields: $$(param-field-list)))

# создаём правила определения параметра на С
$$(eval $$(call param-def,$(1)))

# очищаем переменные полей параметра
$$(call undef-vars,$$(filter-out $$(param.pre.vars),$$(.VARIABLES)))
endef

# правила генерирования заголовочного файла
define param-h
-@-: param-dsl.mk
$(call param-out,#include "param.h",t)
$(call param-out)
$(foreach p,$(param.list),$(call param-out,extern const param_def_t param_$(p)_def;))
#$(call param-out)
endef

# правила генерирования файла исходника
define param-c
-@-: $(param.h)
$(call param-out,#include "param.h",t)
$(foreach h,$(header.param),\
$(call param-out,#include "$(h)"))
$(call param-out)
$(foreach p,$(param.list),$(param/$(p)))
endef

# обработка определений параметров в переменной $(1)
define param-proc-all
param.h := include/$(1).h # имя заголовочного файла
param.c := src/$(1).c # имя файла исходника
param.text := $$($(1)) # текстовое описание параметров

# локализация
ifneq (,$$(param.proc.lang))
param.text := $$(subst [$$(param.proc.lang)]=,=,$$(param.text))
endif

# подготовка исходника
param.text := $$(subst =,.param=,$$(subst define ,define param/,$$(param.text)))

# получение списка параметров
param.list := $$(patsubst param/%,%,$$(strip $$(filter param/%,$$(param.text))))

# вывод списка параметров для отладки
$$(if $$(param.proc.debug),$$(info Found params: $$(param.list)))

# генерирование заголовка с определениями
$$(eval $$(subst -@-,$$(param.h),$$(param-h)))

# вычисление определений параметров
$$(eval $$(param.text))

# обработка определений параметров
$$(foreach p,$$(param.list),$$(eval $$(call param-proc,$$(param.c),$$(p))))

# генерирование исходника с определениями параметров
$$(eval $$(subst -@-,$$(param.c),$$(param-c)))

# очистка временных переменных
$$(call undef-vars,$$(patsubst %,param/%,$$(param.list)) param.h param.c param.text param.list)
endef

# упрощённый возов компилятора
param-rules=$(eval $(call param-proc-all,$(1)))

# правила для исходного файла на DSL
define param-dsl-rules
param.dsl.vars:=$$(.VARIABLES) param.dsl.vars

# подключаем исходник
include $(1)

# получаем список переменных
param.dsl.vars:=$$(filter-out $$(param.dsl.vars),$$(.VARIABLES))

# применяем правила для всех переменных
$$(foreach g,$$(param.dsl.vars),$$(call param-rules,$$(g)))

# очистка переменных
$$(call undef-vars,$$(param.dsl.vars) param.dsl.vars)
endef

# упрощённый вызов компилятора для исходников
param-dsl=$(foreach f,$(1),$(eval $(call param-dsl-rules,$(f))))

Теперь мы можем загрузить наш компилятор и сгенерировать исходник на C из DSL:

# подключаем модуль DSL
include param-dsl.mk

# вызываем компилятор для исходника params.lp2
$(call param-dsl,params.lp2)

# добавим правило по умолчанию для проверки
all: src/params.c

Вот так будут выглядеть сгенерированные нашим компилятором исходники (params.h):

uint8_t num_u8;

char str[32];

param_set_f real_set;
param_get_f real_get;

enum {
  ctl_off,
  ctl_auto,
  ctl_man,
};

param_ipv4_t ip_addr;
param_mac_t mac_addr;

(params.c):

#include "param.h"
#include "params.h"

extern uint8_t num_u8;
struct { /* default value */
  param_def_t base; /* basic fields */
  struct { /* binding fields */
    uint8_t *cell; /* pointer to data cell */
  } bind;
  const uint8_t cons[1]; /* constraint fields */
  const char *const text[2]; /* textual fields */
} param_u8_num_def = { /* parameter data */
  { /* basic fields */
    param_bind_cell|param_cons_def|param_text_unit|param_text_info|param_getable, /* flags */
    sizeof(uint8_t), /* size of value */
    &param_uint, /* type */
    "u8_num" /* name */
  },
  { /* binding fields */
    &num_u8, /* pointer to data cell */
  },
  { /* constraint fields */
    11, /* default value */
  },
  { /* textual fields */
    "шт.", /* measurement units */
    "8-битовое беззнаковое целое только для чтения с единицами", /* short description */
  }
};
extern char str[((sizeof( "Значение по-умолчанию")-1)+1)];
struct { /* default value */
  param_def_t base; /* basic fields */
  struct { /* binding fields */
    char *cell; /* pointer to data cell */
  } bind;
  const char *const cons[1]; /* constraint fields */
  const char *const text[1]; /* textual fields */
} param_some_str_def = { /* parameter data */
  { /* basic fields */
    param_bind_cell|param_cons_def|param_text_info|param_getable|param_setable, /* flags */
    ((sizeof( "Значение по-умолчанию")-1)+1), /* size of value */
    &param_cstr, /* type */
    "some_str" /* name */
  },
  { /* binding fields */
    &str, /* pointer to data cell */
  },
  { /* constraint fields */
     "Значение по-умолчанию", /* default value */
  },
  { /* textual fields */
    "Строка в Си стиле для чтения и записи", /* short description */
  }
};
extern param_set_f real_set;
extern param_get_f real_get;
struct { /* default value */
  param_def_t base; /* basic fields */
  struct { /* binding fields */
    param_set_f *set; /* setter function */
    param_get_f *get; /* getter function */
  } bind;
  const float cons[4]; /* constraint fields */
  const char *const text[1]; /* textual fields */
} param_real_num_def = { /* parameter data */
  { /* basic fields */
    param_bind_set|param_bind_get|param_cons_def|param_cons_max|param_cons_step|param_cons_min|param_text_info|param_getable|param_setable|param_persist, /* flags */
    sizeof(float), /* size of value */
    &param_real, /* type */
    "real_num" /* name */
  },
  { /* binding fields */
    real_set, /* setter function */
    real_get, /* getter function */
  },
  { /* constraint fields */
    0.0, /* default value */
    1.0, /* maximum */
    0.1, /* step */
    -1.0, /* minimum */
  },
  { /* textual fields */
    "The positive floating point number with getter and setter", /* short description */
  }
};
struct { /* default value */
  param_def_t base; /* basic fields */
  const uint8_t cons[4]; /* constraint fields */
  const char *const text[7]; /* textual fields */
} param_ctl_mode_def = { /* parameter data */
  { /* basic fields */
    param_cons_max|param_text_info, /* flags */
    sizeof(uint8_t), /* size of value */
    &param_enum, /* type */
    "ctl_mode" /* name */
  },
  { /* constraint fields */
    3, /* maximum */
    ctl_off,
    ctl_auto,
    ctl_man,
  },
  { /* textual fields */
    "Режим управления", /* short description */
    "off",
    "Выключено",
    "auto",
    "Автоматически",
    "man",
    "Manual",
  }
};
extern uint8_t aes_key[sizeof(param_def_val)];
static const uint8_t param_def_val[] = {0xE3,0xE5,0x3E,0xC1,0x96,0xE0,0x3F,0x28,0xE3,0x22,0x17,0x30,0xDD,0xE2,0xB4,0x3C};
struct { /* default value */
  param_def_t base; /* basic fields */
  struct { /* binding fields */
    uint8_t *cell; /* pointer to data cell */
  } bind;
  const uint8_t *const cons[1]; /* constraint fields */
} param_aes_key_def = { /* parameter data */
  { /* basic fields */
    param_bind_cell|param_cons_def|param_getable|param_setable|param_persist, /* flags */
    sizeof(param_def_val), /* size of value */
    &param_hbin, /* type */
    "aes_key" /* name */
  },
  { /* binding fields */
    &aes_key, /* pointer to data cell */
  },
  { /* constraint fields */
     param_def_val, /* default value */
  },
};
extern param_ipv4_t ip_addr;
struct { /* default value */
  param_def_t base; /* basic fields */
  struct { /* binding fields */
    param_ipv4_t *cell; /* pointer to data cell */
  } bind;
  const param_ipv4_t cons[1]; /* constraint fields */
} param_ip_addr_def = { /* parameter data */
  { /* basic fields */
    param_bind_cell|param_cons_def|param_getable|param_setable|param_persist, /* flags */
    sizeof(param_ipv4_t), /* size of value */
    &param_ipv4, /* type */
    "ip_addr" /* name */
  },
  { /* binding fields */
    &ip_addr, /* pointer to data cell */
  },
  { /* constraint fields */
     {192,168,1,1}, /* default value */
  },
};
extern param_mac_t mac_addr;
struct { /* default value */
  param_def_t base; /* basic fields */
  struct { /* binding fields */
    param_mac_t *cell; /* pointer to data cell */
  } bind;
  const param_mac_t cons[1]; /* constraint fields */
  const char *const text[1]; /* textual fields */
} param_mac_addr_def = { /* parameter data */
  { /* basic fields */
    param_bind_cell|param_cons_def|param_text_info, /* flags */
    sizeof(param_mac_t), /* size of value */
    &param_mac, /* type */
    "mac_addr" /* name */
  },
  { /* binding fields */
    &mac_addr, /* pointer to data cell */
  },
  { /* constraint fields */
     {0x12,0x34,0x56,0xAB,0xCD,0xEF}, /* default value */
  },
  { /* textual fields */
    "ARP mac address of interface.", /* short description */
  }
};

На этом всё. Счастливого make хакинга.

  • dsl
  • GNU Make
  • makefile
  • preprocessor
  • Бортовой журнал Иллюмиума

Отправить комментарий

Содержимое этого поля является приватным и не будет отображаться публично.
  • Доступные HTML теги: <a> <em> <strong> <cite> <code> <ul> <ol> <li> <dl> <dt> <dd>
  • Syntax highlight code surrounded by the {syntaxhighlighter SPEC}...{/syntaxhighlighter} tags, where SPEC is a Syntaxhighlighter options string or "class="OPTIONS" title="the title".

Подробнее о форматировании

CAPTCHA
Этот вопрос задается для того, чтобы выяснить, являетесь ли Вы человеком или представляете из себя автоматическую спам-рассылку.
  ____    _   _   _   ____     ___     ___  
| _ \ | | | \ | | | _ \ ( _ ) / _ \
| |_) | | | | \| | | | | | / _ \ | (_) |
| __/ | | | |\ | | |_| | | (_) | \__, |
|_| |_| |_| \_| |____/ \___/ /_/
Введите код, изображенный в стиле ASCII-арт.
RSS-материал

Навигация

  • Подшивки
  • Фотоальбомы

«Иллюмиум» на якоре.

Работает на Drupal, система с открытым исходным кодом.

(L) 2010, Illumium.Org. All rights reversed ^_~