Если вы хотите создать уникальные дескрипторы, вы можете сделать это с помощью malloc()
и struct
:
typedef intptr_t HANDLE_TYPE;
HANDLE_TYPE init_buffer_traverse(double * src, size_t src_len);
int copy_buffer(HANDLE_TYPE h_traverse, double * dest, size_t dest_len);
void close_handle_buffer_traverse(HANDLE_TYPE h);
typedef struct
{
double * source;
size_t source_length;
size_t position;
} TRAVERSAL;
#define INVALID_HANDLE 0
/*
* Returns a new traversal handle, or 0 (INVALID_HANDLE) on failure.
*
* Allocates memory to contain the traversal state.
* Resets traversal state to beginning of source buffer.
*/
HANDLE_TYPE init_buffer_traverse(double *src, size_t src_len)
{
TRAVERSAL * trav = malloc(sizeof(TRAVERSAL));
if (NULL == trav)
return INVALID_HANDLE;
trav->source = src;
trav->source_len = src_len;
trav->position = 0;
return (HANDLE_TYPE)trav;
}
/*
* Returns the system resources (memory) associated with the traversal handle.
*/
void close_handle_buffer_traverse(HANDLE_TYPE h)
{
TRAVERSAL * trav = NULL;
if (INVALID_HANDLE != h)
free((TRAVERSAL *)h);
}
int copy_buffer(HANDLE_TYPE h,
float* dest, int dest_length)
{
TRAVERSAL * trav = NULL;
if (INVALID_HANDLE == h)
return -1;
trav = (TRAVERSAL *)h;
int copy_length = trav->source_length - trav->position;
if (dest_length < copy_length)
copy_length = dest_length;
for (int i = 0; i*emphasized text* < copy_length; i++)
dest[i] = trav->source[trav->position + i];
// remember where to continue next time the copy_buffer() is called
trav->position += copy_length;
return copy_length;
}
Такой стиль использовался некоторыми программистами на C до появления C++. Стиль включает в себя структуру данных, которая содержит все элементы данных нашего «класса». Большинство API для класса принимает в качестве первого аргумента указатель на одну из этих структур. Этот указатель аналогичен указателю this
. В нашем примере этот параметр был назван trav
.
Исключением для API будут те методы, которые выделяют тип дескриптора; они похожи на конструкторы и имеют тип дескриптора в качестве возвращаемого значения. В нашем случае имя init_buffer_traverse
могло бы также называться construct_traversal_handle
.
Существует множество других методов, помимо этого, для реализации значения "непрозрачного дескриптора". Фактически, некоторые кодеры манипулировали битами (например, с помощью XOR), чтобы скрыть истинную природу дескрипторов. (Эта неясность не обеспечивает безопасности там, где это необходимо.)
В приведенном примере я не уверен (не смотрел на sndlib), имеет ли смысл хранить указатель и длину буфера назначения в структуре дескриптора или нет. Если это так, это сделает его дескриптором «буфера копирования», а не дескриптором «обхода», и вы захотите изменить всю терминологию из этого ответа.
Эти дескрипторы действительны только в течение времени жизни текущего процесса, поэтому они не подходят для дескрипторов, которые должны выдерживать перезапуски сервера дескрипторов. Для этого используйте базу данных ISAM и идентификатор столбца в качестве дескриптора. Подход с базой данных намного медленнее, чем подход с памятью/указателем, но для постоянных дескрипторов вы все равно не можете использовать значения в памяти.
С другой стороны, похоже, что вы реализуете библиотеку, которая будет работать в течение одного времени жизни процесса. В этом случае ответ, который я написал, должен быть пригоден для использования после модификации в соответствии с вашими требованиями.
Дополнение
Вы просили пояснить сходство с C++, о котором я упоминал выше. Чтобы быть конкретным, некоторый эквивалент (приведенному выше коду C) код C++ может быть:
class TRAVERSAL
{
double * source;
size_t source_length;
size_t position;
public TRAVERSAL(double *src, size_t src_len)
{
source = src;
source_length = src_len;
position = 0;
}
public int copy_buffer(double * dest, size_t dest_len)
{
int copy_length = source_length - position;
if (dest_length < copy_length)
copy_length = dest_length;
for (int i = 0; i < copy_length; i++)
dest[i] = source[position + i];
// remember where to continue next time the copy_buffer() is called
position += copy_length;
return copy_length;
}
}
Есть некоторые очевидные различия. Версия C++ выглядит немного менее многословной. Часть этого иллюзорна; эквивалент close_handle_buffer_traverse
теперь соответствует delete
объекту C++. Конечно, delete
не является частью реализации класса TRAVERSAL
, delete
поставляется с языком.
В версии C++ нет "непрозрачного" дескриптора.
Версия C является более явной и, возможно, делает более очевидными, какие операции выполняются аппаратным обеспечением в ответ на выполнение программы.
Версия C более приспособлена для использования приведения к HANDLE_TYPE
для создания "непрозрачного идентификатора", а не типа указателя. Версию C++ можно было бы «обернуть» в API, который делал то же самое, добавляя еще один уровень. В текущем примере пользователи этого класса будут поддерживать копию TRAVERSAL *
, которая не совсем "непрозрачна".
В функции copy_buffer()
версии C++ не нужно упоминать указатель trav
, поскольку вместо этого он неявно разыменовывает предоставленный компилятором указатель this
.
sizeof(TRAVERSAL)
должно быть одинаковым как для примеров C, так и для C++ - без vtable, а также при условии, что идентификация типа во время выполнения для C++ отключена, класс C++ содержит только ту же структуру памяти, что и структура C в нашем первом примере. .
Стиль «непрозрачный идентификатор» в C++ используется реже, поскольку штраф за «прозрачность» в C++ снижен. Членами данных class TRAVERSAL
являются private
, поэтому TRAVERSAL *
нельзя случайно использовать для нарушения нашего контракта API с пользователем API.
Обратите внимание, что как непрозрачный идентификатор, так и указатель класса уязвимы для злоупотреблений со стороны злонамеренного пользователя API — либо непрозрачный идентификатор, либо указатель класса могут быть напрямую приведены, например, к double **
, позволяя владельцу идентификатора изменить член source
. напрямую через память. Конечно, вы уже должны доверять вызывающему API, потому что в этом случае код вызова API находится в том же адресном пространстве. В примере сетевого файлового сервера могут возникнуть проблемы с безопасностью, если «непрозрачный идентификатор», основанный на адресе памяти, будет раскрыт снаружи.
Обычно я не стал бы вдаваться в подробности о доверии пользователя API, но хочу пояснить, что ключевое слово C++ private
не имеет «правоприменения», оно только указывает на соглашение между программистами, которое также соблюдается компилятором, если иное не указано человеком. .
Наконец, указатель класса C++ можно преобразовать в непрозрачный идентификатор следующим образом:
typedef intptr_t HANDLE_TYPE;
HANDLE_TYPE init_buffer_traverse(double *src, size_t src_len)
{
return (HANDLE_TYPE)(new TRAVERSAL(src, src_len));
}
int copy_buffer(HANDLE_TYPE h_traverse, double * dest, size_t dest_len)
{
return ((TRAVERSAL *)h_traverse)->copy_buffer(dest, dest_len);
}
void close_handle_buffer_traverse(HANDLE_TYPE h)
{
delete ((TRAVERSAL *)h);
}
И теперь наша краткость «эквивалентного» C++ может быть подвергнута дальнейшему сомнению.
То, что я написал о старом стиле программирования на C, относящемся к C++, не означало, что C++ лучше подходит для этой задачи. Я имею в виду только то, что инкапсуляция данных и сокрытие деталей реализации могут быть выполнены в C с помощью стиля, почти изоморфного стилю C++. Это может быть полезно знать, если вы программируете на C, но, к сожалению, сначала изучили C++.
PS
Я только что заметил, что наша реализация на сегодняшний день использовала:
dest[i] = (float)source[position + i];
при копировании байтов. Поскольку и dest
, и source
равны double *
(то есть оба они указывают на значения double
), здесь нет необходимости в приведении. Кроме того, приведение от double
к float
может привести к потере цифр точности в представлении с плавающей запятой. Так что это лучше всего удалить и переформулировать как:
dest[i] = source[position + i];
person
Heath Hunnicutt
schedule
15.02.2010
src_buffer
напрямую? - person Carl Norum   schedule 14.02.2010