2011-04-24 56 views
25

Trong Visual Studio, có __declspec(property) tạo thuộc tính tương tự như C#. Borland C++ cung cấp từ khóa __property với cùng chức năng chính xác. Trong C++ 0x, có đề cập đến từ khóa implicit có thể được mở rộng để triển khai cùng chức năng. Nhưng nó đã không làm cho nó thành spec.Tính di động của các thuộc tính Native C++

Tôi đang tìm một phương pháp di động và tương đối sạch sẽ khai báo các thuộc tính có tính chất cú pháp sẽ biên dịch trong các trình biên dịch mới nhất cho Windows, OSX và Linux. Tôi không quan tâm đến tính tương thích của trình biên dịch, chỉ một trình biên dịch cho mỗi nền tảng.

Tôi không tìm kiếm các lựa chọn thay thế cho các thuộc tính yêu cầu dấu ngoặc đơn để nhận hoặc đặt thuộc tính, chẳng hạn như các phương thức quá tải tách các getters và setters.

Đây là một cách sử dụng lý tưởng mà biên dịch trong Visual Studio 2010:

#define _property(_type, _name, _get, _put) __declspec(property(get=_get, put=_put)) _type _name 
#define _property_readonly(_type, _name, _get) __declspec(property(get=_get)) _type _name 

class Window 
{ 
public: 
    _property_readonly(void*, Handle, GetHandle); 
    _property(bool, Visible, GetVisible, SetVisible); 

    void* GetHandle(); 
    bool GetVisible(); 
    void SetVisible(bool); 
} 

void main() 
{ 
    Window MainWindow; 
    if (!MainWindow.Visible) 
     MainWindow.Visible = true; 
} 
+2

Không có từ khóa 'ngầm 'trong C++ 0x. Tôi không thể nhận được liên kết mà bạn đăng mặc dù (nó yêu cầu một tên người dùng/mật khẩu). –

+1

Tôi đã cập nhật liên kết. Có vẻ như không bao giờ được đưa vào thông số C++ 0x. –

+11

$ 0,02 của tôi là rõ ràng nếu bạn đang hướng tới tính di động, chỉ cần tránh xa các phần mở rộng này. Giai đoạn. – sehe

Trả lời

25

Đây là một cái gì đó tương tự như những gì bạn đang yêu cầu và là (Tôi hy vọng) tiêu chuẩn C++ ...

#include <iostream> 

template<typename C, typename T, T (C::*getter)(), void (C::*setter)(const T&)> 
struct Property 
{ 
    C *instance; 

    Property(C *instance) 
     : instance(instance) 
    { 
    } 

    operator T() const 
    { 
     return (instance->*getter)(); 
    } 

    Property& operator=(const T& value) 
    { 
     (instance->*setter)(value); 
     return *this; 
    } 

    template<typename C2, typename T2, 
      T2 (C2::*getter2)(), void (C2::*setter2)(const T2&)> 
    Property& operator=(const Property<C2, T2, getter2, setter2>& other) 
    { 
     return *this = (other.instance->*getter2)(); 
    } 

    Property& operator=(const Property& other) 
    { 
     return *this = (other.instance->*getter)(); 
    } 
}; 

////////////////////////////////////////////////////////////////////////// 

struct Foo 
{ 
    int x_, y_; 

    void setX(const int& x) { x_ = x; std::cout << "x new value is " << x << "\n"; } 
    int getX() { std::cout << "reading x_\n"; return x_; } 

    void setY(const int& y) { y_ = y; std::cout << "y new value is " << y << "\n"; } 
    int getY() { std::cout << "reading y_\n"; return y_; } 

    Property<Foo, int, &Foo::getX, &Foo::setX> x; 
    Property<Foo, int, &Foo::getY, &Foo::setY> y; 

    Foo(int x0, int y0) 
     : x_(x0), y_(y0), x(this), y(this) 
    { 
    } 
}; 

int square(int x) 
{ 
    return x*x; 
} 

int main(int argc, const char *argv[]) 
{ 
    Foo foo(10, 20); 
    Foo foo2(100, 200); 
    int x = foo.x; std::cout << x << "\n"; 
    int y = foo.y; std::cout << y << "\n"; 
    foo.x = 42; std::cout << "assigned!\n"; 
    x = foo.x; std::cout << x << "\n"; 
    std::cout << "same instance prop/prop assign!\n"; 
    foo.x = foo.y; 
    std::cout << "different instances prop/prop assign\n"; 
    foo.x = foo2.x; 
    std::cout << "calling a function accepting an int parameter\n"; 
    std::cout << "square(" << foo.x << ") = " << square(foo.x) << "\n"; 
    return 0; 
} 

Như bạn có thể nhìn thấy từ main việc sử dụng trong suốt chừng nào bạn đang gán giá trị của loại T (ở đây int) hoặc chuyển đổi hoàn toàn thành T thành thuộc tính và miễn là bạn chuyển đổi chúng trở lại thành T giá trị khi đọc.

Behavior sẽ tuy nhiên khác nhau nếu bạn ví dụ vượt qua foo.x một mẫu chức năng vì loại foo.x không phải là int nhưng Property<Foo, int, ...> để thay thế.

Bạn cũng có thể gặp sự cố với chức năng không phải mẫu ... gọi một hàm chấp nhận giá trị T sẽ hoạt động tốt, tuy nhiên thông số T& là ví dụ sẽ là vấn đề vì về cơ bản hàm đang yêu cầu biến để truy cập trực tiếp sử dụng địa chỉ. Vì lý do tương tự, bạn không thể vượt qua tất nhiên địa chỉ của một thuộc tính cho một hàm chấp nhận tham số T*.

+0

Tương tự như [một meta-accessor đơn giản] (http://www.kirit.com/C%2B%2B%20killed%20the%20get%20%26%20set%20accessors/A%20simple%20meta -truy cập); sự khác biệt duy nhất là sự lựa chọn quá tải 'operator()' thay vì 'toán tử =' và 'toán tử T', và (loại) phân biệt giữa các loại POD và không phải POD. –

+1

Con lớn nhất là nó tiêu thụ một con trỏ thêm cho mỗi tài sản nhưng nó chắc chắn là chấp nhận được cho sự tiện lợi. Giả sử tối ưu hóa nội tuyến, nó có rất ít chi phí. Điều này cũng sạch hơn rất nhiều so với việc sử dụng các định nghĩa với các phương thức độc quyền riêng biệt trên mỗi trình biên dịch và tôi có thể có các getters và setters riêng để giữ cho IntelliSense sạch sẽ. Điều này làm cho việc dịch mã phụ thuộc vào cấu trúc thành các lớp hướng sự kiện thực sự dễ dàng. Cảm ơn bạn! –

+0

Tôi chỉ thử nghiệm với việc thực hiện 'Property' này vào đêm qua và đã được hồi hộp ngay lập tức (hy vọng cho các thuộc tính giống như C#). Sau đó, tôi đã cố gắng chuyển thuộc tính tới tham số hàm không được tạo khuôn mẫu và nhấn vào "không' int' nhưng "thuộc tính ' "mà bạn đề cập ở phần cuối của câu trả lời. Vì vậy, đối với bất kỳ ai quan tâm đến việc thử này, chỉ cần nhớ rằng những thuộc tính này không thể được thông qua xung quanh như thể chúng là kiểu cơ bản của chúng. Chúng "không có gì nhiều hơn" so với cú pháp để gọi hàm. Không nhiều không ít. –

3

Tôi đang tìm kiếm một xách tay và phương pháp tương đối sạch của tuyên bố thuộc tính cú pháp sugared rằng sẽ biên dịch trong các trình biên dịch mới nhất dành cho Windows, OSX và Linux.

Bạn đang mô tả các khả năng loại "siêu đối tượng", như các thuộc tính được xác định thời gian hoặc thời gian chạy, chẳng hạn như các thuộc tính có thể được thực hiện thông qua "hạt Java" hoặc "Phản xạ .NET" hoặc bất kỳ số cách nào với các ngôn ngữ kịch bản cấp cao, như Python và Perl.

Ví dụ: những gì bạn mô tả (thuộc tính biên dịch và/hoặc thời gian chạy) được triển khai trong các thư viện Qt (C++) thông qua QMetaObject. Bạn có thể khởi tạo nó trực tiếp, sử dụng nó như là một "thành viên" trong lớp học của bạn, hoặc lấy từ QObject để "tự động" có được hành vi meta-đối tượng (và một số thứ khác, như "đúc" giúp, và tín hiệu/khe chéo chủ đề). Tất nhiên, chúng khá đa nền tảng (ví dụ: Win, Mac, Posix).

Tôi không phải là người hâm mộ lớn của việc sử dụng __declspec(), ngoại trừ việc sử dụng nền tảng cụ thể, chẳng hạn như xuất khẩu rõ ràng các loại thông qua "Microsoft Extension DLL" (mà tôi thường cố gắng tránh nếu có thể). Tôi không nghĩ rằng có bất kỳ cách nào để làm cho việc sử dụng như vậy "cross-nền tảng" (kể từ khi sử dụng cụ thể là cụ thể cho MS DLLs).

Tương tự, sẽ không khó để viết lớp "MyMetaObject" của riêng bạn về cơ bản là "từ điển" hoặc "băm" hoặc "mảng liên kết", đối tượng của bạn sử dụng và được điền động tại thời gian chạy, ngay cả với các loại nội bộ của bạn (chẳng hạn như MyColor, MyTime, MyFilePath, v.v.) Tôi đã thực hiện nhiều lần và không cần nhiều công việc và nó có thể hoạt động khá thanh lịch. (QMetaObject thường khá mạnh hơn các phương pháp đơn giản này, nhưng nó đòi hỏi bước biên dịch "moc", là bước rất mạnh để tạo mã tra cứu nhanh cho các thuộc tính của nó và để bật tín hiệu/khe).

Cuối cùng, bạn bắt đầu chạm nhẹ vào miền "Dynamic C++", điều này ngụ ý việc sử dụng cú pháp C++ gần giống như kịch bản. Đây là một đề xuất đi sâu vào một chút về cách sử dụng động này, nơi bạn viết kịch bản với các thuộc tính này, không cần phải biên dịch lại.(Đề nghị đặc biệt này xảy ra được dựa trên hành vi QMetaObject loại, nhưng có những đề xuất khác với những suy nghĩ sử dụng tương tự):

http://www.codeproject.com/KB/cpp/dynamic_cpp.aspx

Nếu bạn google "Dynamic C++" hoặc "C++ Scripting", bạn có thể nhận được một số ý tưởng khác. Có một số suy nghĩ thông minh xấu xa trong một số thứ đó.

1

Tôi thích câu trả lời của 6502. Nó sử dụng cả bộ nhớ ít hơn và nhanh hơn giải pháp tôi sẽ trình bày. Chỉ có tôi sẽ có một đường cú pháp.

tôi muốn để có thể wite một cái gì đó như thế này (với pImpl thành ngữ):

class A { 
private: 
    class FImpl; 
    FImpl* Impl; 

public: 
    A(); 
    ~A(); 

    Property<int> Count; 
    Property<int> Count2; 
    Property<UnicodeString> Str; 
    Property<UnicodeString> Readonly; 
}; 

Ở đây có mã completet (tôi khá chắc chắn rằng nó là tuân thủ QTI tiêu chuẩn):

template <typename value_t> 
class IProperty_Forward { 
public: 
    virtual ~IProperty_Forward() {} 
    virtual const value_t& Read() = 0; 
    virtual void Set(const value_t& value) = 0; 
}; 

template <typename value_t, typename owner_t, typename getter_t, typename setter_t> 
class TProperty_Forwarder: public IProperty_Forward<value_t> 
{ 
private: 
    owner_t* Owner; 
    getter_t Getter; 
    setter_t Setter; 
public: 
    TProperty_Forwarder(owner_t* owner, getter_t& getter, setter_t& setter) 
    :Owner(owner), Getter(getter), Setter(setter) 
    { } 

    const value_t& Read() 
     { return (Owner->*Getter)(); } 

    void Set(const value_t& value) 
     { (Owner->*Setter)(value); } 
}; 

template <typename value_t> 
class Property { 
private: 
    IProperty_Forward<value_t>* forward; 
public: 
    Property():forward(NULL) { } 

    template <typename owner_t, typename getter_t, typename setter_t> 
    Property(owner_t* owner, getter_t getter, setter_t setter) 
     { Init(owner, getter, setter); } 

    ~Property() 
     { delete forward; } 

    template <typename owner_t, typename getter_t, typename setter_t> 
    void Init(owner_t* owner, getter_t getter, setter_t setter) 
    { 
     forward = new TProperty_Forwarder<value_t, owner_t, getter_t, setter_t>(owner, getter, setter); 
    } 

    Property& operator=(const value_t& value) 
    { 
     forward->Set(value); 
     return *this; 
    } 

    const value_t* operator->() 
    { return &forward->Read(); } 

    const value_t& operator()() 
     { return forward->Read(); } 

    const value_t& operator()(const value_t& value) 
    { 
     forward->Set(value); 
     return forward->Read(); 
    } 

    operator const value_t&() 
     { return forward->Read(); } 
};  

Và một số chi tiết triển khai:

class A::FImpl { 
    public: 
     FImpl():FCount(0),FCount2(0),FReadonly("Hello") { } 

     UnicodeString FReadonly; 
     const UnicodeString& getReadonly() 
      { return FReadonly; } 
     void setReadonly(const UnicodeString& s) 
      { } 

     int FCount; 
     int getCount() 
      { return FCount; } 
     void setCount(int s) 
      { FCount = s; } 

     int FCount2; 
     int getCount2() 
      { return FCount2; } 
     void setCount2(int s) 
      { FCount2 = s; } 

     UnicodeString FStr; 
     const UnicodeString& getStr() 
      { return FStr; } 
     void setStr(const UnicodeString& s) 
      { FStr = s; } 
}; 

A::A():Impl(new FImpl) 
{ 
    Count.Init(Impl, &FImpl::getCount, &FImpl::setCount); 
    Count2.Init(Impl, &FImpl::getCount2, &FImpl::setCount2); 
    Str.Init(Impl, &FImpl::getStr, &FImpl::setStr); 
    Readonly.Init(Impl, &FImpl::getReadonly, &FImpl::setReadonly); 
} 

A::~A() 
{ 
    delete Impl; 
} 

Tôi đang sử dụng C++ Builder cho bất kỳ ai thắc mắc về UnicodeS tring class. Hy vọng nó sẽ giúp những người khác để thử nghiệm Tiêu chuẩn phù hợp C++ Thuộc tính. Cơ chế cơ bản giống như 6502, với cùng giới hạn.

3

Hiện tại, Clang có Microsoft __declspec(property...) được triển khai đầy đủ và tối ưu hóa tốt đẹp. Vì vậy, bạn có thể sử dụng tính trong c của bạn ++ trên tất cả các nền tảng và intermix trong gcc dựa hoặc mã c99, vv

Tôi đã sử dụng nó trong hơn một năm, và chờ đợi này xuất hiện phổ biến hơn năm năm.

Đây là một trong những công cụ C++ mạnh mẽ nhất để tóm tắt cấu trúc và mã tái cấu trúc. Tôi sử dụng nó tất cả thời gian để cho phép tôi nhanh chóng xây dựng một cấu trúc và sau đó refactor nó sau này là hiệu suất hoặc tái cơ cấu đòi hỏi nó.

Điều vô giá và tôi thực sự không hiểu lý do tại sao các tiêu chuẩn C++ chưa được áp dụng từ lâu. Nhưng sau đó một lần nữa, họ có rất nhiều cách phức tạp và bloated tăng cách sử dụng C++ và các mẫu.

Clang rất di động trên mọi nền tảng hiện nay có tính năng này thật tuyệt vời.

Phát triển trong phạm vi (phiên bản miễn phí hoặc trả phí) Visual Studio sử dụng clang gần như liền mạch và bạn có được bộ công cụ phát triển gỡ lỗi đáng kinh ngạc.

Tôi chỉ sử dụng riêng clang ngay bây giờ cho tất cả phát triển C++ của mình.

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