CFFI - использовать структуру с #ifdef - PullRequest
0 голосов
/ 05 сентября 2018

В настоящее время я упаковываю некоторый существующий C-код в Python. Это работает довольно хорошо с CFFI, но есть одна последняя вещь, которую я не понимаю.

Как мне обработать операторы "#ifdef"?

Допустим, у меня есть эта структура в c:

typedef struct can_config_tag
{
    char_t type[4];
    uint16_t module_nr;
#ifdef CAN_MON
    uint16_t rx_main_mon;
#endif
    byte_t rx_obj;
}

Я не понимаю, как я справляюсь с переносом этих выражений "#ifdef". В документации я нашел что-то с "...;" но как я могу получить доступ к данным структуры тогда?

При попытке использовать ... и получить доступ к данным с помощью:

can_config= ffi.new("can_config_tag *")
can_config.rx_main_mon = 2

Я получаю сообщение об ошибке:

AttributeError: cdata 'struct can_config_tag*' has no field 'rx_main_mon'

Так есть ли обходной путь, как обрабатывать эти директивы препроцессора при использовании cffi? Заранее большое спасибо.

1 Ответ

0 голосов
/ 05 сентября 2018

Проблема в том, что ffi.cdef() не может дать результат, который зависит от директив предварительной обработки, потому что они известны только позже, когда вызывается компилятор Си. (Это может быть исправлено в случае сборок вне линии, но это не сразу.)

Существует два возможных решения:

  1. Выполнить несколько шагов компиляции.

  2. Не раскрывайте необязательное поле, но добавляйте пользовательские функции доступа.

Для 1. напишите что-то вроде этого:

ffi_pre = FFI()
ffi_pre.cdef("#define CAN_MON_IS_DEFINED ...")
ffi_pre.set_source("_tmp_cffi", """
    #include <foo_mon.h>
    #ifdef CAN_MON
    #  define CAN_MON_IS_DEFINED 1
    #else
    #  define CAN_MON_IS_DEFINED 0
    #endif
""")
ffi_pre.compile()
from _tmp_cffi import ffi as ffi_tmp
can_mon = ffi_tmp.CAN_MON_IS_DEFINED

... затем напишите остальную часть кода, как у вас уже есть, но используйте if can_mon: где-нибудь в качестве кода Python, например. построить строку в cdef так:

"""
typedef struct can_config_tag
{
    char_t type[4];
    uint16_t module_nr;
""" + ("uint16_t rx_main_mon;" if can_mon else "") + """
    byte_t rx_obj;
};
"""

Решение № 2 состоит в том, чтобы никогда не определять поле rx_main_mon, а вместо этого добавить функции C, которые читают и пишут его, если поле определено:

ffi.cdef("""
typedef struct can_config_tag
{
    char_t type[4];
    uint16_t module_nr;
    byte_t rx_obj;
};
int my_read_rx_main_mon(struct can_config_tag *);
void my_write_rx_main_mon(struct can_config_tag *, int value);
""")
ffi.set_source("somename", """
#include <foomon.h>
static int my_read_rx_main_mon(struct can_config_tag *p)
{
#ifdef CAN_MON
    return p->rx_main_mon;
#else
    return -1;
#endif
}
static void my_write_rx_main_mon(struct can_config_tag *p, int value)
{
#ifdef CAN_MON
    p->rx_main_mon = value;
#endif
}
""")
...