Kot_Begemot
Kot_Begemot личный блог
26 марта 2020, 01:10

Интеграция 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)



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


37 Комментариев
  • meat
    26 марта 2020, 01:23
    можно какой-то минимальный use case для чего понадобятся следующие процедуры?
      • meat
        26 марта 2020, 02:09
        Kot_Begemot, тебе в real-time нужно что-то вычислять или просто так данными манипулировать?
          • meat
            26 марта 2020, 02:29
            Kot_Begemot, это немного

            можешь как-то более подробно описать свою задачу, где ты увидел решение в виде вызова функций matlab? или этот пост никак не связан с твоей задачей?
  • 3Qu
    26 марта 2020, 01:30
    Python forever!
    Матлаб, я так понимаю, уже на фиг никому не нужен.
    Сейчас готовлю материал по обмену Lua c Python. Хотя я о такой простой возможности обмена Луа со всем и вся уже писал, народ не внемлет.) Попробую еще раз.)
      • 3Qu
        26 марта 2020, 02:10
        Kot_Begemot, нет. Для людей не знающих С++ и пр.
        Обычный обмен файлами CSV или JSON. Эт по желанию. Простенько, со вкусом и простейшими средствами. Сам тоже иногда применял.
        Хочу показать, что скорости вполне и для всего хватает. Но людям ведь нужен реактивный самолет, им каждая мс дорога.))
          • 3Qu
            26 марта 2020, 02:20
            Kot_Begemot, ну, и с файлами также: нажал — загрузил. Разницы нет.
            Скорость там минимум ~1МБ/с и больше. Куда больше то? Кто чего не успеет?
            Подождите неск дней. На днях тест программу сделаю, чтобы конкретные цифры были.
              • 3Qu
                26 марта 2020, 02:28
                Kot_Begemot, не понял, что руками?
                  • 3Qu
                    26 марта 2020, 03:01
                    Kot_Begemot, везде формат. В сокетах формат, в MMF -формат, в файлах — формат. В любом межпрограмном обмене формат. Хотя бы преобразование типов везде есть, и матлабе тоже.
                    С JSON работайте, и все преобразования на автомате будут. Ну, почти.)
                      • 3Qu
                        26 марта 2020, 03:07
                        Kot_Begemot, вы ошибаетесь. Все в принципе одинаково при любом обмене. Скажем, даже при вызове функции, вы уже должны помнить где какая переменная, что она означает и ее тип.
                          • 3Qu
                            26 марта 2020, 03:18
                            Kot_Begemot, эт такие мелочи. Для обмена файлами квалификацая вообще не нужна. А функции есть в любом языке. Да и не надо нам функциями обмениваться. А вызов функции делается аналогично как и везде.
                            Ладно, напишу, сами увидите. Но главное — простота реализации и скорость достаточная для всего.
                            Безусловно, программистам это не интересно.) Они и сами все умеют.
  • GrayFox
    26 марта 2020, 02:44
    ну и накой надо? если можно напрямую прогить? кинь билиотеки свои лучше!
  • Jkrsss
    26 марта 2020, 04:51
    Код конечно хорошо, но можно огласить весь список программ, которые используются.
    Матлаб, Борланд?(С++) что то еще. Слышал можно тупа купить виртуальную машину, где всё уже будет установлено. Так то схема понятна, есть данные(в чем то) фундаментальные, технические, цены. С ними как то работаем(исследуем) с помощью АPI функций Матлаба, на базе С++(программы). 

    Список программ?
  • deke
    26 марта 2020, 08:33
    Сперва пишешь про нежелающих кодить на С++, потом описываешь работу с Матлабом на С++  

  • Kapeks
    26 марта 2020, 13:49
    хоть чё нить заработал матлабом на маркете?
  • Иван Смирнов
    26 марта 2020, 23:15
    а на чем писать прогу, чтобы было скорострельнее всего? Хочу небольшие программки писать, но не знаю, на чем. Писал на Visual Basic + Excel, но скорость меня не устраивает
  • _sg_
    26 марта 2020, 23:47
    Спасибо. Интересно.
    Я такую же штуку делал на С диез.

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

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

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

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

Активные форумы
Что сейчас обсуждают

Старый дизайн
Старый
дизайн