Программирование на языке С для пакета MATLAB [часть 2] - Программирование - Каталог статей по программированию - Студия перевода Нуреева Александра
Форма входа
E-mail:
Пароль:
Категории раздела
Программирование [4]
Статьи по программированию по C, C++, Delphi, FoxPro, Assmbler и т.д. Инструкция по Visual Studio .NET.
Поиск
Главная » Статьи » Программирование

Программирование на языке С для пакета MATLAB [часть 2]
ЧАСТЬ 2

Перейти к Части 3 >>
Программирование на языке С для пакета MATLAB

На вход данной функции допускаются только двумерные массивы ячеек. С помощью MATLAB API функции mxGetCell мы извлекаем отдельный элемент входного массива. Для массива ячеек элементы имеют тип mxArray. Обратим внимание на то, что в функции mxGetCell отдельные элементы индексируются единственным индексом, значение которого нужно назначать исходя из упорядочения элементов массивов системы MATLAB по столбцам. Именно поэтому элемент из i-й строки и j-ro столбца извлекается с помощью следующего выражения (индексы в языке С начинаются с нуля):

pAr= mxGetCell( pln[0], i+j*M );
Указатель pAr на структуру mxArray далее передается в функцию MyPrint, где С ПОМОЩЬЮ функций mxIsChar, mxIsDouble, mxIsComplex, mxIsCell, mxIsStruct, mxIsUint8 и mxIsSparse определяется, какие данные содержатся в этой структуре.

Так как в языке С индексы массивов начинаются с нуля, а в М-языке они начинаются с единицы, то, чтобы наблюдать в командном окне системы MATLAB привычные значения индексов, мы в функции MyPrint увеличиваем значения индексов на единицу при их выводе в командное окно функцией mexPrintf.

Протестируем работу созданной только что МЕХ-функции мехCell1 на примере простого массива типа cell (см. рис. 12.14):

Если же применить функцию мехСеll1 к массиву ячеек MyCellArray

MyStruct = struct('fieldl',[ 1 2 3],'field2','Hello');
MyCellArray( 1,1)={ 'Bonjour!' };
MyCellArray( 1,2)={[123;456;789]>;
MyCellArray( 2, 1 ) = { MyStruct };
MyCellArray( 2,2)={[975]};

который мы ранее создавали в главе 7 при изучении массивов типа cell, то результат работы этой функции покажет, что распознаются и ситуации, когда некоторые элементы входного массива ячеек сами имеют сложную структуру: например, являются массивами структур:

МехСell1 ( MyCellArray )
1-1 element is Char
1-2 element is Double
2-1 element is Struct
2-2 element is Double

Таким же образом распознаются ситуации, когда элементами массива ячеек, в свою очередь, являются массивы ячеек.

Заканчивая рассмотрение конкретных примеров программирования МЕХ-функций, отметим, что со всеми не рассмотренными нами MATLAB API функциями всегда можно ознакомиться по встроенной в систему MATLAB подсистеме справочной информации.

12.6. Вызов функций и команд системы MATLAB из МЕХ-функций
Мы уже говорили в предыдущем параграфе, что помимо MATLAB API функций с префиксом mx имеются также MATLAB API функции с префиксом тех. Последние предназначены для выполнения команд и функций среды MATLAB. В примерах предыдущего параграфа мы на практике использовали такого рода функции mexErrMsgTxt, mexWarnMsgTxt И mexPrintf.

Однако концептуально наиболее важной функцией такого типа (помимо интерфейсной функции mexFunction) является функция mexCallMATLAB. Эта функция позволяет изнутри С-кода разрабатываемых нами МЕХ-функций вызывать другие МЕХ-функции, любые М-функции и вообще все команды и функции среды MATLAB. Отсюда ясно, что важность функции mexCallMATLAB трудно переоценить. Именно эта функция позволяет самым тесным образом интегрировать МЕХ-фуикции в среду MATLAB, сделав их самым непосредственым расширением этой программной среды.

В качестве примера использования функции mexCallMATLAB рассмотрим сейчас пример МЕХ-функции, которая ничего не принимает на входе, но самостоятельно создает квадратную матрицу типа double, заполняет ее элементы конкретными значениями, после чего вызывает встроенную в среду MATLAB функцию det для вычисления определителя полученной матрицы. Далее наша МЕХ-функция выведет результат вычисления определителя в командное окно системы MATLAB опять-таки с помощью функции mexCallMATLAB.

Эта МЕХ-функция, которую мы назовем MyMexDeterminant, всю работу производит в своем собственном коде и не возвращает в среду MATLAB никаких значений. Поэтому мы проверяем параметры nIn и nOut, чтобы гарантировать правильный вызов этой МЕХ-функции из командного окна системы MATLAB (то есть без входных параметров и без возвращаемых значений).

Перед тем как писать код МЕХ-функции MyMexDeterminant, представим прототип функции mexCallMATLAB:

int mexCallMATLAB( int nOut, mxArray*pOut[],int nIn, mxArray* pIn[], const char* name );

У этой функции всего пять параметров. Первые четыре из них совпадают с параметрами функции mexFunction, что абсолютно очевидно, потому что это и есть интерфейсная функция среды MATLAB (как же иначе можно было бы вызвать из МЕХ-функции другую МЕХ-функцию). Пятый параметр представляет имя функции (команды), подлежащей выполнению.

Теперь мы готовы к написанию кода МЕХ-функции MyMexDeterminant (напомним еще раз, что имя МЕХ-функции совпадает с именем проекта в Microsoft Developer Studio и используется при вызове этой функции, по не встречается в ее исходном С-коде):

#include "mex.h"
#include
///////////////////////////////////////////////////////
void mexFunction( int nOut, mxArray* pOut[],
int nIn, const mxArray* pIn[] )
{
int ret;
double x;
char str[128];
mxArray* pAr[1];
mxArray* pRes[1];
double pS[] = {1,4,7,2,5,8,3,6,9};
// Input and output checking:
if(nIn!=0) mexErrMagTxt("None input required."); else if(nOut != 0)
mexErrMsgTxt("None output required.");
// 3-by-3 matrix creation and element setting:
pAr[0] = mxCreateDoubleMatrix(3, 3, mxREAL);
memcpy( mxGetPr( pAr[0] ), pS, sizeof( pS ) );
// We ask MATLAB to calculate determinant.
// The result will be in pRes.
ret = mexCallMATLAB( 1, pRes, 1, pAr, "det" );
if( ret != 0 ) mexErrMsgTxt("det failure");
// Mow we show the result in Command window:
// this is equivalent to "disp" command:
x = *( mx6etPr( pRes[0] ) );
sprintf( str, "determinant - %f", x );
mxDestroyArray( pRes[0] );
pRes[0] = mxCreateString( str );
mexCallMATLAB( 0, NULL, 1, pRes, "disp" );
// the last memory cleanup (good practicel):
mxDestroyArray( pAr[0] );
mxDestroyArray( pRes[0] );
}
/////////////—the end of the code ///////////////

По тексту функции можно дать несколько пояснений. Во-первых, заполнение созданного массива типа double значениями его элементов можно выполнить единственным вызовом функции memcpy, так как эта функция за один раз копирует содержимое блока памяти с подготовленными элементами в блок памяти, отведенный под элементы в структуре mxArray.

Вызов функции mexCallMATLAB для вычисления определителя (детерминанта) матрицы достаточно очевиден:

ret = mexCallMATLAB( 1, pRes, 1, pAr, "det" );
У нас тут имеются единственный входной параметр и единственное выходное значение. Входной параметр передается через массив pAr (этот массив состоит из одного элемента, являющегося указателем на структуру mxArray), а выходное значение будет прописано в массив pRes. Последний массив также состоит из единственного указателя на mxArray. Именно так эти переменные и были определены в нашем коде:

mxArray* pAr[1]; mxArray* pRes[1];
Важно проверять возврат функции mexCallMATLAB, так как операция вычисления определителя не всегда возможна (вдруг вы по ошибке передали для вычисления неквадратную матрицу, например).

Из результирующего массива pRes[0] мы извлекаем значение вычисленного определителя и помещаем его в переменную х типа double, после чего используем значение этой переменной для формирования строки текста, содержащего это значение. Делается это с помощью стандартной библиотечной С-функции sprintf.

Далее мы уничтожаем за ненадобностью ранее сформированный массив pRes[0] (фактически освобождаем выделенную под него память), а затем по этому же указателю (не пропадать же добру) создаем новый массив системы MATLAB, но этот массив уже имеет тип char. Он-то и будет служить входным параметром для функции mexCallMATLAB, с помощью которой подготовленное текстовое сообщение и выводится в командное окно системы MATLAB (см. рис. 12.15):

Вот этот фрагмент кода, который осуществляет показанный на рисунке вывод:

pRes[0] = mxCreateString( str );
mexCallMATLAB( 0, NULL, 1, pRes, "disp" );

Так как для исполняемой таким образом функции disp системы MATLAB возвращаемые значения не используются, то мы и прописываем первые два параметра функции mexCallMATLAB нулем и значением NULL соответственно.

В конце текста нашей МЕХ-функции хорошим решением будет самостоятельное (чтобы не перегружать пакет MATLAB дополнительной работой) освобождение памяти, выделенной под структуры mxArray (то есть под массивы системы MATLAB):

// the last memory cleanup (good practice!): mxDestroyArray( pAr[0] );
mxDestroyArray( pRes[0] );

Выполняется эта работа с помощью MATLAB API функции mxDestroyArray, синтаксис вызова которой абсолютно очевиден.

Глава 13. Взаимодействие Windows-приложений с системой MATLAB
13.1. Взаимодействие приложений Windows с MATLAB Engine
До сих пор мы работали целиком и полностью с единственным приложением - с пакетом MATLAB. Даже когда мы разрабатывали некоторые функции на языке С, мы компилировали их в динамические библиотеки, которые для работы загружаются в адресное пространство работающей системы MATLAB, после чего становятся неотделимы от нее. Можно сказать, что МЕХ-функции системы MATLAB (их-то мы и разрабатывали в предыдущей главе на языках C/C++) являются прямым расширением этой системы.

Как быть, если мы разрабатываем собственное изолированное приложение Windows, в котором требуется осуществлять серьезные математические вычисления?

Пакет MATLAB имеет убедительный ответ на этот вопрос, так как предоставляет прямой интерфейс для взаимодействия вашего приложения с этим пакетом. Способ взаимодействия отдельного приложения с системой MATLAB называется взаимодействием с подсистемой MATLAB Engine. В некотором смысле это просто принятая в рамках системы MATLAB терминология. Про приложение, которое взаимодействует с системой MATLAB по технологии MATLAB Engine, говорят, что это приложение MATLAB Engine. Всякая терминология хороша тем, что позволяет точно формулировать детали разработки и обстоятельства функционирования объектов обсуждения.

С точки зрения исходных текстов разрабатываемого на языке С изолированного приложения его взаимодействие с системой MATLAB осуществляется путем вызова специальных функций из библиотеки MATLAB Engine (это небольшая динамическая библиотека libeng.dll размером 32 KB), а функции из этой библиотеки и осуществляют непосредственное взаимодействие с приложением matlab.exe (размер этого файла равен 928 KB). Библиотека MATLAB Engine во время работы загружается в адресное пространство вашего приложения, не слишком утяжеляя его из-за своего небольшого размера. На этапе компиляции же требуется так называемая библиотека импорта libeng.lib. Как получить такую библиотеку, а также как реально осуществить компиляцию проекта для получения изолированного приложения MATLAB Engine, будет рассказано ниже в следующем параграфе настоящей главы.

Сейчас же просто перечислим основные функции из библиотеки MATLAB Engine:

1. engClose( Engine* ep );
2. engEvalString( Engine* ер, const char* string );
3. engGetArray( Engine* ep, const char* name );
4. engOpen( const char* startcmd );
5. engOutputBuffer( Engine* ep, char* p, int n );
6. engPutArray( Engine* ep, const mxArray* mp );
7. engOpenSingleUse(const char*, void*, int*);

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

Глядя на представленные прототипы функций, замечаем как уже известные нам внутренние типы данных системы MATLAB, такие как mxArray, так и новый тип данных Engine. Чтобы воспользоваться этим типом данных, нужно включать (директивой препроцессора include) в исходный код разрабатываемого проекта заголовочный файл engine.h.

Взаимодействие с системой MATLAB начинается вызовом функции engOpen, которая и возвращает правильный указатель на тип Engine:

Engine* pEng;
pEng = engOpen( NULL );

причем всегда нужно проверять возврат на NULL. Он означает, что вызов функции engOpen прошел неудачно (например, по причине невозможности запустить на выполнение приложение matlab.exe) и следует закрыть приложение, так как взаимодействовать с системой MATLAB по технологии MATLAB Engine уже невозможно.

Если же вызов функции engOpen прошел удачно, то есть получен ненулевой указатель, то после этого можно вызывать другие функции с префиксом eng, а также функции с префиксом mx, которые мы раньше активно использовали при разработке МЕХ-функций (смотри предыдущую главу). Однако теперь нельзя использовать функции с префиксом mex.

Когда работа с системой MATLAB заканчивается, нужно вызвать функцию engClose, чтобы осуществить корректное отсоединение и освободить занятые ресурсы:

engClose ( pEng );
В промежутке между вызовами функций engOpen и engClose можно вызывать остальные функции библиотеки MATLAB Engine, то есть функции engPutArray, engGetArray, engEvalString и engOutputBuffer (упомянутая выше функция engOpenSingleuse используется вместо функции engOpen).

Функция engPutArray помещает массив mxArray в рабочее пространство системы MATLAB, после чего над этим массивом можно производить вычисления непосредственно в этом рабочем пространстве при помощи функции engEvalString. Чтобы получить из рабочего пространства копию массива (например, результата вычислений) и разместить ее в адресном пространстве нашего приложения, нужно вызвать функцию engGetArray.

Наконец, функция engOutputBuffer позволяет задать буфер в памяти нашего процесса, куда будет поступать при работе функции engEvalstring поток сообщений, обычно поступающий в командное окно системы MATLAB.

Итак, мы очень кратко описали работу всех функций библиотеки MATLAB Engine. Теперь проиллюстрируем их работу на конкретных примерах. Мы ограничимся компактными учебными примерами, так что весь С-код всегда будет помещаться в единственный файл с именем main.с. Мы также будем создавать только приложения Win32 Console Application, причем делается это лишь для простоты. На самом деле абсолютно приемлемы и даже желательны приложения с полноценным графическим интерфейсом Windows типа Win32 Application.

Всего мы рассмотрим три примера. В первом из них мы в своем приложении сформируем матрицу, затем функцией engPutArray передадим ее в рабочее пространство системы MATLAB. Затем с помощью функции engEvalString вычислим в рабочем пространстве собственные значения этой матрицы и возвратим результаты в наше адресное пространство с помощью функции engGetArray. Вот текст первого примера:

#include
#include
#include
#include "engine.h"
double data[6] = {1,2,3,4,5,6};
int main( void )
{
Engine* pEng = NULL; mxArray* pArr = NULL; mxArray* pV = NULL;
double* pReal = NULL;

double* plmage = NULL;
int M, i;
// Start Engine session:

pEng = engOpen( NULL );
if( pEng == NULL )
{
printf( "\nCan't start Engine session!" );
return 1;
}
// Create real matrix
pArr = mxCreateDoubleMatrix( 3, 2, mxREAL ); mxSetName( pArr, "A" );
// Set its elements:
memcpy( mxGetPr( pArr ), data, 6*sizeof(double) );
// Put array into MATLAB environment // and calculate eigen values of it:
engPutArray( pEng, pArr );
engEvalString( pEng, "Vec = eig( A*A')" );
pV = engGetArray( pEng, "Vec" );
// Stop Engine session: engClose( pEng );
// Get double values from
// possibly complex mxArray v:
pReal = mxGetPr( pV );
pImage = mxGetPi( pV );
M = mxGetM( pV );
// Print results:
printf("Real parts of eig vector\n");
for( i=0; i {
printf("%lf\n", pReal[i] );
}
lf( pImage )
{
printf("\nImage parts of eig vector\n");
for( i=0; i {
printf("%lf\n", plmage[i] );
}
}

// Free mxArray memory buffers:
mxDestroyArray( pArr );
mxDestroyArray( pV ); // Screen delay:
printf( "Press any key to exit");
getch();
return 0;
}



Перейти к Части 3 >>

Другие материалы по теме
Категория: Программирование | Добавил: rusoft (2006-11-12) | Автор: Н.Н. Мартынова
Просмотров: 910 | Рейтинг: 0.0/0 |
Всего комментариев: 1
Имя *:
Email:
Код *:
Пятница, 2010-07-30, 19:23:53
Приветствую Вас Гость
Наш опрос
Как вам новый дизайн?
Всего ответов: 325
Мини-чат
200
Друзья сайта
  • Создать сайт
  • Все для веб-мастера
  • Программы для всех
  • Мир развлечений
  • Лучшие сайты Рунета
  • Кулинарные рецепты
  • Статистика

    Онлайн всего: 2
    Гостей: 2
    Пользователей: 0