LINUX.ORG.RU

Haskell импорт сишных структур

 , ,


0

4

Есть такой код на С:

#include <malloc.h>

struct arr{
	int **arr;
	int maxI;
	int maxJ;
};
typedef struct arr Arr;

Arr *newArr(int i, int j){
	Arr *a = malloc(sizeof(Arr));
	a -> arr = malloc(sizeof(int*) * i);
	a -> maxI = i;
	a -> maxJ = j;
	for(int n; n<i; ++n){
		a -> arr[n] = malloc(sizeof(int) * j);
	}
	return a;
}
void freeArr(Arr *a){
	for(int i=0; i<a -> maxI; ++i)
		free(a -> arr[i]);
	free(a -> arr);
	free(a);
}

int peekArr(Arr *a, int i, int j){
/*	printf(":%Ld: %d %d %d %d\n",(long long)(a -> arr),a -> maxI, a -> maxJ, i, j);
	fflush(stdout);
	printf("> %d\n", a -> arr[i][j]);
	fflush(stdout);*/
	return a -> arr[i][j];
}

void pokeArr(Arr *a, int i, int j, int v){
	a -> arr[i][j] = v;
}

int maxI(Arr *a){
	return a -> maxI;
}

int maxJ(Arr *a){
	return a -> maxJ;
}
Если использовать это как модуль в сишной программе, то все функции работают корректно. Я хочу импортировать из него структуру и методы работы с ней, пишу так
{-# INCLUDE "struct.c" #-}
{-# LANGUAGE ForeignFunctionInterface #-}

import Foreign.C
import Foreign.Ptr

foreign import ccall unsafe "newArr" c_newArr	:: CInt -> CInt -> IO (Ptr ())
foreign import ccall unsafe "freeArr" c_freeArr	:: Ptr () -> IO()
foreign import ccall unsafe "maxI" c_maxI	:: Ptr () -> CInt
foreign import ccall unsafe "maxJ" c_maxJ	:: Ptr () -> CInt
foreign import ccall unsafe "peekArr" c_peekArr	:: Ptr () -> CInt -> CInt -> IO CInt
foreign import ccall unsafe "pokeArr" c_pokeArr	:: Ptr () -> CInt -> CInt -> CInt -> IO()

newtype IArr2 = IA (Ptr ())

newArr	:: Int -> Int -> IO IArr2
newArr maxI maxJ = fmap IA $ c_newArr (fromIntegral maxI) (fromIntegral maxJ)

freeArr :: IArr2 -> IO()
freeArr (IA a) = c_freeArr a

maxI	:: IArr2 -> Int
maxI (IA a) = fromIntegral$ c_maxI a
maxJ	:: IArr2 -> Int
maxJ (IA a) = fromIntegral$ c_maxJ a
peekArr	:: IArr2 -> Int -> Int -> IO Int
peekArr (IA a) i j = fmap fromIntegral $ c_peekArr a (fromIntegral i) (fromIntegral j)
pokeArr	:: IArr2 -> Int -> Int -> Int -> IO()
pokeArr (IA a) i j v = c_pokeArr a (fromIntegral i) (fromIntegral j) (fromIntegral v)

main = do
	a <- newArr 4 6
	print (maxI a, maxJ a)
	peekArr a 0 0 >>= print
	mapM_ (\(i,j) -> pokeArr a i j 0) [(x,y) | x <- [0..3], y <- [0..5]]
	pokeArr a 0 0 1
	pokeArr a 3 2 2
	flip mapM_ [0..3] $ \i -> do
		mapM_ (\j -> peekArr a i j >>= putStr.( ++ " ").show) [0..5]
		putChar '\n'
	freeArr a
функции maxI и maxJ работают нормально, а на peekArr или pokeArr ловлю сегфолт. Там в сишной функции peekArr закоменчены два вывода, так вот при вызове из хаскеля первый срабатывает, а второй нет, то есть где-то повреждается динамически выделенный блок. Как этого избежать? Компилятор хаскеля ghc 7.4.1

★★★★★

Хм.. не воспроизводится:

(4,6)
0
1 0 0 0 0 0 
0 0 0 0 0 0 
0 0 0 0 0 0 
0 0 2 0 0 0

Как компилировалось?

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

а разве компилер не ругается на такое? clang какой-нить...

true_admin ★★★★★ ()

Раз c99, то матрицы лучше делать одним calloc с индексацией вида i * maxJ + j и flexible arrays:

#include <malloc.h>

typedef struct {
    int i, j, arr[];
} Arr;

Arr* newArr(int i, int j)
{
    Arr *a;
    a = calloc(sizeof(Arr) + i * j * sizeof(a->arr[0]), 1);
    a->i = i;
    a->j = j;
    return a;
}

void freeArr(Arr *a)
{
    free(a);
}

int peekArr(Arr *a, int i, int j)
{
    return a->arr[i * a->j + j];
}

void pokeArr(Arr *a, int i, int j, int v)
{
    a->arr[i * a->j + j] = v;
}

int maxI(Arr *a)
{
    return a->i;
}

int maxJ(Arr *a)
{
    return a->j;
}
quasimoto ★★★★ ()
Ответ на: комментарий от quasimoto

а разве при free(a) он сам освободит a -> arr, память не будет засираться?

Aswed ★★★★★ ()

Лол, так и запишем: без сишного кода Хаскель — беспомощный кусок говна.

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

дык ктож в 21 веке собирает без clang и не прогоняет через valgrind? Уверен, обе тулзы ругнутся.

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

free освобождает столько, сколько выделяет calloc/malloc, то есть менеджер кучи (из libc) при вызове malloc(N * M)/calloc(N, M) находит в куче свободный кусок размера N * M, возвращает указатель на его начало, а также запоминает сколько памяти было выделено (N * M, он может, например, записать это число прямо перед выделенным куском памяти, то есть в *((size_t*)((char*)ptr - sizeof(size_t))), или куда-то ещё — главное, чтобы значению указателя возвращаемого malloc однозначно соответствовал размер соответствующего куска памяти). calloc отличается тем что зануляет эту память. Так как размер блока известен в рантайме и связан со значением указателя на его начало — free принимает указатель на начало блока, узнаёт его размер и просто помечает соответствующую память как свободную, так что HM может опять её выделить.

В данном случае аллоцируется sizeof(Arr) + i * j * sizeof(a->arr[0]) байт, то есть первую часть под header (sizeof(Arr) — там только значения i и j) и вторую — под arr переменного размера i * j * sizeof(a->arr[0]) (это фича flexible arrays из C99 — a->arr продолжается дальше за границами структуры).

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

clang будет

a.c:56:16: warning: variable 'n' is uninitialized when used here [-Wuninitialized]
    for(int n; n<i; ++n)
               ^
a.c:56:14: note: initialize the variable 'n' to silence this warning
    for(int n; n<i; ++n)
             ^
              = 0

-Wuninitialized включается -Wall — у gcc тоже есть, но почему-то не работает тут (4.7.3).

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

Вот это да, а на потенциальное не корректное использование enum он еще случайно не ругается ? А то даже если явно указать функции enum аргумент и передать ей тупо число, даже выходящее за границы допустимых значений, или значение из другого enum, то gcc даже с опциями -pedantic -Wall молчит.

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

А без машинного кода сишечка — беспомощный кусок говна

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

Есть такое — -Wassign-enum (по -Weverything, -Wall не включает):

en.c:15:7: warning: integer constant not in range of enumerated type 'enum ab'
      [-Wassign-enum]
    f(5);
      ^

Ещё для enum-ов есть exhaustiveness как в хаскеле:

en.c:8:13: warning: enumeration value 'b' not handled in switch [-Wswitch]
    switch (x) {
            ^

но это у gcc тоже есть:

en.c:8:5: warning: enumeration value ‘b’ not handled in switch [-Wswitch]

но уже без понтов в виде подчёркиваний и цветного выхлопа :)

quasimoto ★★★★ ()
Ответ на: комментарий от s9gf4ult
en.c:17:7: warning: implicit conversion from enumeration type 'enum cd' to different
      enumeration type 'enum ab' [-Wenum-conversion]
    f(c);
    ~ ^

с другой стороны — в си простой int спокойно даункаститься в enum, в С++ — нет, и вообще -Wassign-enum и -Wenum-conversion там уже ошибки на уровне самого языка, а не предупреждения компилятора (кроме того — https://en.wikipedia.org/wiki/C++0x#Strongly_typed_enumerations).

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