2014-11-26 16 views
7

Tôi sử dụng thư viện bên ngoài hoạt động với số lượng lớn dữ liệu. Dữ liệu được truyền bởi một con trỏ thô, cộng với độ dài. Thư viện không yêu cầu quyền sở hữu của con trỏ, nhưng gọi một hàm gọi lại được cung cấp (với cùng hai đối số) khi nó được thực hiện với dữ liệu.tiếp nhận bộ nhớ từ std :: vector

Dữ liệu được chuẩn bị thuận tiện bằng cách sử dụng std::vector<T> và tôi không muốn từ bỏ sự tiện lợi này. Sao chép dữ liệu hoàn toàn nằm ngoài câu hỏi. Vì vậy, tôi cần một cách để "tiếp nhận" bộ đệm bộ nhớ thuộc sở hữu của một std::vector<T>, và (sau này) deallocate nó trong gọi lại.

giải pháp hiện tại của tôi trông như sau:

std::vector<T> input = prepare_input(); 
T * data = input.data(); 
size_t size = input.size(); 
// move the vector to "raw" storage, to prevent deallocation 
alignas(std::vector<T>) char temp[sizeof(std::vector<T>)]; 
new (temp) std::vector<T>(std::move(input)); 
// invoke the library 
lib::startProcesing(data, size); 

và, trong hàm callback:

void callback(T * data, size_t size) { 
    std::allocator<T>().deallocate(data, size); 
} 

Giải pháp này hoạt động, bởi vì chức năng cấp phát tiêu chuẩn của deallocate bỏ qua đối số thứ hai của mình (số lượng nguyên tố) và chỉ cần gọi ::operator delete(data). Nếu không, những điều xấu có thể xảy ra, vì size của véc tơ đầu vào có thể hơi nhỏ hơn một chút so với capacity.

Câu hỏi của tôi là: có cách nào đáng tin cậy (wrt. C++ standard) để tiếp quản bộ đệm std::vector và phát hành "thủ công" sau một thời gian không?

+1

Bạn sẽ cần phải thực hiện trên toàn bộ vector. –

+0

Sẽ rất tuyệt nếu 'vectơ' có hàm' tách '... nhưng nó không –

+0

@TC: nhưng tôi không có nơi nào để lưu trữ - sản xuất đầu vào và deallocation xảy ra trong hai phần riêng biệt của progam –

Trả lời

2

Bạn không thể sở hữu bộ nhớ từ một véc tơ, nhưng bạn có thể giải quyết vấn đề cơ bản của mình theo cách khác. Đây là cách tôi tiếp cận nó - một chút hacky vì biến toàn cầu tĩnh và không phải là luồng an toàn, nhưng nó có thể được thực hiện như vậy với một số khóa đơn giản xung quanh việc truy cập đối tượng registry.

static std::map<T*, std::vector<T>*> registry; 
void my_startProcessing(std::vector<T> * data) { 
    registry.put(data->data(), data); 
    lib::startProcesing(data->data(), data->size()); 
} 

void my_callback(T * data, size_t length) { 
    std::vector<T> * original = registry.get(data); 
    delete original; 
    registry.remove(data); 
} 

Bây giờ bạn chỉ có thể làm

std::vector<T> * input = ... 
my_startProcessing(input); 

Nhưng hãy coi chừng! Những điều tồi tệ sẽ xảy ra nếu bạn thêm/xóa các phần tử vào đầu vào sau khi bạn đã gọi my_startProcessing - bộ đệm mà thư viện có thể bị vô hiệu. (Bạn có thể được phép thay đổi giá trị trong vectơ, vì tôi tin rằng sẽ ghi thông tin vào dữ liệu chính xác, nhưng điều đó cũng sẽ phụ thuộc vào thư viện cho phép.)

Điều này cũng không hoạt động nếu T = bool kể từ std::vector<bool>::data() không hoạt động.

+0

Có vẻ tốt. Nếu tôi không thể tìm ra cách nào để tránh các biến toàn cầu, tôi sẽ rắc nó bằng một chút 'std :: mutex' và' std :: unique_ptr' và nó sẽ ổn thôi. Cảm ơn! –

1

Bạn có thể tạo bản dựng lớp tùy chỉnh trên vectơ.

Điểm mấu chốt ở đây là sử dụng ngữ nghĩa di chuyển trong số SomeData hàm tạo.

  • bạn nhận được dữ liệu chuẩn bị mà không cần sao chép (lưu ý rằng vector nguồn sẽ bị xóa)
  • dữ liệu sẽ được xử lý một cách chính xác bởi destructor thisData vector
  • vector nguồn có thể được xử lý với không có vấn đề

Vì kiểu dữ liệu cơ bản sẽ là mảng bạn có thể tính toán con trỏ bắt đầu và kích thước dữ liệu (xem SomeDataImpl.h bên dưới):

SomeData.h

#pragma once 
#include <vector> 

template<typename T> 
class SomeData 
{ 
    std::vector<T> thisData; 

public: 
    SomeData(std::vector<T> && other); 

    const T* Start() const; 
    size_t Size() const; 
}; 

#include "SomeDataImpl.h" 

SomeDataImpl.h

#pragma once 

template<typename T> 
SomeData<T>::SomeData(std::vector<T> && otherData) : thisData(std::move(otherData)) { } 

template<typename T> 
const T* SomeData<T>::Start() const { 
    return thisData.data(); 
} 

template<typename T> 
size_t SomeData<T>::Size() const { 
    return sizeof(T) * thisData.size(); 
} 

Cách sử dụng Ví dụ:

#include <iostream> 
#include "SomeData.h" 

template<typename T> 
void Print(const T * start, size_t size) { 
    size_t toPrint = size/sizeof(T); 
    size_t printed = 0; 

    while(printed < toPrint) { 
     std::cout << *(start + printed) << ", " << start + printed << std::endl; 
     ++printed; 
    } 
} 

int main() { 
    std::vector<int> ints; 
    ints.push_back(1); 
    ints.push_back(2); 
    ints.push_back(3); 

    SomeData<int> someData(std::move(ints)); 
    Print<int>(someData.Start(), someData.Size()); 

    return 0; 
} 
Các vấn đề liên quan