Преобразование массива указателей C в массив структур Python

Я пишу приложение Python, которое использует API PulseAudio. Реализация активно использует обратные вызовы, написанные на Python и вызываемые C-кодом PulseAudio.

Большая часть информации передается в обратный вызов определенной структурой, например pa_sink_info, которая определена в C следующим образом:

typedef struct pa_sink_info {
  const char *name;                  
  uint32_t index;                    
  const char *description;           
  pa_sample_spec sample_spec;        
  pa_channel_map channel_map;        
  uint32_t owner_module;             
  pa_cvolume volume;                 
  int mute;                          
  uint32_t monitor_source;           
  const char *monitor_source_name;   
  pa_usec_t latency;                 
  const char *driver;                
  pa_sink_flags_t flags;             
  pa_proplist *proplist;             
  pa_usec_t configured_latency;      
  pa_volume_t base_volume;           
  pa_sink_state_t state;             
  uint32_t n_volume_steps;           
  uint32_t card;                     
  uint32_t n_ports;                  
  pa_sink_port_info** ports;         
  pa_sink_port_info* active_port;    
  uint8_t n_formats;                 
  pa_format_info **formats;          
} pa_sink_info;

Из этой структуры очень легко получить скалярные значения, например:

self.some_proc(
  struct.contents.index,
  struct.contents.name,
  struct.contents.description)

Но мне трудно иметь дело с ports и active_port, которые в Python описываются как:

('n_ports', uint32_t),
('ports', POINTER(POINTER(pa_sink_port_info))),
('active_port', POINTER(pa_sink_port_info)),

Здесь n_ports указывает количество элементов в ports, который является указателем на массив указателей на структуры типа pa_sink_port_info. На самом деле, я даже не знаю, как вообще преобразовать их в типы Python.

Каков наиболее эффективный способ преобразования ports в словарь Python, содержащий pa_sink_port_info?


person yktoo    schedule 28.09.2013    source источник


Ответы (1)


Для решения этой проблемы требовалось внимательно прочитать справочник по ctypes Python. После того, как механизм реализации преобразования типа ctypes стал ясен, добраться до нужных значений не так уж и сложно.

Основная идея указателей заключается в том, что вы используете их атрибут contents для доступа к данным, на которые указывает указатель. Еще одна полезная вещь, которую нужно знать, это то, что указатели могут быть проиндексированы как массивы (это не проверяется интерпретатором, поэтому вы сами несете ответственность за то, чтобы убедиться, что это действительно массив).

Для этого конкретного примера PulseAudio мы можем обработать член структуры ports (который является указателем на массив указателей) следующим образом:

port_list = []
if struct.contents.ports:
  i = 0
  while True:
    port_ptr = struct.contents.ports[i]
    # NULL pointer terminates the array
    if port_ptr:
      port_struct = port_ptr.contents
      port_list.append(port_struct.name)
      i += 1
    else:
      break
person yktoo    schedule 30.12.2013