LINUX.ORG.RU

Байты они есть байты. В с типа это они. Если память мне не врет то CFFI числа и строки так самостоятельно из битового представления и выковыривает.

antares0 ★★★ ()
Последнее исправление: antares0 (всего исправлений: 1)

Как вообще работать с данными типа complex из стандарта C99?

функциями, описанными в стандарте С99

Harald ★★★★★ ()
Ответ на: комментарий от Harald

функциями, описанными в стандарте С99

А какими функциями можно, например, построить комплексное число?

ados ★★★★★ ()
Ответ на: комментарий от anonymous

Лучше я как-нибудь потом рассмотрю мнение человека, у которого проблемы с логопедом.

ados ★★★★★ ()
Ответ на: комментарий от ados

Т.е. это тупо обыкновенный C-массив из двух чисел?

CL-FFTW-BINDINGS> (let ((re 1)
                        (im -3))
                    (with-foreign-object (x :double 2)
                      (setf (mem-aref x :double 0)
                            (coerce re 'double-float)
                            (mem-aref x :double 1)
                            (coerce im 'double-float))
                      (complex (foreign-funcall "creal"
                                                :pointer x
                                                :double)
                               (foreign-funcall "cimag"
                                                :pointer x
                                                :double))))
#C(1.8719675605677398d-311 1.4582406633972006d-303)
CL-FFTW-BINDINGS> 

Походу что нет

ados ★★★★★ ()
Ответ на: комментарий от ados

Походу что нет

Походу лишперы таки не умеют элементарно читать доки.

6.2.5 Types
13 Each complex type has the same representation and alignment requirements as an array type containing exactly two elements of the corresponding real type; the first element is equal to the real part, and the second element to the imaginary part, of the complex number.

anonymous ()
Ответ на: комментарий от ados

Лишпер, с чего ты решил, что если что-то имеет то же представление, что и массив, то это что-то subject to array-to-pointer decay?

anonymous ()
Ответ на: комментарий от ados

Ладно, братан. Не обижайся на «лишпера». Я понимаю, что ты привык к тепличным условиям виртуальной машины лиспа где тебе не нужно прибирать за собой и отличать тип от представления типа в памяти. И что не привык читать документацию, потому что для лиспа её либо вообще не было, либо ссылки давно сдохли.

Так что я помогу тебе. SysV ABI для x86_64 говорит нам

Arguments of complex T where T is one of the types float or double are treated as if they are implemented as:

struct complexT {
T real;
T imag;
};

Для i386 я подобного явного не нашёл, но, походу, там так же.

anonymous ()

Для гарантированной работы можешь сделать сишную функцию make_double_complex(double re, double im) {return re+I*im;} и дёргать её через CFFI.

monk ★★★★★ ()
Ответ на: комментарий от monk

Ты предлогаешь динамически выделять?

anonymous ()
Ответ на: комментарий от monk

Допустим, получил я :pointer на что-то. Хочу в creal это передать. Что мне делать? Оно принимает отнюдь не :pointer, как уже мог убедиться наш незадачливый лиспер.

anonymous ()
Ответ на: комментарий от anonymous

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

CL-USER> (cffi:load-foreign-library "./libmake.so")
#<CFFI:FOREIGN-LIBRARY LIBMAKE.SO-837 "libmake.so">
CL-USER> (cffi:defcfun make-double-complex :pointer (a :double) (b :double))
MAKE-DOUBLE-COMPLEX
CL-USER> (cffi:defcfun creal :double (z :pointer))
CREAL
CL-USER> (creal (make-double-complex 1.0d0 2.0d0))
1.0d0
CL-USER> (cffi:defcfun cimag :double (z :pointer))
CIMAG
CL-USER> (cimag (make-double-complex 1.0d0 2.0d0))
2.0d0
monk ★★★★★ ()
Ответ на: комментарий от monk

6.2.5 Types

13 Each complex type has the same representation and alignment requirements as an array type containing exactly two elements of the corresponding real type; the first element is equal to the real part, and the second element to the imaginary part, of the complex number.

Это, как я понял, из текста стандарта C99. Т.е. если описать организацию данных в лиспе то вроде должно получиться надёжно и кроссплатформенно. Мне в целях изучения это более интересно. Но вот не могу понять cffi:define-foreign-type и как с ним всё это грамотно описать.

ados ★★★★★ ()
Последнее исправление: ados (всего исправлений: 1)
Ответ на: комментарий от ados

если описать организацию данных в лиспе то вроде должно получиться надёжно и кроссплатформенно

Да не получится кроссплатформенно, что ж до тебя всё не допрёт. representation никак не связано с тем, как оно передаётся в функцию. double[2] и struct { double; double; }; имеют на x86_64 одно и то же представление, только вот первое передаётся как указатель, а второе — в регистрах xmm0 и xmm1.

anonymous ()
Ответ на: комментарий от monk

Я так понял, у тебя просто регистры xmm0 и xmm1 не были перезаписаны между вызовами двух функций.

anonymous ()
Ответ на: комментарий от anonymous

А вот так работает:

(ql:quickload :cffi-libffi)
(cffi:defcstruct cdouble (re :double) (im :double))
(cffi:defcfun cimag :double (z (:struct cdouble)))
(cffi:defcfun creal :double (z (:struct cdouble)))

CL-USER> (cimag '(re 2.0d0 im -231.0d0))
-231.0d0

monk ★★★★★ ()

Ура! Заработало!

(defcstruct c-complex-double
  (re :double)
  (im :double))

(defctype c-complex-double (:struct c-complex-double))

(defcfun creal :double
  (z c-complex-double))

(defcfun cimag :double
  (z c-complex-double))

(defcfun conj c-complex-double
  (z c-complex-double))

(defmethod translate-from-foreign (z (type (eql 'c-complex-double)))
  (complex (creal z)
           (cimag z)))

(defmethod translate-into-foreign-memory (z
                                          (type (eql 'c-complex-double))
                                          ptr)
  (with-foreign-slots ((im re)
                       ptr c-complex-double)
    (setf im (coerce (imagpart z)
                     'double-float)
          re (coerce (realpart z)
                     'double-float))))
CL-FFTW-BINDINGS> (let ((lisp-z (complex 12 13)))
                    (with-foreign-object (x 'c-complex-double)
                      (translate-into-foreign-memory lisp-z
                                                     'c-complex-double
                                                     x) 
                      (let ((z (mem-ref x '(:struct c-complex-double))))
                        (list :my-complex-number
                              (translate-from-foreign z
                                                      'c-complex-double)
                              :conjugated
                              (translate-from-foreign (conj z)
                                                      'c-complex-double)))))
(:MY-COMPLEX-NUMBER #C(12.0d0 13.0d0) :CONJUGATED #C(12.0d0 -13.0d0))
$ uname -a
Linux localhost 4.7.4-1-ARCH #1 SMP PREEMPT Thu Sep 15 15:42:18 CEST 2016 i686 GNU/Linux

Всем спасибо

ados ★★★★★ ()
Ответ на: Ура! Заработало! от ados

Но почему так не работает?

CL-FFTW-BINDINGS> (let ((lisp-z (complex 12 13)))
                    (with-foreign-object (x 'c-complex-double)
                      (translate-into-foreign-memory lisp-z
                                                     'c-complex-double
                                                     x) 
                      (let ((z (mem-ref x 'c-complex-double)))
                        (list :my-complex-number
                              (translate-from-foreign z
                                                      'c-complex-double)
                              :conjugated
                              (translate-from-foreign (conj z)
                                                      'c-complex-double)))))
There is no applicable method for the generic function
  #<STANDARD-GENERIC-FUNCTION CFFI:TRANSLATE-INTO-FOREIGN-MEMORY (6)>
when called with arguments
  (#.(SB-SYS:INT-SAP #XB13B9FEC)
   #<C-COMPLEX-DOUBLE-TCLASS C-COMPLEX-DOUBLE>
   #.(SB-SYS:INT-SAP #XB13B9FD0)).
   [Condition of type SIMPLE-ERROR]
ados ★★★★★ ()
Ответ на: Ура! Заработало! от ados
(defmethod translate-into-foreign-memory (z
                                          (type (eql 'c-complex-double))
                                          ptr)

Это неверно. https://common-lisp.net/project/cffi/manual/html_node/Foreign-Structure-Types...

Надо

(defcstruct (c-complex-double :class t-complex-double)
  (re :double)
  (im :double))

(defmethod translate-into-foreign-memory (z
                                          (type t-complex-double)
                                          ptr)

monk ★★★★★ ()
Ответ на: И вот ещё вопрос от ados

Нужно ли в этом коде подчищать данные которые возвращает (conj z)?

Нет. В этом и смысл возврата по значению. libffi создаёт временную структуру, на которую применяется translate-from-foreign, а затем структура удаляется.

monk ★★★★★ ()
Ответ на: комментарий от monk

И всё же я не понимаю - а если я хочу сначала оттранслировать данные в родной вид и потом натравить на эти же данные cffi-функцию? Тогда после translate-from-foreign меня ждёт облом?

ados ★★★★★ ()
Ответ на: комментарий от ados

Т.е. всё же надо подчищать в смысле определив free-translated-object для таких структур?

Он есть по-умолчанию.

monk ★★★★★ ()
Ответ на: комментарий от ados

И всё же я не понимаю - а если я хочу сначала оттранслировать данные в родной вид и потом натравить на эти же данные cffi-функцию? Тогда после translate-from-foreign меня ждёт облом?

Что значит «на эти же данные»? translate-from-foreign тебе вернёт лисповый complex. При отправке его в cffi-функцию создастся новый временный struct, который будет заполнен при помощи translate-into-foreign-memory и будет существовать пока работает эта функция.

P.S. С учётом того, что все вызовы «по значению», временные объекты будут созданы через libffi на стеке, а не через malloc/free. То есть явного освобождения памяти нет.

monk ★★★★★ ()
Ответ на: комментарий от monk

Что-то я читаю доки и у меня не работает раскрытие макросов:

CL-FFTW-BINDINGS> (let ((lisp-z (complex 12 13)))
                    (with-foreign-object (x '(:struct c-complex-double))
                      (setf (mem-aref x '(:struct c-complex-double))
                            lisp-z)
                      (mem-ref x '(:struct c-complex-double))))
  0: (EXPAND-FROM-FOREIGN (INC-POINTER X 0)
                          #<T-COMPLEX-DOUBLE C-COMPLEX-DOUBLE>)
  0: EXPAND-FROM-FOREIGN returned
       (WITH-FOREIGN-SLOTS ((RE IM) (INC-POINTER X 0)
                            (:STRUCT C-COMPLEX-DOUBLE))
         (COMPLEX RE IM))
  0: (EXPAND-FROM-FOREIGN (CFFI-SYS:%MEM-REF #:PTR6 :DOUBLE 0)
                          #<CFFI::FOREIGN-BUILT-IN-TYPE :DOUBLE>)
  0: EXPAND-FROM-FOREIGN returned (CFFI-SYS:%MEM-REF #:PTR6 :DOUBLE 0)
  0: (EXPAND-FROM-FOREIGN (CFFI-SYS:%MEM-REF #:PTR6 :DOUBLE 8)
                          #<CFFI::FOREIGN-BUILT-IN-TYPE :DOUBLE>)
  0: EXPAND-FROM-FOREIGN returned (CFFI-SYS:%MEM-REF #:PTR6 :DOUBLE 8)
  0: (TRANSLATE-INTO-FOREIGN-MEMORY #C(12 13)
                                    #<T-COMPLEX-DOUBLE C-COMPLEX-DOUBLE>
                                    #.(SB-SYS:INT-SAP #XB13B9FEC))
  0: TRANSLATE-INTO-FOREIGN-MEMORY returned 12.0d0
#C(12.0d0 13.0d0)

Хотя методы определён:

(defmethod expand-into-foreign-memory (z
                                       (type t-complex-double)
                                       ptr)
  `(with-foreign-slots ((im re)
                        ,ptr (:struct c-complex-double))
     (setf im (coerce (imagpart ,z)
                      'double-float)
           re (coerce (realpart ,z)
                      'double-float))))

(defmethod expand-from-foreign (ptr (type t-complex-double))
  `(with-foreign-slots ((re im) ,ptr (:struct c-complex-double))
     (complex re im)))

translate-from-foreign тоже трассируется, но оптимизация макрораскрытием работает.

ados ★★★★★ ()
Ответ на: комментарий от ados

translate-from-foreign тоже трассируется, но оптимизация макрораскрытием работает

Судя по исходнику, этот кусок у них временно поломан:

(define-compiler-macro mem-set
    (&whole form value ptr type &optional (offset 0))
  "Compiler macro to open-code (SETF MEM-REF) when type is constant."
  (if (constantp type)
      (let* ((parsed-type (parse-type (eval type)))
             (ctype (canonicalize parsed-type)))
        ;; Bail out when using emulated long long types.
        #+cffi-sys::no-long-long
        (when (member ctype '(:long-long :unsigned-long-long))
          (return-from mem-set form))
        (if (aggregatep parsed-type)    ; XXX: skip for now.
            form      ; use expand-into-foreign-memory when available.
            `(%mem-set ,(expand-to-foreign value parsed-type)
                       ,ptr ,ctype ,offset)))
      form))

monk ★★★★★ ()
Ответ на: комментарий от monk

Со свежей версией из git-репозитория такой проблемы не наблюдаю.

ados ★★★★★ ()
Ответ на: комментарий от monk

А почему так не работает?

CL-FFTW-BINDINGS> (defctype c-complex-double-ptr :pointer)
C-COMPLEX-DOUBLE-PTR
CL-FFTW-BINDINGS> (defcfun cimag :double
                    (z c-complex-double-ptr))
WARNING: redefining CL-FFTW-BINDINGS::CIMAG in DEFUN
CIMAG
CL-FFTW-BINDINGS> (let ((lisp-z (complex 12 13)))
                    (with-foreign-object (x '(:struct c-complex-double))
                      (setf (mem-ref x '(:struct c-complex-double))
                            lisp-z)
                      (values
                       (cimag x)
                       (mem-ref x '(:struct c-complex-double)))))
1.4582406633972006d-303
#C(12.0d0 13.0d0)
CL-FFTW-BINDINGS> 
ados ★★★★★ ()
Последнее исправление: ados (всего исправлений: 1)
Ответ на: комментарий от ados

x — указатель. Но в функцию можно передать или указатель

(defcfun cimag :double
                    (z c-complex-double-ptr))
или значение по указателю
(defcfun cimag :double
  (z c-complex-double))

Во втором случае передаётся (в стек, регистры) сама структура (её поля), а не указатель на неё.

monk ★★★★★ ()
Ответ на: комментарий от monk

Ну вот CFFI и complex.h (комментарий)

Я второй командой определил функцию так чтобы она принимала указатель и следующей командой вызываю её с передачей указателя, но возвращается какая-то хрень. Может в git-версии опять что-то поломали?

ados ★★★★★ ()
Ответ на: комментарий от ados

Или твой код здесь на моей платформе работать не должен?

Не должен. Он вообще нигде работать не должен. Работает только потому, что я писал (cimag (make-double-complex 1.0d0 2.0d0)) и временные данные не успевали потеряться. Если писать (setf a (make-double-complex 1.0d0 2.0d0)) (cimag a), то уже не работает.

monk ★★★★★ ()
Ответ на: комментарий от monk

Слушай, я не понимаю документацию.

Please note that this interface is only for those that must know about the values contained in a relevant struct. If the library you are interfacing returns an opaque pointer that needs only be passed to other C library functions, by all means just use :pointer or a type-safe definition munged together with defctype and type translation. To pass or return a structure by value to a function, load the cffi-libffi system and specify the structure as (:struct structure-name). To pass or return the pointer, you can use either :pointer or (:pointer (:struct structure-name)).

Допустим такая ситуация - в библиотеке, которую мне надо использовать через cffi, типа ООП и инкапсуляция - структуры в доках не описаны и они могут от версии к версии меняться, но в доках хорошо описан интерфейс работы с этими структурами, который если и будет меняться то незначительно и которого вполне достаточно для использования библиотеки. Я хочу попробовать работать со структурами не описывая их передавая функциям интерфейса указатели. Я правильно понимаю что можно не писать на C обёртки функций чтобы те работали с указателями?

Вот есть в библиотеке fftw такой тип как fftw_plan и похоже это банально указатель на структуру. Это такая договорённость писать библиотеки или мне просто повезло?

ados ★★★★★ ()
Последнее исправление: ados (всего исправлений: 2)
Ответ на: комментарий от ados

Я хочу попробовать работать со структурами не описывая их передавая функциям интерфейса указатели. Я правильно понимаю что можно не писать на C обёртки функций чтобы те работали с указателями?

В смысле, не описывая поля структуры? Или что за «обёртки»? Если работа через указатель, то структуру можно не описывать (если тебе не надо читать/писать её поля самому).

Вот есть в библиотеке fftw такой тип как fftw_plan и похоже это банально указатель на структуру. Это такая договорённость писать библиотеки или мне просто повезло?

Договоренность. Если передавать структуру, а не указатель на неё, то при вызове функции будут копироваться все поля структуры. Если структура достаточно большая, то это неудобно.

monk ★★★★★ ()
Вы не можете добавлять комментарии в эту тему. Тема перемещена в архив.