Самый безопасный и самый читаемый способ обернуть алгоритм общими аргументами? - PullRequest
1 голос
/ 17 июня 2011

Введение

У меня есть алгоритм, который берет указатель на массив char. Алгоритм сначала извлекает длину массива, а затем переворачивает массив.

Проблема

Проблема в том, что я хочу использовать это в массиве wchar_t. И хотите иметь возможность сделать это без необходимости копировать всю функцию, изменять имя и тип аргумента.

Вот упомянутая функция:

void reverseString(char *str){
  unsigned int l = getStringLength(str);
  int i = 0;
  int m = l >> 1;

  while(i < m){
    str[i] ^= str[l - 1];
    str[l - 1] ^= str[i];
    str[i] ^= str[l - 1];
    i++;
    l--;
  }
}

Начиная с поиска в Google и чтения SO, он не сможет использовать указатель void (концептуально то же самое, используя union), поскольку это оставит мне решение, подобное этому, что для меня так же плохо, как написание отдельных функций, но с разными именами и типами аргументов:

void reverseString(void *array, short typeSize){
  unsigned int l = getArrayLength(array);
  int m = l >> 1;
  int i = 0;
  char *str = 0;
  wchar_t *wstr = 0;

  if(typeSize == 1){
    str = (char *) array;
    while(i < m){
      str[i] ^= str[l - 1];
      str[l - 1] ^= str[i];
      str[i] ^= str[l - 1];
      i++;
      l--;
    }
  }else if(typeSize == 4){
    wstr = (wchar_t *) array;
    while(i < m){
      wstr[i] ^= wstr[l - 1];
      wstr[l - 1] ^= wstr[i];
      wstr[i] ^= wstr[l - 1];
      i++;
      l--;
    }
  }
}

Примечание: getStringLength - это просто функция, которая перебирает указатель, пока не достигнет '\0' и не вернет сумму итерации.

Ответ

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

Редактировать: Функция, которую я показал, является лишь примером, я ищу универсальное решение проблем с алгоритмами.

Ответы [ 3 ]

2 голосов
/ 17 июня 2011

Использование «дженериков» в C, вероятно, приведет к появлению кода, который заметно медленнее и более сложен / труден для чтения / труден в обслуживании, чем исходный код.Используйте препроцессор, если вы должны сделать это.

Я рекомендую избегать этой техники, если это вообще возможно: вам действительно следует использовать char или wchar_t в вашей программе, а несмесь обоих!(char или UChar или почти универсально предпочтительнее, поскольку вы можете выбрать кодировку, но я отступаю ...)

#define gchar char
#define gstrlen strlen
#define func_name reverse
#include "reverse_impl.h"
#undef gchar
#undef gstrlen
#undef func_name

#define gchar wchar_t
#define gstrlen wstrlen
#define func_name wreverse
#include "reverse_impl.h"
#undef gchar
#undef gstrlen
#undef func_name

Затем в reverse_impl.h:

void func_name(gchar *str)
{
    gchar *p = str, *q = str + gstrlen(str), t;
    if (p == q)
       return;
    q--;
    for (; p < q; p++, q--) {
        t = *p;
        *p = *q;
        *q = t;
    }
}

Кроме того, НЕ ДЕЛАЙТЕ ЭТОГО:

x ^= y; // bad!
y ^= x;
x ^= y;

Труднее читать и вполне возможно намного медленнее выполнить.

Такжеобратите внимание, что и reverse, и wreverse создадут мусор, если вы дадите им ввод Unicode: reverse сделает некорректный вывод, а wreverse может переключить диакритические знаки или полностью испортить Hangul, в зависимости от того, как они представлены.

2 голосов
/ 17 июня 2011

Вряд ли это будет эффективно, но вы можете легко сделать так, чтобы ваша версия void reverseString(void *array, short typeSize) реверсировала элементы с помощью тривиальной арифметики указателей и memcpy s соответствующего размера.

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

[В дополнение: обратите внимание, что использование свопа XOR вряд ли будет более эффективным, чем его выполнение »наивно".Это, конечно, менее читабельно!]

1 голос
/ 17 июня 2011

... универсальное решение ...

Решение состоит в том, чтобы написать что-то вроде qsort(): все функции, которые должны знать размер отдельныхзначения передаются в вашу собственную функцию с указателями, которые аннулируются повсюду

#include <math.h>
#include <stdio.h>
#include <wchar.h>

void universalReverseArray(void *arr, size_t siz,
                           size_t (*arrlen)(void*),
                           void (*swap)(void*, void*))
{
  size_t elems = arrlen(arr);
  size_t i = 0;
  size_t m = elems >> 1;
  unsigned char *p = arr;

  while(i < m) {
    swap(p + i * siz, p + (elems - 1) * siz);
    i++;
    elems--;
  }
}

void cswap(void *a, void *b) {
  char *aa = a, *bb = b;
  char t = *aa;
  *aa = *bb;
  *bb = t;
}

void dswap(void *a, void *b) {
  double *aa = a, *bb = b;
  double t = *aa;
  *aa = *bb;
  *bb = t;
}

void wswap(void *a, void *b) {
  wchar_t *aa = a, *bb = b;
  wchar_t t = *aa;
  *aa = *bb;
  *bb = t;
}

size_t clen(void *arr) {
  char *aa = arr;
  size_t retval = 0;
  while (*aa) {
    retval += 1;
    aa += 1;
  }
  return retval;
}

size_t dlen(void *arr) {
  double *aa = arr;
  size_t retval = 0;
  while (fabs(*aa) >= 0.0001) {
    retval += 1;
    aa += 1;
  }
  return retval;
}

size_t wlen(void *arr) {
  wchar_t *aa = arr;
  size_t retval = 0;
  while (*aa) {
    retval += 1;
    aa += 1;
  }
  return retval;
}

int main(void) {
  double x[] = {1, 2, 3, 4, 5, 0};
  char y[] = "foobar";
  wchar_t z[4];
  z[0] = 'a'; z[1] = 'b'; z[2] = 'c'; z[3] = 0;

  for (int k=0; k<5; k++) {printf("%f ", x[k]);}
  printf("%s ", y);
  printf("%ls\n", z);

  universalReverseArray(x, sizeof *x, dlen, dswap);
  universalReverseArray(y, sizeof *y, clen, cswap);
  universalReverseArray(z, sizeof *z, wlen, wswap);

  for (int k=0; k<5; k++) {printf("%f ", x[k]);}
  printf("%s ", y);
  printf("%ls\n", z);

  return 0;
}

Вы можете видеть, что это работает на ideone: http://ideone.com/t1iOg

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...