2012-08-29 31 views
14

Tôi đang cố gắng sử dụng std :: unique_ptrs để quản lý Windows HANDLE theo cách ngoại lệ.Sử dụng std :: unique_ptr cho Windows HANDLEs

Đầu tiên tôi đã cố gắng:

struct HandleDeleter 
{ 
    void operator()(HANDLE handle) 
    { 
     if(handle) 
     { 
      FindVolumeClose(handle) 
     } 
    } 
} 
typedef std::unique_ptr< HANDLE, HandleDeleter > unique_vol_handle_t; 

Sau đó trong mã của tôi khi tôi cố gắng sử dụng nó:

unique_vol_handle_t volH(FindFirstVolumeW(buffer, MAX_GUID_PATH));

tôi nhận được lỗi sau từ Visual Studio 2012RC:

1>   error C2664: 'std::unique_ptr<_Ty,_Dx>::unique_ptr(std::nullptr_t) throw()' : cannot convert parameter 1 from 'HANDLE' to 'std::nullptr_t' 
1>   with 
1>   [ 
1>    _Ty=HANDLE, 
1>    _Dx=VolumeHandleDeleter 
1>   ] 
1>   nullptr can only be converted to pointer or handle types 

tham chiếu đến đường khai báo volH, ngay phía trên.

Sau khi tìm kiếm một thời gian, tôi thấy a blog article mà về cơ bản nói thêm:

typedef HANDLE pointer;

để phía trên cùng của tờ khai struct, và tất cả sẽ tốt.

Tôi không tin, nhưng tôi đã thử và nó đã giải quyết được lỗi. Tôi đang bối rối như thế nào xác định một loại (thậm chí không tham khảo nó) có thể làm cho một sự khác biệt như vậy.

Hai câu hỏi:

1) Bạn có thể giải thích lỗi gốc không? Tôi không hiểu tại sao trình biên dịch lại đề cập đến std::nullptr_t/nullptr.

2) Làm cách nào để typedef giải quyết vấn đề này (hoặc ít nhất là xuất hiện)? Có một giải pháp ít 'ma quái ở một khoảng cách' cho điều này?

+0

[Câu hỏi này] (http://stackoverflow.com/questions/12066721/what-are- sử dụng-of-the-type-stdnullptr-t). có thể giúp bạn tự hỏi về 'nullptr_t'. –

+0

Cảm ơn, Joachim. Điều đó giúp với các thông điệp nullptr. – U007D

+1

+1 để tham khảo cơ học lượng tử – Jon

Trả lời

20

Việc triển khai unique_ptr kiểm tra sự hiện diện của loại ::pointer trên thiết bị phân cách. Nếu deleter có loại ::pointer thì loại này được sử dụng làm pointer typedef trên unique_ptr. Nếu không, một con trỏ tới đối số mẫu đầu tiên sẽ được sử dụng.

Theo cppreference.com, loại unique_ptr::pointer được định nghĩa là

std::remove_reference<D>::type::pointer nếu loại tồn tại, nếu không T*

+1

Cảm ơn bạn, Kevin đã giải thích rõ ràng. Và bằng cách không xác định loại con trỏ, tôi về cơ bản nói với các deleter để sử dụng HANDLE * (void **) thay vì HANDLE (void *), đó là không chính xác. Trình biên dịch đã cho tôi thông báo lỗi khó hiểu nhất thế giới, nhưng lời giải thích của bạn bằng tiếng Anh đơn giản, vì vậy tôi chấp nhận câu trả lời của bạn - cảm ơn bạn! – U007D

+0

Điều đó có hữu ích không? Tiêu chuẩn nói rằng 'toán tử *' trả về '* get()', nhưng có kiểu trả về 'T &', và 'get()' có kiểu trả về 'con trỏ'. –

+0

Đối với hầu hết các xử lý 0 là giá trị hợp lệ và INVALID_HANDLE_VALUE là giá trị không hợp lệ, vì vậy giải pháp này không chính xác đối với hầu hết các xử lý vì reset() của unique_ptr sẽ kiểm tra nullptr thay vì INVALID_HANDLE_VALUE để bạn phải tạo lớp trình bao bọc của riêng mình. – Air2

4

Từ MSDN manual on unique_ptr:

Con trỏ được lưu trữ để tài nguyên sở hữu, stored_ptr có con trỏ kiểu. Đó là Del::pointer nếu được xác định và Type * nếu không. Đối tượng được lưu trữ đối tượng stored_deleter không chiếm không gian trong đối tượng nếu số deleter là không quốc tịch. Lưu ý rằng Del có thể là một loại tham chiếu.

Điều này có nghĩa là nếu bạn cung cấp một hàm giả, nó phải cung cấp loại pointer được sử dụng cho loại con trỏ thực tế của unique_ptr. Nếu không, nó sẽ là con trỏ đến loại được cung cấp của bạn, trong trường hợp của bạn HANDLE* không chính xác.

+0

Xin chào, Joachim. Lời giải thích của Kevin ở trên rõ ràng hơn tôi so với ngôn ngữ của MSDN vì vậy tôi đã chấp nhận câu trả lời của anh ấy. Nhưng điều này không có ý nghĩa bây giờ. Cảm ơn bạn! – U007D

3

Tôi khuyên bạn nên xem A Proposal to Add additional RAII Wrappers to the Standard Library và tại the drawbacks of using smart pointers with handles.

Cá nhân, tôi đang sử dụng triển khai tham chiếu đề xuất đó thay vì sử dụng std::unique_ptr cho các trường hợp này.

+0

Tôi không thể nói rằng tôi hoàn toàn đồng ý với 'nhược điểm của việc sử dụng con trỏ thông minh với tiền đề xử lý', nhưng tôi đánh giá cao các liên kết đến các bài viết thú vị và kích thích tư duy như thế này. Cảm ơn bạn. – U007D

2

Tôi đã thực hiện các thao tác sau cho nhiều loại xử lý khác nhau trong Windows. Giả sử chúng ta đã tuyên bố ở đâu đó:

std::unique_ptr<void, decltype (&FindVolumeClose)> fv (nullptr, FindVolumeClose); 

này là dân cư với:

HANDLE temp = FindFirstVolume (...); 
if (temp != INVALID_HANDLE_VALUE) 
    fv.reset (temp); 

Không cần phải khai báo một struct riêng biệt để quấn deleters. Vì HANDLE thực sự là một void * các unique_ptr mất void làm loại của nó; đối với các loại tay cầm khác, sử dụng macro DECLARE_HANDLE, điều này có thể tránh được:

Và cứ tiếp tục như vậy.

1

Cách thích hợp (và an toàn) để sử dụng std::unique_ptr cho Windows xử lý là một cái gì đó như thế này:

#include <windows.h> 
#include <memory> 

class WinHandle { 
    HANDLE value_; 
public: 
    WinHandle(std::nullptr_t = nullptr) : value_(nullptr) {} 
    WinHandle(HANDLE value) : value_(value == INVALID_HANDLE_VALUE ? nullptr : value) {} 

    explicit operator bool() const { return value_ != nullptr; } 
    operator HANDLE() const { return value_; } 

    friend bool operator ==(WinHandle l, WinHandle r) { return l.value_ == r.value_; } 
    friend bool operator !=(WinHandle l, WinHandle r) { return !(l == r); } 

    struct Deleter { 
     typedef WinHandle pointer; 
     void operator()(WinHandle handle) const { CloseHandle(handle); } 
    }; 
}; 

inline bool operator ==(HANDLE l, WinHandle r) { return WinHandle(l) == r; } 
inline bool operator !=(HANDLE l, WinHandle r) { return !(l == r); } 
inline bool operator ==(WinHandle l, HANDLE r) { return l == WinHandle(r); } 
inline bool operator !=(WinHandle l, HANDLE r) { return !(l == r); } 

typedef std::unique_ptr<WinHandle, WinHandle::Deleter> HandlePtr; 

Bằng cách này INVALID_HANDLE_VALUE được ngầm coi là vô giá trị, và do đó sẽ không bao giờ được thông qua để CloseHandle chức năng và bạn không bao giờ cần phải kiểm tra một cách rõ ràng cho nó. Sử dụng std::unique_ptr 's operator bool() thay vào đó, như bình thường:

HandlePtr file(CreateFile(...)); 
if (!file) { 
    // handle error 
} 

EDIT: tôi ban đầu được quên rằng INVALID_HANDLE_VALUEis not the only invalid value cho các loại HANDLE, và rằng trong thực tế, nó được coi là một valid pseudo handle bởi nhiều người, nếu không muốn nói nhất , các hàm hạt nhân. Vâng, tốt để biết.

-1

Trong khi @Levi Haskell câu trả lời của anh ta mất INVALID_HANDLE_VALUE vào tài khoản, nó không tính đến 0 là giá trị xử lý hợp lệ và xử lý INVALID_HANDLE_VALUE và 0 (hoặc nullptr) như nhau. Đây không phải là chính xác:

Đề nghị của tôi (dựa trên Levi Haskell mã của mình để các khoản tín dụng cho anh ta)

template<void *taInvalidHandleValue> 
class cWinHandle 
{ 
    HANDLE value_ { taInvalidHandleValue }; 
public: 
    cWinHandle() = default; 
    cWinHandle(HANDLE value) : value_(value) {} 
    ~cWinHandle() 
    { 
     reset(); 
    } 

    explicit operator bool() const { return value_ != taInvalidHandleValue; } 
    operator HANDLE() const { return value_; } 

    HANDLE get() const { return value_; } 
    HANDLE release() { HANDLE const result{ value_ }; value_ = taInvalidHandleValue; return result; } 
    friend bool operator ==(cWinHandle l, cWinHandle r) { return l.value_ == r.value_; } 
    friend bool operator !=(cWinHandle l, cWinHandle r) { return !(l == r); } 
    void reset() 
    { 
     if (value_ != taInvalidHandleValue) 
     { 
      CloseHandle(value_); 
      value_ = taInvalidHandleValue; 
     } 
    } 
}; 

inline bool operator ==(HANDLE l, cWinHandle<nullptr> r) { return cWinHandle<nullptr>(l) == r; } 
inline bool operator !=(HANDLE l, cWinHandle<nullptr> r) { return !(l == r); } 
inline bool operator ==(cWinHandle<nullptr> l, HANDLE r) { return l == cWinHandle<nullptr>(r); } 
inline bool operator !=(cWinHandle<nullptr> l, HANDLE r) { return !(l == r); } 
inline bool operator ==(HANDLE l, cWinHandle<INVALID_HANDLE_VALUE> r) { return cWinHandle<INVALID_HANDLE_VALUE>(l) == r; } 
inline bool operator !=(HANDLE l, cWinHandle<INVALID_HANDLE_VALUE> r) { return !(l == r); } 
inline bool operator ==(cWinHandle<INVALID_HANDLE_VALUE> l, HANDLE r) { return l == cWinHandle<INVALID_HANDLE_VALUE>(r); } 
inline bool operator !=(cWinHandle<INVALID_HANDLE_VALUE> l, HANDLE r) { return !(l == r); } 
Các vấn đề liên quan