Краткая версия:Кастинг Структуры пустоты указателей на структуры с типизированными указателями
Пусть у меня есть два структур:
struct charPtrWithLen
{
size_t len;
char * charPtr;
}
struct voidPtrWithLen
{
size_t len;
void * voidPtr;
}
Есть ли способ бросить voidPtrWithLen в charPtrWithLen и наоборот, или даже лучше , неявно преобразовывать один в другой, почти так же, как char * и void * могут быть легко переведены и неявно преобразованы между собой?
Другими словами:
Я пытаюсь написать все мои C так, что все указатели на массивы приносят их информацию о размере с ними. Я также пытаюсь написать общие функции, используя указатели void, где это применимо, чтобы сохранить операции, которые по существу идентичны, ну, идентичны. Я ищу способ передать структурированные массивы «размер-массив» с типизированным указателем в общие функции, содержащие аргументы «размер-массив», содержащие void-указатель.
Длинная версия, с вовлеченной например:
Таким образом, пустота указатели удивительно гибкая, так что я могу это сделать:
int foo(void * ptr, size_t dataLen);
/* ... */
char * c;
size_t c_n;
/* ... */
foo(c, c_n);
/* ... */
int * i;
size_t i_n;
/* ... */
foo(i, i_n);
Но поскольку картина «указатель на произвольный массив длины, а также размер там-то "настолько распространено, предположим, что в какой-то момент я устаю указывать свои различные функции в терминах пар аргументов, указателя и длины, и вместо этого я начинаю кодировать с такими парами, заключенными в структуру вместо:
typedef struct
{
size_t v_n;
void * v;
}
pointerWithSize;
/* ... */
int foo(pointerWithSize);
Пока все хорошо. Я всегда могу назначить «char * c» или «int * i» в «void * v» pointerWithSize с минимальными трудностями. Но когда вы делаете это достаточно долго, используя один и тот же шаблон, вы сталкиваетесь со следующей проблемой: достаточно скоро у вас есть куча общих функций, которые работают с данными агностически и, таким образом, счастливы принять недействительные указатели, например такие вещи, как:
pointerWithSize combinePointersWithSize(pointerWithSize p1, pointerWithSize p2);
int readFromStream(FILE * readFromHere, pointerWithSize * readIntoHere);
Но вы в конечном итоге с функциями, которые по своей сути предназначены для конкретных типов данных:
size_t countOccurancesOfChar(pointerWithSize str, char c);
int summate(pointerWithSize integers);
И тогда вы в конечном итоге с раздражением от того, чтобы сделать проливает внутри последней категории функций. Например. вы в итоге получаете такие вещи: /* Это внутри countOccurancesOfChar */ if ((char *) str.m) [i] == c) { /* ..или это внутри сумма: */ sum + = ((int *) integers.m) [i];
Итак, вы попадаете в точку, в которой у вас есть много функций, которые действуют конкретно на «строки с размером», и во всех этих случаях вы не хотите, чтобы вокруг с указателями void не было необходимости. Поэтому вместо того, в тех случаях, вы начинаете делать вещи, как это:
typedef struct
{
size_t v_n;
char * v;
}
stringWithSize;
/* ... */
size_t countOccurancesOfChar(stringWithSize str, char c);
int parseFormatting(stringWithSize str, struct someFormat_t foo);
, который является большим, потому что теперь весь код, связанной строка не должен быть завален слепками. НО, теперь я не могу использовать свою замечательную общую функцию combPointersWithSize, чтобы объединить мои строки, содержащиеся в stringWithSize, таким образом, чтобы это было синтаксически чисто, как я мог, если бы я все еще писал свои функции в терминах двух отдельных аргументов для каждого указателя - и-размер.
Чтобы закончить рисунок:
pointerWithSize combinePointersWithSize(pointerWithSize p1, pointerWithSize p2);
void * combineAlternative(void * p1, size_t p_n1, void * p2);
/* ... */
stringWithSize a, b, c;
/* ... */
/* This doesn't work, incompatible types: */
c = combinePointersWithSize(a, b);
/* But this works, because char * can be passed into void * parameter. */
c.v_n = a.v_n + b.v_n;
c.v = combineAlternative(a.v, a.v_n, b.v, b.v_n); /* Works fine. */
Возможные решения я Продуманные:
1: Не писать свои функции с теми структурами в качестве аргументов, вместо того, чтобы писать их с отдельными аргументами пары. Но это большая часть того, чего я хочу избежать в первую очередь - мне нравится «чистота» и ясность намерения, что имеет размер size_t и указатель, объединенный в одну структуру.
2: Есть ли что-то вроде этого:
stringWithSize a, b, c;
/* ... */
pointerWithSize d;
d = combinePointersWithSize((pointerWithSize){.v=a.v, .v_n=a.v_n}, (pointerWithSize){.v=b.v, .v_n=b.v_n})
/* and then do either this: */
c.v = d.v;
c.v_n = d.v_n;
foo(c);
/* ..or this: */
foo((stringWithSize){.v=d.v, .v_n=d.v_n});
..но я думаю, что большинство согласится, что это также, как плохо или хуже исходной задачи литья в библиотечных функций. На первый взгляд это выглядит хуже, потому что он выгружает нагрузку на клиентский код вместо кода библиотеки, который, мы надеемся, будет довольно стабильным после его реализации/завершения (включая тестирование/etc). С другой стороны, если вы сохранили каждую функцию, определенную с точки зрения void *, содержащего pointerWithSize, вы могли бы заставить подобные приведения к тому, что вы делаете внутри своих собственных функций, в другом месте их кода и, что еще хуже, теряя преимущество компилятора, кричащего на вас, потому что теперь код несет все в пределах одной структуры pointerWithSize.
Я также обеспокоен тем, как многие компиляторы там есть возможность оптимизировать первую из двух вариантов этого решения прочь (где «d» серверы просто как владелец временного результата
3:. Союз . -of-указатели Вместо моего предыдущего примера pointerWithSize, я хотел бы сделать:.
typedef union
{
void * void;
char * char;
int * int;
/* ...and so on... */
}
rainbowPointer;
typedef struct
{
size_t v_n;
rainbowPointer v;
}
pointerWithSize;
на первый взгляд, это почти достаточно хорошо, однако, я очень часто в конечном итоге хочет хранить массивы некоторой структуры, которая специфична для программа, над которой я работаю внутри этой конструкции «указатель с размером», и в этих случаях предопределенный союз типов указателей быть бесполезным для меня, я все равно вернусь к этой проблеме.
4: Я мог бы написать функции обертки для каждого типа перестановленного указателя. Я мог бы EVEN писать функциональные макросы, чтобы определить каждый из этих типов типов с указателем и размером, который в одном и том же мачете генерирует функции обертки. Например:
#define pointerWithSizeDef(T, name) \
typedef struct \
{ \
size_t v_n; \
T * v;
} \
name; \
foo_ ## name (name p1) \
{ \
/* generic function code defined in macro */ \
/* Or something like this: */ \
foo((pointerWithSize){.v=p1.v, .v_n=p1.v_n});
};
/* Then, stuff like this: */
pointerWithSizeDef(char, stringWithSize)
Моя интуиция заключается в том, что рано или поздно этот метод станет громоздким.
5: Если есть механизм, без влияния на производительность, но это непривлекательный иначе, я мог бы написать мои общие функции, как функции, как макросы, которые, в свою очередь, вызывающими основную фактическую функцию:
int foo_actual(void * v, size_t v_n);
#define foo(p) \
foo_actual(p.v, p.v_n);
..or даже что-то вроде этого, чтобы заменить произнесения синтаксис:
#define castToPointerWithSize(p) \
((pointerWithSize){.v=p.v, .v_n=p.v_n})
/* ... */
stringWithSize a;
foo(castToPointerWithSize(a));
Но эти примеры для возможного-решение- # 5 шоу, я не могу на самом деле придумать способ сделать это, что не будет быстро стать возможной проблемой (например, если кто-то захочет разместить вызов функции, который вернул указательWithSize вместо «p» в приведенных выше примерах - вы будете запускать эту функцию дважды, и это не будет очевидно из кода.
Поэтому я не думаю, что какие-либо из решений, о которых я думал, действительно достаточны для моего использования, поэтому я надеюсь, что некоторые из вас знают о синтаксисе или механизме синтаксиса, которые я мог бы использовать здесь, чтобы сделать это легко отличить/«отличить» между двумя структурами, которые идентичны, за исключением типа указателя одного из своих членов.
Но только потому, что один является 'void *', а другой - 'char *'. Если другой указатель на какой-то другой тип, это не переносится. –