Как проектировать библиотеки, написанные на ANSI C? - PullRequest
3 голосов
/ 29 сентября 2011

Я хочу разработать библиотеку с ANSI C.

У меня есть string struct:

struct libme_string
{
  char* buffer;
  int length;
};

Я хочу написать функцию libme_create_string(), которая создает и инициализирует string (как конструкторы в C ++).

Какой из этих методов лучше разработать libme_create_string()?


Метод № 1

Выделите память для строкового объекта в libme_create_string() и верните ее:

struct libme_string* libme_create_string(int length)
{
  // Check arguments...

  // Allocate memory for object.
  struct libme_string* str = malloc(sizeof(struct libme_string));

  // Handle memory allocation errors...

  str->buffer = malloc(length);
  str->length = length;

  // Handle memory allocation errors...

  return str;
}

void libme_delete_string(struct libme_string* str)
{
    // Check arguments...

    free(str->buffer);
    free(str);
}

Используйте

struct libme_string* str;
str = libme_create_string(1024);

// ...

libme_delete_string(str);
str = NULL;

Метод № 2

Не выделяйте память для строкового объекта в функции libme_create_string(), примите это в качестве аргумента:

struct void libme_create_string(libme_string* str, int length)
{
  // Check arguments...

  // Just allocate memory for members.
  str->buffer = malloc(length);
  str->length = length;

  // Handle memory allocation errors...
}

void libme_delete_string(struct libme_string* str)
{
  // Check arguments...

  free(str->buffer);
}

Используйте

struct libme_string str; // << different, not a pointer!
libme_create_string(&str, 1024);

// ...

libme_delete_string(&str);

Примечания

  • string просто образец.
  • Метод № 2 быстрее, не так ли?

Наконец, есть ли хорошие рекомендации по проектированию библиотек, написанных на C?

Ответы [ 3 ]

4 голосов
/ 29 сентября 2011

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

Если вы стараетесь изо всех сил инкапсулировать инстанцирование (что вы должны делать в любом случае), тогда первый действительно единственный путь - & mdash; один шаг, сделано. Вторая версия означает, что для того, чтобы иметь полностью инициализированную переменную, вам нужно не только создать ее экземпляр, но и немедленно вызвать вспомогательную функцию для нее. Этот дополнительный шаг - ошибка, ожидающая своего появления.

3 голосов
/ 29 сентября 2011

Лично я предпочитаю первый метод. Согласен: это немного похоже на C ++, но ...

thing_t *thing_new(...);
void thing_delete(thing_t *ptr);

Я считаю, что все члены "size" или "count" должны быть без знака, предпочтительно size_t. Также: ваш последний фрагмент пытается освободить () автоматическую переменную. Это хорошая причина не использовать его.

EDIT:

Существует (как минимум) третий способ: вернуть весь объект как значение . Мне не особенно нравится метод, но он по крайней мере избегает двойного распределения. Это выглядит так:

typedef struct {
  StrLen length;
  StrType type;      /* type is not stored in the brainfile 
                     **but recomputed on   loading */
  char *word;
} STRING;

STATIC STRING new_string(char *str, size_t len)
{
STRING this;

if (str) {
     if (!len) len = strlen(str);
     if (len) { this.word = malloc(len); memcpy(this.word, str, len); }
     else { this.word = malloc(1); memset(this.word, 0, 1); }
     this.length = len;
     this.type = word_classify(this);
     }
else        {
     this.word = NULL;
     this.length = 0;
     this.type = 0;
     }
return this;
}

Типичное использование выглядит следующим образом:

if (*np == WORD_NIL) {
  STRING this;
  *np = dict->size++;
  this = new_string(word.word, word.length);
  dict->entry[*np].string = this;
  dict->entry[*np].hash = hash_word(this);
  }

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

2 голосов
/ 29 сентября 2011

Почему бы не разделить процесс на две функции, чтобы вы могли использовать все, что вам нужно:

struct libme_string * create_string();
void destroy_string(struct libme_string *);

struct libme_string * init_string(struct libme_string * str, unsigned int length);
struct limbe_string * deinit_string(struct libme_string * str);

Использование # 1, все динамические распределения:

struct libme_string * str = init_string(create_string(), 10);
destroy_string(deinit_string(str));

Использование # 2, автоматическая внешняя структура:

struct libme_string str;
init_string(&str);
deinit_string(&str);

Убедитесь, что функции init возвращают указатель, чтобы вы могли составлять вызовы, как я.

Если deinit() также устанавливает указатель на ноль, то вы можете destroy() вызвать deinit(), если указатель ненулевой, хотя это немного нарушает симметрию.

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