c++ 클래스 필드가 std::vector<특정 클래스>
일 때, Rust에서 올바르게 사용하는 방법
autocxx
크레이트를 사용하면 된다.
다음과 같은 c++ 클래스가 있다고 가정하자.
class Goat {
public:
Goat(uint32_t horns) : horns(horns) {}
uin32_t get_horns() {
return this-> horns;
}
private:
uin32_t horns;
}
class GoatWrapper {
public:
void set_goat_list() {
test_list.push_back(Goat(30)); // 뿔의 개수
test_list.push_back(Goat(20));
test_list.push_back(Goat(10));
}
const std::vector<Goat> get_goat_list() const {
return this->goat_list;
}
private:
std::vector<Goat> goat_list;
};
필드 goat_list
의 경우 Goat
클래스를 담고 있는 vector
이다.
rust에서 다음과 같이 goat_wrapper 객체를 만든다.
let mut goat_wrapper = ffi::GoatWrapper::new().within_unique_ptr();
goat_wrapper.pin_mut().set_goat_list();
let mut goat_list = test_wrapper.pin_mut().get_goat_list(); // test_list는 UniquePtr<CxxVector<Goat>> 타입이다.
println!("goat_list len : {}", test_list.len()) // 3 (마리)출력!
for a in temp.pin_mut().iter_mut() {
let horns = a.get_horns();
println!("horns : {}", horns);
}
Rust에서 autocxx
를 사용할 때, c++ 객체를 pin_mut()으로 고정(Pinning) 해야 하는 경우가 많다. 그 이유는 c++ 객체의 메모리 위치를 변경하지 않도록 보장하기 위해서다. Rust에서는 기본적으로 소유권 이동이 적용된다. 하지만 c++에서는 포인터를 사용해 객체를 직접 가르키는 경우가 많다. c++ 객체가 Rust에서 이동하게되면, c++ 코드가 유지하던 포인터 관계가 깨질 수 있기 때문에 이를 방지하기 위해서 객체를 고정해서 이동을 막는다.
또한 c++ 객체가 자기 자신을 참조하는 경우가 있을 수 있다.
class Test {
public:
int value;
Test* self;
Test(int v) : value(v), self(this) {}
void print_self() {
std::cout << "My address: " << this << ", Stored address: " << self << std::endl;
}
};
위와 같은 이유로 rust에서 c++ 객체를 사용시 pin_mut()
를 해주어야 한다.
따라서 temp.pin_mut().iter_mut()
가 중요하다.
iter_mut()
가 Pin<&mut T>
을 반환하는데 T
타입은 Pin으로 고정된 상태여야한다.
std::vector<Goat>
vector가 가지고 있는 Goat
의 주소값을 고정시켜야 get_horns()를 호출할 수 있다.
만약 iter()
를 사용하게 되면 에러가 발생한다.
c++ 객체의 포인터 구조를 보장하기 위해서 rust에서 c++ 객체를 사용할 때 pin_mut()
을 통해 객체의 메모리 위치를 변경하지 않도록 보장해야한다.