| 일 | 월 | 화 | 수 | 목 | 금 | 토 |
|---|---|---|---|---|---|---|
| 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 | 27 | 28 | 29 | 30 |
| 31 |
- GPIO
- Visual Studio
- 회로
- bare metal
- WPF
- buildroot
- avr-gcc
- 라즈베리파이
- Raspberry
- C++
- 디버깅
- nucleo
- Visual Studio Code
- Arduino
- atmel
- QEMU
- STM32
- AArch64
- vscode
- Debugging
- cpp
- esp32
- UART
- yocto
- Debug
- raspberrypi
- c#
- AVR
- Linux
- 아두이노
- Today
- Total
임베디드를 좋아하는 조금 특이한 개발자?
[C++] std::bind 함수를 원하는 호출 형태로 가공 본문
- 참고 문서
https://en.cppreference.com/w/cpp/utility/functional/bind.html
std::bind - cppreference.com
template< class F, class... Args > /* unspecified */ bind( F&& f, Args&&... args ); (1) (since C++11) (constexpr since C++20) template< class R, class F, class... Args > /* unspecified */ bind( F&& f, Args&&... args ); (2) (since C++11) (constexpr since C+
en.cppreference.com
1. 서론
프로젝트를 진행하다보면 꼭 한번 씩은 함수 포인터를 경험하게 됩니다. GUI 프로그래밍에서는 이벤트 핸들러 함수를 등록하거나 아니면 기능을 유연하게 추가하기 위해서도 함수 포인터를 사용합니다. 이럴때 마다 이전에 작성한 함수를 재사용하기 위해 wrapper 함수를 작성했어야 했습니다.
또는 맴버함수의 경우 함수이지만 다른 함수와 다르게 호출하는 방법이 다릅니다. 그렇기에 맴버 함수를 일반 함수처럼 호출 하는 형태로 가공해야합니다.
#include <iostream>
#include <functional>
using namespace std;
uint32_t calc(function<uint32_t(uint32_t,uint32_t)> handler, uint32_t n1, uint32_t n2) {
return handler(n1, n2);
}
// 함수
uint32_t add(uint32_t x, uint32_t y) {
return x + y;
}
class calculator {
public:
// 맴버 함수
uint32_t add(uint32_t x, uint32_t y) {
return x + y;
}
};
int main(void) {
// 문제 없음
int rs1 = calc(&add, 1, 2);
// 에러 발생 (맴버 함수가 속해 있는 인스턴스가 없음)
int rs2 = calc(&calculator::add, 1, 2);
return 0;
}

이러한 문제점을 해결하기 위해 C++에서는 std::bind를 제공하여 자연스럽게 함수를 원하는 형태로 가공하는 것이 가능합니다.
int main(void) {
using namespace std::placeholders;
// 문제 없음
int rs1 = calc(&add, 1, 2);
// bind로 맴버 함수를 전달
calculator cal1;
int rs2 = calc(bind(&calculator::add, &cal1, _1, _2), 1, 2);
cout << "rs1 : " << rs1 << endl;
cout << "rs2 : " << rs2 << endl;
return 0;
}

2. bind 사용 방법
#include <iostream>
#include <functional>
using namespace std;
uint32_t add(uint32_t x, uint32_t y) {
return x + y;
}
int main(void) {
using namespace std::placeholders;
// bind의 첫번째 인자는 호출가능한 함수(callable)
function<uint32_t(uint32_t, uint32_t)> f1 = bind(add, _1, _2);
// _1이 10으로 _2가 20으로 대체되어 호출
uint32_t result = f1(10, 20);
cout << result << endl;
return 0;
}

bind에서 첫번째 인자는 호출가능한 함수(함수, 맴버 함수, 람다 함수)이여야 합니다. 그리고 그 뒤에 f1으로 호출할 때 필요한 맴버 변수를 지정하게 됩니다.
사용 방법은 매우 간단하지만 밑 응용 방법을 보게 된다면 매우 강력한 기능임을 알 수 있습니다.
3. bind 응용
3.1. 매개 변수 생략
#include <iostream>
#include <functional>
using namespace std;
uint32_t mul(uint32_t x, uint32_t y) {
return x * y;
}
int main(void) {
// 1과 _1의 차이를 명확히 보여주기 위해 using를 사용하지 않았습니다.
// uint32_t(uint32_t, uint32_t)를 uint32_t(uint32_t)로 변경.
// 즉 매개변수 x의 값을 2로 고정함
function<uint32_t(uint32_t)> mul2 = bind(&mul, 2 , std::placeholders::_1);
for (uint32_t i = 2; i < 10; i++) {
cout << "2 x " << i << " = " << mul2(i) << endl;
}
return 0;
}

위 예제를 통해서 mul의 x 매개 변수를 2로 고정하여서 mul2의 함수 형태가 변경된 것을 확인 할 수 있습니다.
3.2. 매개 변수 순서 변경
#include <iostream>
#include <functional>
using namespace std;
void test(uint32_t n1, uint32_t n2) {
cout << "n1 : " << n1 << ", n2 : " << n2 << endl;
}
int main(void) {
using namespace std::placeholders;
// _1의 위치와 _2의 위치를 바꾸었습니다.
function<void(uint32_t,uint32_t)> func = bind(&test, _2, _1);
// n1 값에 20이, n2값에 10으로 대입된다.
func(10, 20);
return 0;
}

3.3. ref, cref를 통해 변수의 값을 반영
- 주의 사항 -
참조하는 변수에 따라 Dangling이 발생할 수 있습니다.
#include <iostream>
#include <functional>
using namespace std;
void test(uint32_t n1, uint32_t n2) {
cout << "n1 : " << n1 << ", n2 : " << n2 << endl;
}
int main(void) {
using namespace std::placeholders;
uint32_t value1 = 10;
// n1에 value1의 값인 10이 들어간다.
function<void(uint32_t)> func1 = bind(&test, value1, _1);
// n1은 value1을 참조하게 된다.
function<void(uint32_t)> func2 = bind(&test, cref(value1), _1);
value1 = 100;
// n1에 10이 들어갔으므로 n1이 10으로 출력
func1(20);
// n1이 valu1를 참조하므로 n1이 100으로 출력
func2(30);
return 0;
}

3.4. bind의 중첩 호출
#include <iostream>
#include <functional>
using namespace std;
uint32_t test(uint32_t num) {
cout << "num : " << num << endl;
return num;
}
int main(void) {
using namespace std::placeholders;
auto func1 = bind(&test, _1);
// func1를 중접
function<uint32_t(uint32_t)> func2 = bind(&test, func1);
// func2는 fun1을 중첩하여 bind하였기 때문에 test가 2번 호출
// func1의 _1는 10으로 대입
// 중첩된 bind는 placehodler를 공유하여 사용한다.
func2(10);
return 0;
}

3.5. 다양한 형태의 함수의 호출 일원화
#include <iostream>
#include <functional>
#include <vector>
using namespace std;
void func(uint32_t num) {
cout << "[funciton] num : " << num << endl;
}
class test {
public:
void mem_func(uint32_t num) {
cout << "[member function] num : " << num << endl;
}
};
int main(void){
using namespace std::placeholders;
// Callable 배열
vector<function<void(uint32_t)>> functions;
// 일반 함수 bind
functions.emplace_back(bind(&func, _1));
// 맴버 함수 함수 bind
test t1;
functions.emplace_back(bind(&test::mem_func, &t1, _1));
// 람다 함수 함수 bind
functions.emplace_back(
bind(
[](uint32_t num) {
cout << "[lambda function] num : " << num << endl;
},
_1
)
);
for (int i = 0; i < functions.size(); i++) {
functions[i](i);
}
return 0;
}

4. 결론
bind는 매우 편리한 기능임에는 분명하지만 C++언어가 그러하듯 잘못사용하였다가는 큰 문제가 발생할 요지가 있습니다. 잘못된 변수를 참조하여 Dangling 문제나 너무 많은 중첩 bind를 통해 성능 저하가 발생할 수 있다는 점이 있지만 이러한 점을 확실이 인지하고 사용한다면 개발에 큰 편의성을 제공해줄 수 있습니다.
'C++' 카테고리의 다른 글
| [C++] 사용자 정의 리터럴 (operator "") (0) | 2026.03.22 |
|---|---|
| [C++20 : Chrono.h] system_clock를 사용한 현재 시간 구하기 (0) | 2026.03.22 |
| [Visual Studio Code] C++ 자동완성(Autocomplete) 기능 활성화 (0) | 2026.03.09 |
| accumulate() 함수의 overflow 문제 해결 방법 (0) | 2025.01.21 |
| rel_ops를 통한 클래스 비교연산자 자동 구현 (0) | 2022.08.28 |