2013-10-31 14 views
9

Cập nhật

Tôi đã tạo một qt bugticket với hy vọng tài liệu sẽ được mở rộng.So sánh QVariant với các loại riêng đang hoạt động?

Câu hỏi gốc

Tin tưởng một Question from 2010Qt Documentation, các operator==() không làm việc với các loại tùy chỉnh.

Trích:

bool QVariant :: operator == (const QVariant & v) const

So sánh QVariant này với v và trả true nếu họ đều bình đẳng; nếu không trả về false.

QVariant sử dụng toán tử bình đẳng của loại() nó chứa để kiểm tra tính bình đẳng. QVariant sẽ cố gắng convert()v nếu loại của nó không giống với loại của biến thể này. Xem canConvert() để biết danh sách các chuyển đổi có thể có.

Cảnh báo: Chức năng này không hỗ trợ các loại tùy chỉnh được đăng ký với qRegisterMetaType().

Tôi đã cố gắng tạo lại trường hợp repro từ Stackoverflow Question from 2010 và so sánh đã hoạt động mà không gặp bất kỳ sự cố nào đối với tôi.

Tôi cũng đã tiến thêm một bước và thử so sánh bằng cách sử dụng một lớp học riêng cũng hoạt động hoàn hảo. Để tái sản xuất, đặt đoạn mã sau vào bất kỳ tiêu đề:

enum MyEnum { Foo, Bar }; 
Q_DECLARE_METATYPE(MyEnum) 

class MyClass 
{ 
    int value; 
public: 
    MyClass() : value(0) 
    { 
    } 

    MyClass(int a) : value(a) 
    { 
    } 

    bool operator==(const MyClass &) const 
    { 
    Q_ASSERT(false); // This method seems not to be called 
    return false; 
    } 

    bool operator!=(const MyClass &) const 
    { 
    Q_ASSERT(false); // This method seems not to be called 
    return true; 
    } 
}; 

Q_DECLARE_METATYPE(MyClass) 

Và đoạn mã sau vào bất kỳ chức năng:

QVariant var1 = QVariant::fromValue<MyEnum>(Foo); 
QVariant var2 = QVariant::fromValue<MyEnum>(Foo); 
Q_ASSERT(var1 == var2); // Succeeds! 

var1 = QVariant::fromValue<MyEnum>(Foo); 
var2 = QVariant::fromValue<MyEnum>(Bar); 
Q_ASSERT(var1 != var2); // Succeeds! 

QVariant obj1 = QVariant::fromValue<MyClass>(MyClass(42)); 
QVariant obj2 = QVariant::fromValue<MyClass>(MyClass(42)); 
Q_ASSERT(obj1 == obj2); // Succeeds! 

obj1 = QVariant::fromValue<MyClass>(MyClass(42)); 
obj2 = QVariant::fromValue<MyClass>(MyClass(23)); 
Q_ASSERT(obj1 != obj2); // Succeeds! 

Tôi đoán rằng trong các phiên bản qt mới hơn kích thước của một loại được mua lại khi Q_DECLARE_METATYPE được sử dụng để QVariant có thể so sánh các giá trị của các loại không xác định trước đó.

Nhưng đó chỉ là phỏng đoán và tôi không muốn mạo hiểm tính ổn định của ứng dụng bằng cách đoán qt nào thay vì dựa vào tài liệu.

Tôi có thể tìm hiểu cách QVariant so sánh các loại không xác định không? Tôi muốn dựa vào đặc điểm kỹ thuật hơn là thực hiện.

+0

bạn có gọi 'qRegisterMetaType()' trên 'MyEnum' và' MyClass'? – Zaiborg

+0

Không, tôi không gọi 'qRegisterMetaType()'. Không có dòng mã nào sử dụng MyClass hoặc MyEnum ngoại trừ các dòng tôi đã đăng ở trên. –

+0

bạn đã thử đưa các giá trị trở lại so với loại so sánh ban đầu của nó? ('QVariant :: value ()') cũng là ví dụ kiểu 'var1'? (hoặc để được rõ ràng, những gì hiện các biến nói những gì loại nó là) – Zaiborg

Trả lời

15

Tôi e rằng bạn sẽ cần phải dựa vào mã (và, là hành vi, nó không thể thay đổi mà không vi phạm), chứ không phải tài liệu. Có một bất ngờ ngay bên dưới, mặc dù.

Đây là mã có liên quan.

QVariant::operator== đối với các loại có nhà khai thác chưa đăng ký sẽ chỉ sử dụng memcmp. Đoạn mã có liên quan (trong 5.1) là:

bool QVariant::cmp(const QVariant &v) const 
{ 
    QVariant v1 = *this; 
    QVariant v2 = v; 
    if (d.type != v2.d.type) 
     // handle conversions.... 

    return handlerManager[v1.d.type]->compare(&v1.d, &v2.d); 
} 

handlerManager là một đối tượng toàn cầu được sử dụng để thực hiện các thao tác nhận biết kiểu. Nó chứa một mảng các đối tượng QVariant::Handler; mỗi người trong số các đối tượng như chứa con trỏ để thực hiện một số hoạt động trên các loại họ biết làm thế nào để xử lý:

struct Handler { 
    f_construct construct; 
    f_clear clear; 
    f_null isNull; 
    f_load load; 
    f_save save; 
    f_compare compare; 
    f_convert convert; 
    f_canConvert canConvert; 
    f_debugStream debugStream; 
}; 

Mỗi của những thành viên thực sự là một con trỏ tới một hàm.

Lý do có mảng đối tượng chung này phức tạp một chút - nó cho phép các thư viện Qt khác (nói, QtGui) cài đặt trình xử lý tùy chỉnh cho các loại được xác định trong các libs đó (f.i. QColor).

Các operator[] trên handlerManager sẽ thực hiện thêm một số ma thuật, cụ thể là có được quyền mỗi mô-đun xử lý tuỳ theo loại:

return Handlers[QModulesPrivate::moduleForType(typeId)]; 

Bây giờ loại là tất nhiên một kiểu tùy chỉnh, do đó Handler trở đây là mô-đun Unknown. Đó Handler sẽ sử dụng customCompare chức năng trong qvariant.cpp, mà thực hiện điều này:

static bool customCompare(const QVariant::Private *a, const QVariant::Private *b) 
{ 
    const char *const typeName = QMetaType::typeName(a->type); 
    if (Q_UNLIKELY(!typeName) && Q_LIKELY(!QMetaType::isRegistered(a->type))) 
     qFatal("QVariant::compare: type %d unknown to QVariant.", a->type); 

    const void *a_ptr = a->is_shared ? a->data.shared->ptr : &(a->data.ptr); 
    const void *b_ptr = b->is_shared ? b->data.shared->ptr : &(b->data.ptr); 

    uint typeNameLen = qstrlen(typeName); 
    if (typeNameLen > 0 && typeName[typeNameLen - 1] == '*') 
     return *static_cast<void *const *>(a_ptr) == *static_cast<void *const *>(b_ptr); 

    if (a->is_null && b->is_null) 
     return true; 

    return !memcmp(a_ptr, b_ptr, QMetaType::sizeOf(a->type)); 
} 

Trong đó, ngoài một chút kiểm tra lỗi và xử lý các biến thể chia sẻ và null trong một cách đặc biệt, sử dụng memcmp về nội dung.

... chỉ khi loại này không phải là loại con trỏ, có vẻ như vậy. Tự hỏi tại sao có mã đó ở đó ...


Tin vui!

Bắt đầu với Qt 5.2, bạn có thể sử dụng QMetaType::registerComparator (xem here) để đặt Qt gọi operator<operator== trên loại tùy chỉnh của bạn. Chỉ cần thêm vào main của bạn:

qRegisterMetaType<MyClass>(); 
QMetaType::registerComparators<MyClass>(); 

Và thì đấy, bạn sẽ nhấn khẳng định trong điều hành bình đẳng của mình. QVariant::cmp hiện tại là:

QVariant v1 = *this; 
QVariant v2 = v; 
if (d.type != v2.d.type) 
    // handle conversions, like before 

// *NEW IMPORTANT CODE* 
if (v1.d.type >= QMetaType::User) { 
    // non-builtin types (MyClass, MyEnum...) 
    int result; 
    // will invoke the comparator for v1's type, if ever registered 
    if (QMetaType::compare(QT_PREPEND_NAMESPACE(constData(v1.d)), QT_PREPEND_NAMESPACE(constData(v2.d)), v1.d.type, &result)) 
     return result == 0; 
} 
// as before 
return handlerManager[v1.d.type]->compare(&v1.d, &v2.d); 
+0

Câu trả lời tuyệt vời và rất hữu ích! Cảm ơn! –

+0

Tuyệt vời !! Tôi nghĩ rằng nếu Qt được sinh ra ngày nay nó sẽ sử dụng một cách sử dụng rộng rãi hơn các mẫu. Nhưng sự tuân thủ tiêu chuẩn của trình biên dịch không quá phổ biến như ngày nay (cụ thể là, cơ chế tín hiệu/khe). –

Các vấn đề liên quan