Post

리눅스에서 C++ 함수를 C언어세서 사용하기

목차


개요

C++ 함수를 C언어에서 사용하는 방법은 크게 두 가지가 있다.

정적 라이브러리 (.a) 로 임포트하는 방법과 동적 라이브러리 (.so) 로 임포트하는 방법이다.

이 문서에서는 두 가지 방법을 간단히 알아본다.

방법

testfunc.cpp

예시를 들어보자. 이런 함수가 존재한다. 이 함수는 두 문자열을 합쳐서 반환하는 함수이다. 그러나 그 과정에서 C++에서만 사용할 수 있는 std::string 을 사용하고 있다. 이 함수를 C언어에서 사용하려면 어떻게 해야할까?

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
#include <iostream>
#include <string>
#include <cstdlib>
#include <cstring>

char* str_plustest(char *str1, char *str2)
{
	std::string s1(str1);
	std::string s2(str2);
	std::string s3 = s1 + s2;

	size_t len = s3.length() + 1;
	char *result = (char *)malloc(s3.length() + 1);
	memset(result, 0, len);
	strcpy(result, s3.c_str());
	return result;
}

testfunc.h 헤더를 만들고 함수의 선언을 넣어준다.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
#ifndef __TESTFUNC_H__
#define __TESTFUNC_H__


// C++ 함수를 C에서 사용하기 위해 extern "C" 필요
#ifdef __cplusplus
extern "C" {
#endif

// 여기에 함수 선언을 넣어준다.

/**
 * @brief 두 문자열을 합쳐서 반환하는 함수
 * 반환하는 과정에서 malloc을 사용하므로 함수 호출 시 반환값에 대한 free가 필요하다.
 *
 * @return char* 합쳐진 문자열 (free 필요)
*/
char* str_plustest(char *str1, char *str2);


#ifdef __cplusplus
}
#endif


#endif /* __TESTFUNC_H__ *

그리고 testfunc.cpp 파일에 testfunc.h 헤더를 include 해준다.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
#include "testfunc.h"

#include <iostream>
#include <string>
#include <cstdlib>
#include <cstring>

char* str_plustest(char *str1, char *str2)
{
	std::string s1(str1);
	std::string s2(str2);
	std::string s3 = s1 + s2;

	size_t len = s3.length() + 1;
	char *result = (char *)malloc(s3.length() + 1);
	memset(result, 0, len);
	strcpy(result, s3.c_str());
	return result;
}

C언어 소스에서 str_plustest 함수를 사용하기 위해 다음과 같이 작성했다.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
#include <stdio.h>
#include <stdlib.h>

#include "testfunc.h"

#define free_safe(ptr) if((_p) != NULL) { free((_p)); (_p) = NULL; }

int main()
{
	const char *str1 = "Hello ";
	const char *str2 = "World!";

	char *result = str_plustest((char *)str1, (char *)str2);
	printf("CONSOLE | result : %s\n", result);
	free_safe(result);

	return 0;
}

그리고 testfunc.cpptestfunc.h 파일은 cpp 디렉토리에 위치하도록 하였다.

이렇게 하면 소스는 준비가 끝난 것이다.

ls -al ./ 결과

  • cpp/
    • testfunc.cpp
    • testfunc.h
  • main.c

리눅스에서 C 관련 라이브러리를 사용하기 위해서는 라이브러리의 이름이 lib로 시작해야 한다.

1. 정적 라이브러리 (.a) 로 임포트

1
2
3
4
5
6
7
# cd cpp

LIBRARY_NAME="testfunc"
g++ -c testfunc.cpp -o testfunc.o
ar rcs "lib${LIBRARY_NAME}.a" testfunc.o

# 별다른 문제가 없다면 libtestfunc.a 파일이 생성된다.

그리고 main.c 파일을 빌드한다.

1
2
3
4
5
6
7
8
# -I : include 헤더를 참조할 디렉토리 경로 (testfunc.h를 cpp 디렉토리에 넣었으므로 추가.)
# -L : 라이브러리를 참조할 디렉토리 경로
# -l : 라이브러리 이름 (libtestfunc.a 파일을 참조한다.)
# libtestfunc.a 파일을 참조하고 싶다면, lib를 뺀 이름을 넣어야 한다.
# -lstdc++ : C++ 표준 라이브러리 링크
gcc main.c -I./cpp -L./cpp -ltestfunc -lstdc++ -o main

# 별다른 문제가 없다면 main 바이너리 파일이 생성된다.

생성된 ./main 파일을 실행하면 Hello World! 가 출력된다.

2. 동적 라이브러리 (.so) 로 임포트

명령어가 조금 다르다.

1
2
3
4
5
6
7
# cd cpp

LIBRARY_NAME="testfunc"
g++ -fPIC -c testfunc.cpp -o testfunc.o
g++ -shared -o "lib${LIBRARY_NAME}.so" testfunc.o

# 별다른 문제가 없다면 libtestfunc.so 파일이 생성된다.

main.c 파일을 빌드하는 과정은 똑같다.

1
2
3
4
5
6
7
# -I : include 헤더를 참조할 디렉토리 경로 (testfunc.h를 cpp 디렉토리에 넣었으므로 추가.)
# -L : 라이브러리를 참조할 디렉토리 경로
# -l : 라이브러리 이름 (libtestfunc.a 파일을 참조한다.)
# libtestfunc.a 파일을 참조하고 싶다면, lib를 뺀 이름을 넣어야 한다.
# -lstdc++ : C++ 표준 라이브러리 링크
gcc main.c -I./cpp -L./cpp -ltestfunc -lstdc++ -o main
# 별다른 문제가 없다면 main 바이너리 파일이 생성된다.

동적 라이브러리이기 때문에 동적 라이브러리 실행에 대한 환경변수를 설정해 줘야 한다.

다음과 같이 실행한다.

1
2
# LD_LIBRARY_PATH : 동적 라이브러리를 참조할 디렉토리 경로
LD_LIBRARY_PATH=./cpp ./main

참고

정적 라이브러리와 동적 라이브러리의 차이

  • 정적 라이브러리는 컴파일 시점에 라이브러리를 링크한다. 실행 파일에 내용이 모두 들어가기 때문에 실행 파일이 커진다.
    • 대신 프로그램 안에 모든 내용이 들어가기 때문에 실행 파일만 있으면 실행이 가능하다.
    • 프로그램이 로드되는 시점에는 정적 라이브러리의 내용을 메모리에 로드해야 하기에 시간이 느리지만, 수행 시간은 동적 라이브러리에 비해 빠르다.
  • 동적 라이브러리는 실행 시점에 라이브러리를 링크한다. 실행 파일이 작아진다.
    • 런타임 시점에서 라이브러리를 로드하기 때문에 실행 파일만 있으면 실행이 불가능하다.
    • 동적 라이브러리에 대한 의존성이 있고, 어떤 동적 라이브러리 모듈을 참조할지 알아야 하기에 추가적인 환경변수 지정 등이 필요하다.
      • ex ) /etc/ld.so.conf.d 수정 이후 ldconfig 적용 또는 LD_LIBRARY_PATH 환경변수 설정 등..
    • 프로그램이 로드되는 시점에는 동적 라이브러리의 내용을 메모리에 로드해야 하기에 시간이 빠르지만, 함수 호출 마다 라이브러리의 주소에 접근해야 하기에 수행 시간이 느리다.

라이브러리마다 각각의 장단점이 있으므로 상황에 맞게 사용하면 된다.

This post is licensed under CC BY 4.0 by the author.