Как использовать библиотеку Boost 1.55.0 для создания разделяемой памяти с помощью приложения matlab - PullRequest
0 голосов
/ 29 октября 2018

Я пытаюсь использовать библиотеку Boost 1.55.0 для создания общей памяти. У меня есть файл example.cpp, который создает общую память Boost. Мексинг этого файла успешен, но выдает следующее исключение при отладке «MATLAB.exe вызвал точку останова». Это исключение из-за несовместимости версии надстройки с версией matlab? Как решить эту проблему
`/ * Файл: sfun_counter_cpp.cpp * Аннотация:
*
* Пример C ++ S-функции, которая хранит объект C ++ в
* указатели на вектор PWork.
*
* Авторские права 1990-2000 The MathWorks, Inc.
*

* /

#include "iostream"   
#include <boost/interprocess/managed_shared_memory.hpp> 
#include <boost/interprocess/containers/vector.hpp>  
#include <boost/interprocess/allocators/allocator.hpp>

typedef struct
{
 int           outGate;
 unsigned int  outPin;
 int           inGate;
 unsigned int  inPin;   
} wire;

typedef struct
{
unsigned int  gateType;
unsigned int  inPins;
unsigned int  outPins;
std::vector<wire>  inWires;
std::vector<wire>  outWires;
} gate;

std::vector<gate> gates;
wire wiredata;
gate gatedata;

class  counter {
    double  x;
public:
    counter() {
        x = 0.0;
    }
    double output(void) {
        x = x + 1.0;
        return x; 
    }
};

#ifdef __cplusplus
extern "C" { // use the C fcn-call standard for all functions  
#endif       // defined within this scope                     

#define S_FUNCTION_LEVEL 2
#define S_FUNCTION_NAME  sfun_counter_cpp

/*
* Need to include simstruc.h for the definition of the SimStruct and
* its associated macro definitions.
*/
#include "simstruc.h"

/*====================*
* S-function methods *
*====================*/

/* Function: mdlInitializeSizes ===============================================
* Abstract:
*    The sizes information is used by Simulink to determine the S-function
*    block's characteristics (number of inputs, outputs, states, etc.).
*/
static void mdlInitializeSizes(SimStruct *S)
{
    /* See sfuntmpl_doc.c for more details on the macros below */

    ssSetNumSFcnParams(S, 1);  /* Number of expected parameters */
    if (ssGetNumSFcnParams(S) != ssGetSFcnParamsCount(S)) {
        /* Return if number of expected != number of actual parameters */
        return;
    }

    ssSetNumContStates(S, 0);
    ssSetNumDiscStates(S, 0);

    if (!ssSetNumInputPorts(S, 0)) return;

    if (!ssSetNumOutputPorts(S, 1)) return;
    ssSetOutputPortWidth(S, 0, 1);

    ssSetNumSampleTimes(S, 1);
    ssSetNumRWork(S, 0);
    ssSetNumIWork(S, 0);
    ssSetNumPWork(S, 1); // reserve element in the pointers vector
    ssSetNumModes(S, 0); // to store a C++ object
    ssSetNumNonsampledZCs(S, 0);

    ssSetOptions(S, 0);
}

/* Function: mdlInitializeSampleTimes 
=========================================
* Abstract:
*    This function is used to specify the sample time(s) for your
*    S-function. You must register the same number of sample times as
*    specified in ssSetNumSampleTimes.
*/
static void mdlInitializeSampleTimes(SimStruct *S)
{
    ssSetSampleTime(S, 0, mxGetScalar(ssGetSFcnParam(S, 0)));
    ssSetOffsetTime(S, 0, 0.0);

}

#define MDL_START  /* Change to #undef to remove function */
#if defined(MDL_START) 
/* Function: mdlStart 
=======================================================
* Abstract:
*    This function is called once at start of model execution. If you
*    have states that should be initialized once, this is the place
*    to do it.
*/
static void mdlStart(SimStruct *S)
{
    ssGetPWork(S)[0] = (void *) new counter; // store new C++ object in 
the
}                                            // pointers vector
#endif /*  MDL_START */

/* Function: mdlOutputs =======================================================
* Abstract:
*    In this function, you compute the outputs of your S-function
*    block. Generally outputs are placed in the output vector, ssGetY(S).
*/
static void mdlOutputs(SimStruct *S, int_T tid)
{

    using namespace boost::interprocess;
    counter *c = (counter *) ssGetPWork(S)[0];   // retrieve C++ object from
    shared_memory_object::remove("MySharedMemory");
    //create the shared memory 
    managed_shared_memory segment(create_only, "MySharedMemory", 65536); 

    //create the allocators for the struct elements to be accessed as 
vectors
    typedef allocator<gate, 
managed_shared_memory::segment_manager>gate_alloc;
    typedef allocator<wire, 
managed_shared_memory::segment_manager>inwire_alloc;
    typedef allocator<wire, 
managed_shared_memory::segment_manager>outwire_alloc;

    //create a boost vector with an associated allocator to it
    typedef vector<gate, gate_alloc>gate_vec;
    typedef vector<wire, inwire_alloc>inwire_vec;
    typedef vector<wire, outwire_alloc>outwire_vec;

    //Initialize shared memory STL-compatible allocator
    const gate_alloc alloc_inst(segment.get_segment_manager());
    const inwire_alloc alloc_inst1(segment.get_segment_manager());
    const outwire_alloc alloc_inst2(segment.get_segment_manager());

    //construct the segment for pushing the data into it
    gate_vec *gate_data = segment.construct<gate_vec>("gatedata") 
(alloc_inst);
    inwire_vec *inwire_data = segment.construct<inwire_vec>("inwiredata")  
        (alloc_inst1);
    outwire_vec *outwire_data = segment.construct<outwire_vec> 
("outwiredata") 
        (alloc_inst2);


    //push the data into the vectors
    wiredata.inGate = 10;
    wiredata.inPin = 2;
    wiredata.outGate = 1;
    wiredata.outPin = 3;
    inwire_data->push_back(wiredata);
    outwire_data->push_back(wiredata);

    gatedata.gateType = 1;
    gatedata.inPins = 2;
    gatedata.outPins = 3;
    gate_data->push_back(gatedata);


    real_T  *y = ssGetOutputPortRealSignal(S,0); // the pointers vector 
and use
    y[0] = c->output();                          // member functions of 
the
}                                                // object

/* Function: mdlTerminate 
=====================================================
* Abstract:
*    In this function, you should perform any actions that are necessary
*    at the termination of a simulation.  For example, if memory was
*    allocated in mdlStart, this is the place to free it.
*/
static void mdlTerminate(SimStruct *S)
{
    counter *c = (counter *) ssGetPWork(S)[0]; // retrieve and destroy C++
    delete c;                                  // object in the 
termination
}                                              // function
/*======================================================*
* See sfuntmpl_doc.c for the optional S-function methods *
*======================================================*/

/*=============================*
* Required S-function trailer *
*=============================*/

#ifdef  MATLAB_MEX_FILE    /* Is this file being compiled as a MEX-file?*/
#include "simulink.c"      /* MEX-file interface mechanism */
#else
#include "cg_sfun.h"       /* Code generation registration function */
#endif

#ifdef __cplusplus
} // end of extern "C" scope
#endif

` это функция, которую я должен mex, отладить и запустить. Несмотря на то, что mexing вышеупомянутого фрагмента кода успешен, он выдает исключение "MATLAB.exe вызвал точку останова" во время отладки.

1 Ответ

0 голосов
/ 29 октября 2018

Я могу привести пример. К сожалению, я не могу проверить это с Windows, но я проверил это в системе UNIX. Основная идея такая же. В этом случае это разделяемая память из внешнего двоичного файла в mex-функцию Matlab.

Внешний двоичный файл:

#include <cstring>
#include <string>
#include <cstdlib>
#include <iostream>
#include <boost/interprocess/shared_memory_object.hpp>
#include <boost/interprocess/mapped_region.hpp>

using namespace boost::interprocess;

const std::string payload("SHARED MEMORY CONTENT");

int main(void) {
  shared_memory_object shm(open_or_create, "memory4mat" ,read_write);
  shm.truncate(payload.size());
  mapped_region mem(shm, read_write);
  std::memcpy(mem.get_address(), payload.c_str(), mem.get_size());

  do {
    std::cout << '\n' << "Press a key to continue...";
  } while (std::cin.get() != '\n');

  shared_memory_object::remove("memory4mat");

  return 0;
}

, в то время как функция mex:

#include "mex.hpp"
#include "mexAdapter.hpp"
#include "MatlabDataArray.hpp"
#include <string>
#include <cstdlib>
#include "boost/interprocess/shared_memory_object.hpp"
#include "boost/interprocess/mapped_region.hpp"

using namespace boost::interprocess;

class MexFunction : public matlab::mex::Function {
public:
    void operator()(matlab::mex::ArgumentList outputs, matlab::mex::ArgumentList inputs) {
      matlab::data::ArrayFactory factory;

      shared_memory_object shm(open_only, "memory4mat", read_only);
      mapped_region mem(shm, read_only);

      std::string payload(static_cast<const char *>(mem.get_address()), mem.get_size());
      outputs[0] = factory.createCharArray(payload);
      outputs[1] = factory.createScalar<int16_t>(mem.get_size());
    }
};

он использует C ++ интерфейс и API данных для Matlab . Чтобы скомпилировать два примера, вам нужно добавить каталог включения Boost в качестве параметров компилятора (общая память - это функция заголовка только в boost).

Внешний двоичный файл создает общую память , которая содержит строку "SHARED MEMORY CONTENT" и ожидает ввода от пользователя, чтобы удалить объект общей памяти.

mex-файлы открывают разделяемую память, если она существует (если разделяемая память не существует, об ошибке сообщается и обрабатывается в Matlab, что является одной из причин, почему я предпочитаю C ++ api), и копирую ее содержимое в массив символов Matlab , Функция возвращает два значения: первое - это содержимое разделяемой памяти, второе - длина разделяемой памяти (маппер использует всю память, установленную с помощью усечения).

Этот простой пример использует только основные функции и должен работать в системах Unix и Windows, но, опять же, я не могу тестировать на win.

Более полный пример

Давайте попробуем с более полным примером общей памяти и файлов Matlab Mex. Давайте напишем очень простой внешний двоичный файл, который позволяет нам создавать / удалять / читать / записывать общую память. Этот двоичный файл содержит много вещей, жестко закодированных для простоты, таких как имя файла памяти ("shmem"):

// File: share_server.cpp
// g++ share_server.cpp -o share_server
#include <cstring>
#include <string>
#include <cstdlib>
#include <iostream>
#include <boost/interprocess/shared_memory_object.hpp>
#include <boost/interprocess/mapped_region.hpp>

using namespace boost::interprocess;

static const std::size_t size = 20;
static const std::size_t wsize = 15;
static const char name[6] = "shmem";
static const char input[wsize] = "write in share";
char output[size];

inline void printHelp() {
  std::cout << "Options:" << std::endl;
  std::cout << "  n) open a new 'shmem' memory" << std::endl;
  std::cout << "  d) delete a 'shmem' memory" << std::endl;
  std::cout << "  r) read from 'shmem' memory" << std::endl;
  std::cout << "  w) write to 'shmem' memory" << std::endl;
  std::cout << "  x) Exit" << std::endl;
}

inline void cmd_createShare() {
  try {
    shared_memory_object sm(create_only, name, read_write);
    sm.truncate(size);
    std::cout << "Shared object created" << std::endl;
  } catch(std::exception & e) {
    std::cout << "Create Error :: " << e.what() << std::endl; 
  }
}

inline void cmd_deleteShare() {
  try {
    shared_memory_object::remove(name);
    std::cout << "Shared object deletetd" << std::endl;
  } catch(std::exception & e) {
    std::cout << "Delete Error:: " << e.what() << std::endl; 
  }
}

inline void cmd_readShare() {
  try {
    shared_memory_object sm(open_only,  name, read_only);
    mapped_region sh_mem(sm, read_only);
    std::string ret(static_cast<const char *>(sh_mem.get_address()), sh_mem.get_size());
    std::cout << ret << std::endl;
  } catch(std::exception & e) {
    std::cout << "Read Error:: " << e.what() << std::endl; 
  }
}

inline void cmd_writeShare() {
  try {
    shared_memory_object sm(open_only,  name, read_write);
    mapped_region sh_mem(sm, read_write);
    std::memcpy(sh_mem.get_address(), input, wsize);
    std::cout << "Write completed" << std::endl;
  } catch(std::exception & e) {
    std::cout << "Read Error:: " << e.what() << std::endl; 
  }
}

мы можем написать 3 mex файла (используя API C ++) для взаимодействия с разделяемой памятью. Первый, самый простой, считывает содержимое разделяемой памяти в виде строки и возвращает его в рабочую область Matlab. Интерфейс в синтаксисе Matlab выглядит примерно так:

function [value, read_size] = read_share(share_name)
  ...
end

и реализация C ++ выглядит следующим образом:

// File: read_share.cpp
#include "mex.hpp"
#include "mexAdapter.hpp"
#include "MatlabDataArray.hpp"
#include <string>
#include <cstring>
#include <cstdlib>
#include <vector>
#include <exception>

#include "boost/interprocess/shared_memory_object.hpp"
#include "boost/interprocess/mapped_region.hpp"

using namespace boost::interprocess;
using namespace matlab::data;

class MexFunction : public matlab::mex::Function {
private:
  std::shared_ptr<matlab::engine::MATLABEngine> engine;
  ArrayFactory factory;

  void throwError(std::string errorMessage) {
    engine->feval(matlab::engine::convertUTF8StringToUTF16String("error"),
            0, std::vector<Array>({ factory.createScalar(errorMessage) }));
  }

  uint64_t read_shared_memory(const std::string & name, std::string & ret_value) {
    try {
      shared_memory_object sm(open_only,  name.c_str(), read_only);
      mapped_region sh_mem(sm, read_only);
      ret_value += std::string(static_cast<const char *>(sh_mem.get_address()), sh_mem.get_size());
      return ret_value.size();
    } catch(std::exception & e) {
      throwError(std::string("Reading error: ") + std::string(e.what()));
    }
    return 0;
  }

  void checkArguments(matlab::mex::ArgumentList inputs, matlab::mex::ArgumentList outputs) {
    if (inputs.size() != 1)
      throwError("Input must be of size 1");
    if (inputs[0].getType() != ArrayType::CHAR)
      throwError("First element must be a matlab char array");
    if (outputs.size() > 2)
      throwError("Too many outputs (required 1)");
  }

public:
  MexFunction() { 
    engine = getEngine();
  }

  void operator()(matlab::mex::ArgumentList outputs, matlab::mex::ArgumentList inputs) {  
    checkArguments(inputs, outputs);
    const CharArray name_array = std::move(inputs[0]);
    std::string name = name_array.toAscii();
    std::string ret_string("");
    uint64_t ret_size = read_shared_memory(name, ret_string);
    outputs[0] = factory.createScalar(ret_string);
    outputs[1] = factory.createScalar<uint64_t>(ret_size);
  }  
};

Второй mex-файл - операция записи. Требуется два ввода: имя разделяемой памяти и строка для записи в память. Мекс проверяет максимальный размер разделяемой памяти и сохраняет не более доступного пространства. Функция возвращает байты, записанные в интерфейсе для функции записи, что-то вроде:

function written_size = write_share(share_name, string)
  ...
end

и реализация:

// File: write_share.cpp
#include "mex.hpp"
#include "mexAdapter.hpp"
#include "MatlabDataArray.hpp"
#include <string>
#include <algorithm>
#include <cstring>
#include <cstdlib>
#include <exception>

#include "boost/interprocess/shared_memory_object.hpp"
#include "boost/interprocess/mapped_region.hpp"

using namespace boost::interprocess;
using namespace matlab::data;

class MexFunction : public matlab::mex::Function {
private:
  std::shared_ptr<matlab::engine::MATLABEngine> engine;
  ArrayFactory factory;

  void throwError(std::string errorMessage) {
    engine->feval(matlab::engine::convertUTF8StringToUTF16String("error"),
            0, std::vector<Array>({ factory.createScalar(errorMessage) }));
  }

  uint64_t write_shared_memory(const std::string & name, const std::string & value) {
    try {
      shared_memory_object sm(open_only,  name.c_str(), read_write);
      mapped_region sh_mem(sm, read_write);
      uint64_t size = std::min(value.size(), sh_mem.get_size());
      std::memcpy(sh_mem.get_address(), value.c_str(), size);
      return size;
    } catch(std::exception & e) {
      throwError(std::string("Reading error: ") + std::string(e.what()));
    }
    return 0;
  }

  void checkArguments(matlab::mex::ArgumentList inputs, matlab::mex::ArgumentList outputs) {
    if (inputs.size() != 2)
      throwError("Input must be of size 2");
    if (inputs[0].getType() != ArrayType::CHAR)
      throwError("First element must be a matlab char array");
    if (inputs[1].getType() != ArrayType::CHAR)
      throwError("Second element must be a matlab char array to save");
    if (outputs.size() > 1)
      throwError("Too many outputs (required 1)");
  }

public:
  MexFunction() { 
    engine = getEngine();
  }

  void operator()(matlab::mex::ArgumentList outputs, matlab::mex::ArgumentList inputs) {  
    checkArguments(inputs, outputs);
    const CharArray name_array = std::move(inputs[0]);
    std::string name = name_array.toAscii();
    const CharArray value_array = std::move(inputs[1]);
    std::string value = value_array.toAscii();
    uint64_t written = write_shared_memory(name, value);
    outputs[0] = factory.createScalar<uint64_t>(written);
  }  
};

Последний мекс является наиболее сложным и обрабатывает создание и удаление общей памяти. Вы заметите присутствие деструктора, который обрабатывает удаление общей памяти, когда мекс выгружается из Matlab. Интерфейс принимает команду в виде "create" или "delete", строку с именем общего ресурса и размером общей памяти для создания (это должен быть беззнаковый int - uint16(...)). Функция возвращает размер разделяемой памяти (он должен быть равен размеру):

function size_shmem = menage_mex(command, share_name, uint16(size))
  ...
end

Реализация следующая:

// File: menage_share.cpp
#include "mex.hpp"
#include "mexAdapter.hpp"
#include "MatlabDataArray.hpp"
#include <string>
#include <cstring>
#include <cstdlib>
#include <vector>
#include <exception>

#include "boost/interprocess/shared_memory_object.hpp"
#include "boost/interprocess/mapped_region.hpp"

using namespace boost::interprocess;
using namespace matlab::data;

class MexFunction : public matlab::mex::Function {
private:
  std::shared_ptr<matlab::engine::MATLABEngine> engine;
  ArrayFactory factory;
  std::vector<std::string> pool; 

  void throwError(std::string errorMessage) {
    engine->feval(matlab::engine::convertUTF8StringToUTF16String("error"),
            0, std::vector<Array>({ factory.createScalar(errorMessage) }));
  }

  uint64_t run_command(const std::string & cmd, const std::string & name, uint64_t size) {
    if (cmd == "create")
      return create_shared_memory(name, size);       
    if (cmd == "delete")
      return delete_shared_memory(name, size);

    throwError("The command is unknown");
    return 0;
  }

  uint64_t create_shared_memory(const std::string & name, uint64_t size) {
    bool in_pool = false;
    for (const auto & el : pool) {
      if (el == name) {
        in_pool = true;
        break;
      }
    }
    if (in_pool) {
      try {
        shared_memory_object sm(open_only, name.c_str(), read_only);
        mapped_region sm_reg(sm, read_only);
        if (sm_reg.get_size() != size)
          throwError("Memory already exist and it is of different size");
        return 0;
      } catch (std::exception & e) {
        throwError(std::string("Cannot open existing shared memory (maybe already open?) :: ") + std::string(e.what()));
      }
    } else {
      try {
        shared_memory_object sm(create_only, name.c_str(), read_write);
        sm.truncate(size);
        pool.push_back(name);
        return size;
      } catch (std::exception & e) {
        throwError(std::string("Cannot create shared memory [" + name + "] (maybe already open?) :: ") + std::string(e.what()));
      }
    }
    return 0;
  }

  uint64_t delete_shared_memory(const std::string & name, uint64_t size) {
    std::size_t in_pool = 0; 
    for (const auto & el : pool) {
      if (el == name)
        break;
      in_pool++;
    }
    if (in_pool < pool.size()) {
      shared_memory_object::remove(name.c_str());
      pool.erase(pool.begin() + in_pool);
    } else {
      throwError("Shared memory [" + name + "] is not handled by this mex");
    }
    return 0;
  }

  void checkArguments(matlab::mex::ArgumentList inputs, matlab::mex::ArgumentList outputs) {
    if (inputs.size() != 3)
      throwError("Input must be of size 3");
    if (inputs[0].getType() != ArrayType::CHAR)
      throwError("First element must be a matlab char array");
    if (inputs[1].getType() != ArrayType::CHAR)
      throwError("Second element must be amatlab char array");
    if (inputs[2].getType() != ArrayType::UINT64)
      throwError("Third element must be a single uint64 integer");
    if (outputs.size() > 1)
      throwError("Too many outputs (required 1)");
  }

  void inputArguments(std::string & cmd, std::string & name, uint64_t & size, matlab::mex::ArgumentList inputs) {
    const CharArray cmd_array = std::move(inputs[0]);
    const CharArray name_array = std::move(inputs[1]);
    const TypedArray<uint64_t> size_array = std::move(inputs[2]);
    cmd = cmd_array.toAscii();
    name = name_array.toAscii();
    size = size_array[0];
  }

public:
  MexFunction() { 
    pool.clear(); 
    engine = getEngine();
  }

  ~MexFunction() {
    for (const auto & el : pool) {
      shared_memory_object::remove(el.c_str());
    }
  }

  void operator()(matlab::mex::ArgumentList outputs, matlab::mex::ArgumentList inputs) {  
    checkArguments(inputs, outputs);

    std::string cmd, name;
    uint64_t size;
    inputArguments(cmd, name, size, inputs);
    uint64_t ret = run_command(cmd, name, size);
    outputs[0] = factory.createScalar<uint64_t>(ret);
  }  
};

Для компиляции mex вы можете использовать следующий скрипт:

MEX_OPT = ['-I', '/path/to/boost'];

MEX_SRC = { ...
  'menage_share.cpp', ...
  'read_share.cpp', ...
  'write_share.cpp' ...
};

for i = 1:length(MEX_SRC)
  mex(MEX_OPT, MEX_SRC{i});
end

!g++ share_server.cpp -o share_server

и вы можете проверить их следующим образом:

(MATLAB)                                        | (TERMINAL)
>> menage_share('create', 'shmem', uint64(20))  | 
<< 20                                           |
>> write_share('shmem', 'Hello there')          | $ ./share_server
<< 11                                           |   ( ... help message ... )
                                                | << r
                                                | >> Hello there 
...