스펙업/2024 winter-study

[MATLAB] 매트랩에서 C언어 함수 사용하기

성밍쟁 2024. 1. 12. 15:40
728x90
반응형

프롤로그

매트랩에서 C언어에서 작성된 함수를 불러와서 사용해야하는 일을 알아보기 위해 이것저것 찾아보았다.

찾은 방법은 총 2가지이며 MEX와 DLL 방법 두 가지이다.

MEX는 MATLAB에서 빠르고 편리하게 사용할 수 있는 방면에 MATLAB API 를 사용하기 때문에 C언어에서 조금씩 변형된 함수들로 작성해야하고

DLL은 그냥 복잡했다.

 

 

 

MEX (MATLAB Executable)

MEX는 MATLAB에서 C, C++ 또는 Fortran 으로 작성된 프로그램을 실행할 수 있게 하는 기능으로, 이를 통해 MATLAB의 고수준 언어 기능과 C/C++ 또는 Fortran 의 저수준 언어 성능을 결합할 수 있다.

MEX 파일은 MATLAB 함수처럼 동작하며, MATLAB 데이터 타입과 직접적으로 상호작용할 수 있게 해주는 특수한 형태의 동적 링크 라이브러리이다.

이 방식은 MATLAB API를 통해 MATLAB 데이터 타입과 상호작용하여, 성능이 중요한 계산을 최적화 하거나 이미 존재하는 C/C++ 코드를 재사용하고자 할 때 매우 유용하다.

사용법은 아래와 같다.

 

 

매트랩 애드온 받기

매트랩 애드온 받기버튼을 누르고

MATLAB Support for MinGW-w64 C/C+/Fortan Compiler 를 설치한다.

 

 

파일 준비

C언어를 매트랩에서 사용할 것이기에 일단 임시적으로 파일을 생성하자.

//name - test.c
#include "mex.h"

void mexFunction(int nlhs, mxArray *plhs[], int nrhs, const mxArray *prhs[]) { //출력인자, 출력인자를 위한 배열, 입력인자, 입력인자를 위한 배열
    // 입력 인자 확인
    if(nrhs != 2) {
        mexErrMsgIdAndTxt("MyToolbox:arrayProduct:nrhs", "두 입력이 필요합니다.");
    }

    // 입력이 double 형식인지 확인
    if(!mxIsDouble(prhs[0]) || !mxIsDouble(prhs[1])) {
        mexErrMsgIdAndTxt("MyToolbox:arrayProduct:notDouble", "입력은 double이어야 합니다.");
    }
    // 입력 배열 받기
    double *A = mxGetPr(prhs[0]);
    double *B = mxGetPr(prhs[1]);

    // 입력 배열의 크기 받기
    mwSize n = mxGetNumberOfElements(prhs[0]);

    // 출력 배열 생성
    plhs[0] = mxCreateDoubleMatrix(1, n, mxREAL);

    // 출력 배열에 데이터 쓰기
    double *C = mxGetPr(plhs[0]);
    for(mwSize i = 0; i < n; i++) {
        C[i] = A[i] + B[i];
    }
}

test.c라는 C언어 파일을 하나 생성한다. 매트랩에서 C언어를 사용하기 위해서는 매트랩의 API를 사용해야하므로, 기존에 알던 C언어와는 좀 다를 수가 있다.

위 코드는 1차원 배열 2개 입력받았을 때(매트랩 행렬) 두 행렬을 더해주는 기능이다.

그리고 test_mat.m 이라는 파일을 하나 생성해준다.

//test_map.m

A = [1, 2, 3];
B = [4, 5, 6];
C = test(A, B); % 'test'는 컴파일된 MEX 파일의 이름입니다.
disp(C)

 

이렇게 테스트 할 파일들은 준비되었다.

 

파일은 이렇게 준비되어야 한다.

이게 해보니까 파일 명이 막 OneDrive - xxx 이런식으로 공백이 있거나 한글이 있거나 하면 제대로 실행이 안 되니까 그냥 C:\ 에다가 matlab 폴더 생성하고 거기에다가 작성하기를 추천한다.

 

 

실행

명령창에 mex -setup 를 입력한다.

mex -setup

그렇다면 다른 C 컴파일러를 선택하라는데, MinGW64 Compiler (C) 를 선택해준다.

 

그리고 mex test.c 를 입력하여

mex test.c

위와 같이 성공적으로 완료되었습니다. 가 뜨면 정상이다.

 

폴더가 하나 생성되어있을 것이다.

 

여기에  이제 아까 작성한 test_mat.m 을 실행시키자

정상적으로 두 행렬의 덧셈 연산이 수행된 것을 알 수 있다.

 

 

 

동적 링크 라이브러리(DLL)

C언어로 작성된 함수를 동적 링크 라이브러리(DLL)로 컴파일 하고 MATLAB에서 이를 불러와서 사용할 수 있다.

 

 

Visual Studio 프로젝트 생성하기

여기서 새 프로젝트 만들기 버튼을 클릭하고

dll을 입력하고 "내보내기가 있는 동적 연결 라이브러리(DLL)을 선택해준다.

프로젝트 명은 아무거나 입력하는데, 나는 Project1을 입력하고, 제일 오른쪽 아래에 만들기 버튼을 클릭한다

만들어진 모습이다.

 

 

C 소스파일과 헤더파일 만들기

아까와 같은 기능을 하도록 설정을 할 것이다.

소스파일  우클릭 -> 추가 -> 새 항목 버튼을 클릭하고

test2.c를 입력하고 추가 버튼을 누른다

#include "test2.h"


__declspec(dllexport) void array_add(double* A, double* B, double* C, int n) {
    for (int i = 0; i < n; ++i) {
        C[i] = A[i] + B[i];
    }
}

test2.c 에다가 위와 같이 입력한다.

입력 후 저장(ctrl+s)를 한다.

 

다음으로 헤더파일을 생성할 것 이다.

오른쪽 헤더파일 우클릭 -> 추가 -> 새항목을 클릭하고

test2.h 헤더파일을 생성한다.

그리고나서

//#pragma once

#ifdef __cplusplus
extern "C" {
#endif

	__declspec(dllexport) void array_add(double* A, double* B, double* C, int n);

#ifdef __cplusplus
}
#endif

위 와 같이 입력한다.

//#pragma once 는 일단 주석처리 해놨는데, 나중에 오류떠서 주석처리 해놓은 것이다.(어떤 기능을 하는지는 따로 찾아보지는 않음)

입력이 다 되면 저장버튼을 누르고

이제 Project1을 우클릭해서 속성 버튼을 누른다

왼쪽에 C/C++ 부분에서 미리 컴파일된 헤더 부분을 클릭하고 나서 오른쪽 맨 위에 있는 미리 컴파일된 헤더 부분을

사용 에서 미리 컴파일된 헤더 사용 안 함으로 바꿔주고 적용 -> 확인 버튼을 누른다. (이거 안 하니까 오류남)

그 후에

위 사진과 같이 빌드 -> 솔루션 빌드 버튼을 클릭한다.

이렇게 뜨면 성공이다.

 

 

MATLAB 코드 작성하기

일단 매트랩을 실행시킨다.

파일 경로를 확인한다.

Project1/x64/Debug 안에 Project1.dll 이 있는지 확인하고, Project1 안에 방금 만든 test2.c 와 test2.h 가 있는지 확인한다.

그리고 이제 현재 폴더에다가 test2_mat.m 파일을 만든다

% MATLAB 스크립트: test2_mat.m
% DLL과 헤더 파일의 경로를 정의
dllPath = fullfile('C:', 'matlab', 'Project1', 'x64', 'Debug', 'Project1.dll');
headerPath = fullfile('C:', 'matlab', 'Project1', 'test2.h');

% 라이브러리가 이미 로드되었는지 확인
if not(libisloaded('Project1'))
    [notfound, warnings] = loadlibrary(dllPath, headerPath);
    if not(isempty(notfound))
        error('DLL could not be found or loaded. Check the path and the name.');
    end
    if not(isempty(warnings))
        disp(warnings);
    end
end

% 입력 배열 생성
A = [1, 2, 3];
B = [4, 5, 6];
C = zeros(1, 3); % 결과를 저장할 배열

% libpointer를 사용하여 배열에 대한 포인터 생성
pA = libpointer('doublePtr', A);
pB = libpointer('doublePtr', B);
pC = libpointer('doublePtr', C);

% 라이브러리 함수 호출
if libisloaded('Project1')
    calllib('Project1', 'array_add', pA, pB, pC, numel(A));
    C = pC.Value; % 결과를 MATLAB 배열로 가져오기
else
    error('The library is not loaded.');
end

% 결과 확인
disp('결과');
disp(C);

% 사용이 끝났으면 라이브러리 언로드
unloadlibrary('Project1');

dllpath와 headerPath 는 fullfile 을 이용하여 Project1.dll과 test2.h 가 위치하고 있는 파일 경로를 적는다.

그리고 실행을 시켜보면

결과가 제대로 나온 것을 확인할 수 있다.

Korean_Korea.949 저거 오류 뜨는 거 해결해보려고 했는데, 해결이 안 되고 딱히 중요한 것은 아니라서 그냥 냅뒀다.

 

DLL방식에서 중요한 것은 파일 이름, 파일 경로이다. 

파일 명이라든지 프로젝트 명이 달라지더라도 위와 같이 사용하면 될 것이다.

 

 

 

에필로그

DLL만 하는데 6시간 걸렸다. 자꾸 어디서 오류가 뜨는지 파악하기도 힘들었고, 파일 경로부터;; 힘들긴했다.

Visual Studio 도 처음 써보는 게 컸다.

나중에 DLL와 MEX 사용방법에 대해 좀 더 찾아보겠다.

 

 

 

728x90
반응형