Блог им. Kot_Begemot

Интеграция MatLab Engine и С++ (1)

В сложных вычислительных задачах (или просто при нежелании программировать на Lua, Cpp и т.д., а пользоваться более высокоуровневыми инструментами разработки), незаменимым оказывается API интерфейс Матлаба реализованный в качестве Active-X COM Automation Server.  Для его реализации на языке Си существует специальная библиотека libeng.lib, позволяющая языкам Си, С++, Фортран обмениваться данными и пользоваться всеми ресурсами Матлаба (обычно это обработка видео, автопилоты, ИИ, нейронные сети и т.п.).


Поэтому, в качестве изучения возможностей, попробуем реализовать простейший проект обмена данными и вызова функций Матлаб со стороны Си++ при использовании CodeBlocks и MinGW64.



  • Запуск интерфейса Матлаб

Чтобы адресовать все внешние процессы к единому процессу Матлаб, а не запускать Engine для каждого процесса в отдельности, 
запустим «двигатель» матлаба внутренней командой :

server=actxserver('matlab.application.single'); server.Execute(' enableservice (''AutomationServer'', true)');

 В этом случае используемые в Cи++ функции engOpen() будут получать указатель на уже существующий интерфейс, а не открывать новый.


  • Подключение необходимых библиотек и получение указателя интерфейса
Необходимый минимум :

1. Библиотека libeng.lib, отвечающая за управление интерфейсом Matlab   (matlabroot)/extern/lib/win64/microsoft  
2. Библиотека libmx.lib, отвечающая за конвертацию данных Matlab — Cpp   (matlabroot)/extern/lib/win64/microsoft
3. Файл заголовок engine.h, описывающий доступные пользователю функции.  (matlabroot)/extern/include/win64/microsoft


Указатель интерфейса получается вызовом функции ep=engOpen(NULL), с единственно допустимым параметром для Win OS — NULL.


#include <windows.h>
#include <stdlib.h>
#include <stdio.h>
#include <conio.h>
#include <string.h>
#include "engine.h"


int PASCAL WinMain (HINSTANCE hInstance,
                    HINSTANCE hPrevInstance,
                    LPSTR     lpszCmdLine,
                    int       nCmdShow)
{

   // ПОДКЛЮЧЕНИЕ К СЕРВЕРУ МАТЛАБ(!)

    printf("%s","Connect to exist Matlab Engine ...");
        Engine *ep;      // Start the MATLAB engine
        if (!(ep = engOpen(NULL))){
                MessageBox ((HWND)NULL, (LPSTR)"Can't start MATLAB engine",
                        (LPSTR) "Engwindemo.c", MB_OK);
                exit(-1);
        }
    printf("%s"," Ok. \n");
    printf("%s","\n");


  • Передача в Матлаб массива типа Double float,2p

// ЗАПИСЬ ДВУМЕРНОГО МАССИВА типа DOUBLE
    int row=2,col=3;
    double DARR[row][col]={0.1,0.2,0.3,0.4,0.5,0.6}; // Массив СИ++
    mxArray *OutArr=mxCreateDoubleMatrix(2,3,mxREAL); // Создаем массив OUT
    memcpy((char *) mxGetPr(OutArr), (char *) DARR, row*col*sizeof(double)); // Копируем массив DARR в OUT

    engPutVariable(ep, "DoubleArrayFromCpp", OutArr); // Отправляем массив в матлаб
    printf("%s","Double Array Send to Matlab Engine \n");
    printf("%s","\n");
    printf("%s","\n");
    mxDestroyArray(OutArr); //очищаем массив
    getch();


Для того чтобы передать заданный массив размером 2х3 в Матлаб нам необходимо сначала создать некоторый матлаб-совместимый объект mxArray, указатель на который возвращает функция: 

*mxCreateDoubleMatrix( число строк, число столбцов, флаг комплексного числа)


После чего, созданный объект заполняется при помощи функции:

memcpy( память назначения (первый элемент mxArray), память источник (массив Cи++), объем копирования памяти в байтах) 


Полученный таким образом массив передается в Матлаб по стандартной процедуре:

engPutVariable( указатель процесса Матлаб, имя переменной в процессе Матлаб, передаваемая переменная из Си++) 



  • Чтение ранее переданного Double float,2p массива из Матлаб
 // Чтение ДВУМЕРНОГО МАССИВА типа DOUBLE
    mxArray *InDArr=engGetVariable(ep, "DoubleArrayFromCpp"); // Получаем указатель на считанный массив
    int rowi= mxGetM(InDArr); // Считаем число строк
    int coli=mxGetN(InDArr); // Считаем число столбцов
    double InArr[rowi][coli]; //Создаём клон массива в Си++
    memcpy(InArr, mxGetPr(InDArr) ,rowi*coli*sizeof(double) ); // Заполняем клон-массив

    printf(" Double Array Loaded from Matlab, num rows %d", rowi);
    printf("\n Double Array Loaded from Matlab, num columns %d", coli);
    printf("%s","\n");
    printf("\n Double Array Loaded from Matlab, first element %f", InArr[0][0]);
    printf("\n Double Array Loaded from Matlab, last element %f", InArr[rowi-1][coli-1]);
    printf("%s","\n");
    printf("%s","\n");
    mxDestroyArray(InDArr); //очищаем массив
    getch();

Чтение происходит аналогично, но в обратной последовательности.

  • Исполнение заданной Си++ функции  в среде Матлаб
    // ВЫЗОВ ФУНКЦИИ в MATЛАБ ( и, одновременно, создание 2x2 Cell массива)
    char func[100] = "CellArray={'Smart-Lab','Kot-Begemot';'Blog','Cpp-MatlabEngine'}";
    engEvalString(ep,func);

Функция для исполнения Матлаб задается строкой (массив char) и в данном случае представляет собой создание и заполнение 2х2 массива ячеек (Cell Array) строковыми переменными.

После того, как функция задана строкой, она исполняется командой:

engEvalString(указатель процесса Матлаб, команда матлаб заданная строкой)

  • Чтение массива ячеек из матлаб в цикле

   // Чтения массива строк (Cell Array)
    mxArray *InCArr=engGetVariable(ep, "CellArray"); // Получаем указатель на считанный массив
    int rowc= mxGetM(InCArr); // Считаем число строк
    int colc=mxGetN(InCArr); // Считаем число столбцов
    printf("\n Cell Array Loaded from Matlab, num rows %d", rowc);
    printf("\n Cell Array Loaded from Matlab, num columns %d", colc);
    printf("%s","\n");

    char *CArr[rowc][colc];
    int i,j;
    for (i=0;i<rowc;i++)
    {
        for (j=0;j<colc;j++)
        {
         CArr[i][j]=mxArrayToString(mxGetCell(InCArr,i*colc+j));
         printf("\n Cell Array Loaded from Matlab element %d %s\n", i*colc+j, CArr[i][j]  );
        }
    }
    mxDestroyArray(InCArr); //очищаем массив
    getch();

Чтение массива ячеек аналогично чтению массива double за исключением того, что читать нам его приходится поэлементно в цикле, получая строки из ячеек при помощи функции :

строка из ячейки = mxArrayToString( указатель ячейки)

и

указатель ячейки = mxGetCell( указатель массива, указатель порядкового номера ячейки от 0 до последнего элемента массива)


* В ячейке Cell Array может содержаться не только строка, но и многомерный массив Double, и даже другой Cell Array, в ячейках которого содержаться иные массивы. В данном случае, предполагается использование только простых массивов строк.


  • Неиспользуемые функции 

int engOutputBuffer(Engine *ep, char *buffer, int buflen); — захват выходного буфера процесса Матлаб 

int engSetVisible(Engine *ep, bool newVal); — изменение видимости окна процесса Матлаб 

int engClose(Engine *ep); — закрытие указанного процесса Матлаб






Результаты работы программы:

Интеграция MatLab Engine и С++ (1)

Интеграция MatLab Engine и С++ (1)



Предложения и критика приветствуются.
Торгуйте алгоритмами.
С уважением, Кот-Бегемот.


★12
37 комментариев
можно какой-то минимальный use case для чего понадобятся следующие процедуры?
avatar
meat, на сколько фантазии хватит. Я NN и Image Processing не занимаюсь уже много лет, поэтому мне для коннектора Matlab-Lua нужно, чтобы к 8х Квик можно было подключаться и не зависеть от Git-прогеров, которые сегодня делают, а завтра не обновляют.
avatar
Kot_Begemot, тебе в real-time нужно что-то вычислять или просто так данными манипулировать?
avatar
meat, ну как… 2000 страйков раз в минуту по 5-7 сериям и это только Si. Много, да )))

Но мне для внешнего управления больше нужно, чем для скорости.  
avatar
Kot_Begemot, это немного

можешь как-то более подробно описать свою задачу, где ты увидел решение в виде вызова функций matlab? или этот пост никак не связан с твоей задачей?
avatar
Python forever!
Матлаб, я так понимаю, уже на фиг никому не нужен.
Сейчас готовлю материал по обмену Lua c Python. Хотя я о такой простой возможности обмена Луа со всем и вся уже писал, народ не внемлет.) Попробую еще раз.)
avatar
3Qu, 

Хотя я о такой простой возможности обмена Луа со всем и вся уже писал

имеется ввиду dll на Cpp?
avatar
Kot_Begemot, нет. Для людей не знающих С++ и пр.
Обычный обмен файлами CSV или JSON. Эт по желанию. Простенько, со вкусом и простейшими средствами. Сам тоже иногда применял.
Хочу показать, что скорости вполне и для всего хватает. Но людям ведь нужен реактивный самолет, им каждая мс дорога.))
avatar
3Qu, самолёт-не самолёт, а на нём лететь приятней. Тем более когда разработка строится часто приходится грузить данные в режиме нажал/загрузил. 

Но CSV, конечно, хватит даже далеко не среднему юзеру. 
avatar
Kot_Begemot, ну, и с файлами также: нажал — загрузил. Разницы нет.
Скорость там минимум ~1МБ/с и больше. Куда больше то? Кто чего не успеет?
Подождите неск дней. На днях тест программу сделаю, чтобы конкретные цифры были.
avatar
3Qu, руками… руками.
avatar
Kot_Begemot, не понял, что руками?
avatar
3Qu, файлы руками. Там мобильности — ноль без палочки. По прямой намного приятней и возможностей больше. И это без учёта скорости, конечно. Файлы вяжет формат, количество, разграничение доступа, отсутствие или как минимум качество обратной связи (что писать, куда писать, когда писать) и пр. 

А у меня, можно считать, прямой доступ. Это удобно, красиво, надёжно, без доп нагрузки на ХДД и еще +100500 плюсов. 
avatar
Kot_Begemot, везде формат. В сокетах формат, в MMF -формат, в файлах — формат. В любом межпрограмном обмене формат. Хотя бы преобразование типов везде есть, и матлабе тоже.
С JSON работайте, и все преобразования на автомате будут. Ну, почти.)
avatar
3Qu, да, но файлы вообще форматом повязаны. Нужно целую библиотеку держать и помнить что куда сохранялось, когда и зачем. А в режиме вопрос-ответ работа иначе строится, по ситуации.
avatar
Kot_Begemot, вы ошибаетесь. Все в принципе одинаково при любом обмене. Скажем, даже при вызове функции, вы уже должны помнить где какая переменная, что она означает и ее тип.
avatar
3Qu, да, но через файлы это сложнее — помнить там нужно больше, да ещё и двигать каретку постоянно.  Вы же не пишете в файлы, скажем, локальные переменные функций? Хотя можно было бы...  и среднему юзеру этого было бы достаточно… ну и т.д.
avatar
Kot_Begemot, эт такие мелочи. Для обмена файлами квалификацая вообще не нужна. А функции есть в любом языке. Да и не надо нам функциями обмениваться. А вызов функции делается аналогично как и везде.
Ладно, напишу, сами увидите. Но главное — простота реализации и скорость достаточная для всего.
Безусловно, программистам это не интересно.) Они и сами все умеют.
avatar
ну и накой надо? если можно напрямую прогить? кинь билиотеки свои лучше!
avatar
Код конечно хорошо, но можно огласить весь список программ, которые используются.
Матлаб, Борланд?(С++) что то еще. Слышал можно тупа купить виртуальную машину, где всё уже будет установлено. Так то схема понятна, есть данные(в чем то) фундаментальные, технические, цены. С ними как то работаем(исследуем) с помощью АPI функций Матлаба, на базе С++(программы). 

Список программ?
avatar
Jkrsss, нет, больше ничего — Матлаб, CodeBlocks (Cpp) и всё.  
avatar
Сперва пишешь про нежелающих кодить на С++, потом описываешь работу с Матлабом на С++  

avatar
хоть чё нить заработал матлабом на маркете?
avatar
Kot_Begemot, ты не ответил на вопрос.
avatar
а на чем писать прогу, чтобы было скорострельнее всего? Хочу небольшие программки писать, но не знаю, на чем. Писал на Visual Basic + Excel, но скорость меня не устраивает
Иван Смирнов, смотря под что пишете и смотря какой сложности. Здесь слишком много тонкостей разных. Я даже не рискну вам ответить. 
avatar
Спасибо. Интересно.
Я такую же штуку делал на С диез.

А тем кто спрашивает зачем все это?

Можно ответить, что в результате мы получаем:
Полнофункциональный язык программирования + мощный MathProcessor
с большим кол-вом toolbox-ов, например:
CurveFitting,
Econometric,
FilterDesign,
Financial,
Fuzzy,
ImageProccess,
Neural,
Statistics
etc

+ Прекрасная документация

Разве этого мало?
avatar
_sg_, да я тоже не понимаю этих вопросов… Но каждому своё, как говорится)
avatar
Привет. Готовых, с открытым кодом библиотек для решения любых задач, связанных с рынком, и не очень, на несколько порядков больше в открытом доступе, чем функций в матлабе. Интеграция этих библиотек в свой код на с++, и тем более на пайтон намного проще, чем все эти пляски с бубенцами вокруг матлаба
avatar
Andrew Morozov, не ради критики, а ради интереса осмелюсь спросить.
А Вы сами какую архитектуру в своих разработках для торговли используете? И на каких языках написаны функциональные модули в Вашей системе?
Вот автор использует С++ и Matlab. Я считаю это неплохой вариант. Не идеальный, конечно, но вполне рабочий.
А что используете Вы? Поделитесь, пожалуйста.
avatar
Я использую специализированный торговый софт и с++. Не потому что считаю, что это единственный правильный выбор, а потому что мне так лично удобно. Ничего против матлаба не имею, как инструмент для быстрого построения прототипов и анализа достойный, речь идёт о комбинации матлаба и с++, или python и с++. Логично, и это лучше работает, когда код на языке более высокого уровня вызывает код более низкого уровня, а не наоборот, как предлагает автор. То есть вызов плюсовых библиотек из питона это правильно и быстро. Другое дело, если нет достаточных знаний с++, легче простог в самом матлабе работать, хотя на мой взгляд, Мультичартс намного удобнее. Моя платформа Sierrachart.
avatar
Andrew Morozov,
Спасибо за Ваш ответ. Интересно.
Теперь понятен Ваш подход. Похож на классический и правильный, но несколько несовременный на мой взгляд.
В настоящее время в результате эволюции софтверных архитектур фокусировка на конкретных языках исчезает.
В настоящее время Приложения среднего уровня и, тем более приложения уровня Enterprise, проектируются в виде множества микро-сервисов с «шиной данными» между ними.
В результате для реализации конкретного микро-сервиса разработчик выбирает наиболее подходящий для этого язык программирования и реализуют его наиболее оптимальным образом.
Взаимодействие между сервисами происходит по «шине данных».
Конкретный сервис инициализируется и подписывается на получение необходимых данных через Контроллер шины данных. То есть, сказал в коде new, а потом subscribe. И все. Данные прилетают сами. Используются Push-технологии. И никто уже не думает, что из чего вызывать и не циклится на этом.
Причем такая архитектура одинаково хорошо работает и для Intranet-сетей и для Internet, ну а для Desktop тем более.
Даже Moex, к моему большому удивлению, начала иcпользовать Push-технологии.
А дискуссии что из чего вызывать уже не актуальны.
avatar

теги блога Kot_Begemot

....все тэги



UPDONW
Новый дизайн