Передача многомерного массива во внешнюю функцию "C" как void * - PullRequest
1 голос
/ 11 июля 2019

В моей библиотеке есть функция C, которая прекрасно работает с многомерными массивами:

void    alx_local_maxima_u8 (ptrdiff_t rows, ptrdiff_t cols,
                    const uint8_t arr_in[static restrict rows][static cols],
                    bool arr_out[static restrict rows][static cols])
        __attribute__((nonnull));

И у меня есть unsigned char *, который я получаю от class, определенного в openCV.Этот указатель представляет двумерные данные, но это не так, и я должен использовать их с арифметикой указателей (unsigned char *img_pix = img->data + i*img->step + j;), что мне особенно не нравится.

Я создаю массив boolтого же размера изображения (это реальный массив, поэтому я могу использовать нотацию массива) для хранения результатов функции.

Я мог бы написать почти точную копию alx_local_maxima_u8(), которая использует толькоуказатель и арифметика указателя, но я хотел бы иметь возможность использовать его повторно, если смогу.

Безопасно ли писать прототип, который использует void * таким образом, чтобы обмануть C ++ ?:

extern "C"
{
[[gnu::nonnull]]
void    alx_local_maxima_u8 (ptrdiff_t rows, ptrdiff_t cols,
                             const void *arr_in,
                             void *arr_out);
}

Теоретически void * может содержать любой указатель, который получит C, и C не получит доступ к данным, которые не принадлежат этим указателям, поэтому единственные проблемы, которые я вижу, - это наложение аunsigned char * как uint8_t *[] и пропуск void *, где ожидается uint8_t *[], что может вызвать все виды ошибок компоновщика.Кроме того, я не знаю, будут ли C bool и C ++ bool преобразовываться в одно и то же в памяти (я надеюсь, что это так).

Может быть, мне следует написать оболочку на C, которая получает void *и передает их фактической функции, так что мне не нужно обманывать C ++.

Производительность - это проблема, но я использую -flto, поэтому любые обертки, вероятно, исчезнут в компоновщике.

Я использую GCC (-std=gnu++17) в Linux с включенным POSIX.

Ответы [ 2 ]

2 голосов
/ 15 июля 2019

Гарантия того, что T [N] [M] будет содержать NxM последовательных объектов типа T, препятствует некоторым другим полезным оптимизациям;Основная полезность этой гарантии в предстандартных версиях C заключалась в том, что она позволяла коду рассматривать хранилище как одномерный массив в некоторых контекстах, но многомерный массив в других.К сожалению, Стандарты не могут распознать какое-либо различие между указателями, сформированными в результате распада внутреннего массива, и указателем, сформированным путем приведения внешнего массива к типу внутреннего элемента либо напрямую, либо через void*, даже если они накладывают ограничения напервый, который будет препятствовать полезности последнего.

На любой типичной платформе, в отсутствие оптимизации всей программы, ABI будет рассматривать указатель на элемент многомерного массива как эквивалентный указателюк элементу одномерного массива с таким же общим количеством элементов, что делает безопасным обработку последнего как первого.Я не верю, что в стандарте C или C ++ есть что-то, что не позволило бы реализации «оптимизировать» что-то вроде:

// In first compilation unit
void inc_element(void*p, int r, int c, int stride)
{
  int *ip = (int*)p;
  ip[r*stride+c]++;
}
// In second compilation unit
int array[5][5];
void inc_element(void*p, int r, int c, int stride);
int test(int i)
{
  if (array[1][0])
    inc_element(array, i, 0, 5);
  return array[1][0];
}

путем замены вызова на inc_element на array[0][i*5]++что, в свою очередь, может быть оптимизировано до array[0][0]++.Я не думаю, что авторы Стандарта намеревались пригласить компиляторы для таких «оптимизаций», но я не думаю, что они думали, что агрессивные оптимизаторы интерпретируют неудачу, запрещающую такие вещи, как приглашение.

1 голос
/ 15 июля 2019

Передача указателя массива как const void * не должна вызывать каких-либо проблем, но следует помнить, что bool может иметь различное представление в C и C ++.Было бы безопаснее использовать более явный тип, такой как unsigned char, для базового типа массива.

Указание этого типа для указателя также улучшило бы удобочитаемость, поскольку к ячейкам матрицы можно обращаться напрямую, используя p[r * cols + c].

...