Первый пост мой.
Смотрите, какую я фигню написал. Полностью типобезопасный вектор на сишных макросах:
#include <stdlib.h>
#include <stdio.h>
// сами напишите, короче:
// void *xmalloc(size_t nelems, size_t elemsz)
// void *xrealloc(void *p, size_t nelems, size_t elemsz)
// void *x2realloc(void *p, size_t *pnelems, size_t elemsz)
#define XNEW(Type_, N_) ((Type_*) xmalloc(N_, sizeof(Type_)))
#define VECTOR_OF(Type_) struct { Type_ *data; size_t size, capacity; }
#define VECTOR_NEW_RESERVE(Type_, Capa_) {XNEW(Type_, Capa_), 0, Capa_}
#define VECTOR_INIT(Vec_) \
do { \
(Vec_).data = NULL; \
(Vec_).size = 0; \
(Vec_).capacity = 0; \
} while (0)
#define VECTOR_INIT_RESERVE(Vec_, Capa_) \
do { \
(Vec_).data = XNEW(Type_, Capa_); \
(Vec_).size = 0; \
(Vec_).capacity = (Capa_); \
} while (0)
#define VECTOR_CLEAR(Vec_) \
do { (Vec_).size = 0; } while (0)
#define VECTOR_RESERVE(Vec_, Capa_) \
do { \
if ((Vec_).capacity < (Capa_)) { \
(Vec_).capacity = (Capa_); \
(Vec_).data = xrealloc((Vec_).data, (Vec_).capacity, sizeof(*(Vec_).data)); \
} \
} while (0)
#define VECTOR_ENSURE(Vec_, Capa_) \
do { \
if ((Vec_).capacity < (Capa_)) { \
(Vec_).data = x2realloc((Vec_).data, &(Vec_).capacity, sizeof(*(Vec_).data)); \
} \
} while (0)
#define VECTOR_SHRINK(Vec_) \
do { \
(Vec_).capacity = (Vec_).size; \
(Vec_).data = xrealloc((Vec_).data, (Vec_).capacity, sizeof(*(Vec_).data)); \
} while (0)
#define VECTOR_PUSH(Vec_, Elem_) \
do { \
VECTOR_ENSURE(Vec_, (Vec_).size + 1); \
(Vec_).data[(Vec_).size++] = (Elem_); \
} while (0)
#define VECTOR_FREE(Vec_) free((Vec_).data)
int main()
{
VECTOR_OF(int) v = {}; // прям как в крестах
//VECTOR_OF(int) v = {1, 2, 3}; // не, так нельзя — warning: initialization makes pointer from integer without a cast
int x = 1;
VECTOR_PUSH(v, x++); // no side effects
VECTOR_PUSH(v, x++);
VECTOR_PUSH(v, x++);
for (size_t i = 0; i < v.size; ++i) {
printf("%d\n", v.data[i]);
}
VECTOR_FREE(v);
//VECTOR_OF(char) u = VECTOR_NEW_RESERVE(int, 32); // warning: initialization from incompatible pointer type
VECTOR_OF(char) u = VECTOR_NEW_RESERVE(char, 32);
//VECTOR_PUSH(u, "x"); // warning: assignment makes integer from pointer without a cast
VECTOR_FREE(u);
}
Их ещё тайпдефать можно, типа
typedef VECTOR_OF(char) String;. Только вот незатайпдефенные нельзя передавать и возвращать из функций, потому что анонимные структуры в сишке считаются ничему не равными (а вот в составе других структур — можно). Можно было бы предложить делать так:
void push_next(VECTOR_OF(int) *pv)
{
static int x = 1;
VECTOR_PUSH(*pv, x++);
}
void print_vec(VECTOR_OF(int) *pv)
{
for (size_t i = 0; i < pv->size; ++i) {
printf("%d\n", pv->data[i]);
}
}
int main()
{
VECTOR_OF(int) v = {};
push_next((void*) v);
push_next((void*) v);
push_next((void*) v);
print_vec((void*) v);
VECTOR_FREE(v);
}
но это включает предупреждения о том, что функция принимает указатель на анонимную структуру, описанную прямо в списке аргументов.
Но на самом-то деле, это нинужно:
1. если функция вообще не меняет вектор, а только читает, то ей достаточно константного указателся и длины;
2. если функция не меняет размер вектора, а только читает и изменяет элементы, то ей достаточно неконстантного указателся и длины;
3. если функция может менять размер вектора, но только в меньшую сторону, то ей достаточно неконстантного указателся и указателся на длину;
4. если функция может менять размер вектора в большую сторону, то ей нужны (увы): указатель на указатель, указатель на длину, указатель на capacity — см.
x2realloc.
Такие функции будут работать не только с векторами, а с любыми непрерывными кусками данных (в последнем случае — только с полученными с помощью malloc). Хорошо же.
Кто-нибудь делает так же?