原生 C++ 的可移植性特性

发布于 2024-11-03 09:52:19 字数 1054 浏览 1 评论 0原文

在 Visual Studio 中,有 __declspec(property) 用于创建属性与C#类似。 Borland C++ 提供了具有完全相同功能的__property 关键字。在 C++0x 中,提到了 隐式 关键字可以扩展以实现相同的功能。但它没有进入规范。

我正在寻找一种可移植且相对干净的方法来声明语法糖属性,该方法将在 Windows、OSX 和 Linux 的最新编译器中进行编译。我不关心编译器兼容性,只关心每个平台一个编译器。

我并不是在寻找需要括号来获取或设置属性的属性的替代方案,例如分隔 getter 和 setter 的重载方法。

以下是在 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;
}

In Visual Studio, there is __declspec(property) which creates properties similar to C#. Borland C++ offers the __property keyword with the exact same functionality. In the C++0x, there is mention of a implicit keyword that could be expanded to implement the same functionality. But it didn't make it into the spec.

I am looking for a portable and relatively clean method of declaring syntactically sugared properties that will compile in the latest compilers for Windows, OSX and Linux. I am not concerned with compiler compatibility, just one compiler per platform.

I am not looking for alternatives to properties that require parenthesis to get or set the property, such as overloaded methods separating the getters and setters.

Here is an ideal usage which compiles in 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;
}

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(4

就是爱搞怪 2024-11-10 09:52:19

这与您所要求的类似,并且是(我希望)标准 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;
}

正如您从 main 中看到的,只要您分配 T 类型的值,用法就是透明的(此处为 int)或隐式转换为 T 属性,只要您在读取时将它们转换回 T 值即可。

但是,如果您将 foo.x 传递给模板函数,行为将会有所不同,因为 foo.x 的类型不是 int 而是 < code>Property代替。

您也可能会遇到非模板函数的问题...调用接受 T 值的函数可以正常工作,但是 T& 参数将是一个问题是因为基本上该函数要求变量直接使用地址进行访问。出于同样的原因,您当然不能将属性的地址传递给接受 T* 参数的函数。

This is something similar to what you are asking and is (I hope) standard 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;
}

As you can see from main the usage is transparent as long as you are assigning values of type T (here int) or implicitly convertible to T to properties and as long you are converting them back to T values on reading.

Behavior will be different however if you for example pass foo.x to a template function because the type of foo.x is not int but Property<Foo, int, ...> instead.

You can also have problems with non-template functions... calling a function accepting a T value will work fine, however a T& parameter is for example going to be a problem because basically the function is asking a variable to access directly using the address. For the same reason you cannot pass of course the address of a property to a function accepting a T* parameter.

花开柳相依 2024-11-10 09:52:19

Clang 现在已经完全实现了 Microsoft __declspec(property...) 并且优化得非常漂亮。 因此,您可以在所有平台上的 C++ 中使用属性,并在基于 gcc 或 c99 代码等中混合使用。

我已经使用它一年多了,并等待它的到来普遍出现五年多了。

它是用于抽象结构和重构代码的最强大的 C++ 工具之一。我一直使用它来快速构建结构,然后根据性能或重组需要对其进行重构。

它是无价的,我真的不明白为什么 C++ 标准很久以前没有采用它。但话又说回来,他们使用 C++ 和模板的方式非常复杂且臃肿。

Clang 现在可以跨所有平台移植,拥有此功能真是太棒了。

使用 clang 在 Visual Studio(免费或付费版本)中进行开发几乎是无缝的,并且您可以获得令人难以置信的调试开发工具集,相比之下,这些工具集和平台上的工作只会变得痛苦。

我现在只使用 clang 进行所有 C++ 开发。

另请参阅:此交叉引用帖子

Clang now has the Microsoft __declspec(property...) fully implemented and it optimizes beautifully. So you can use properties in your c++ across all platforms and intermix in gcc based or c99 code etc.

I have been using it for over a year, and waited for this to appear universally for more than five years.

It is one of the most powerful C++ tools for abstracting structure and refactoring code. I use it all the time to allow me to quickly build a structure and then refactor it later as performance or restructuring requires it.

It is invaluable and I really don't understand why the C++ standards have not adopted it long ago. But then again, they have so much of the complex and bloated boost way of using c++ and templates.

Clang is so portable across every platform now that having this feature is fantastic.

Development within (free or paid version of) Visual Studio using clang is almost seamless and you get the incredible debugging development toolset that just makes working on other toolsets and platforms painful by comparison.

I exclusively use clang now for all my c++ development.

See also: this cross-reference post

伏妖词 2024-11-10 09:52:19

我正在寻找一款便携式的
相对干净的声明方法
语法糖化的属性
将在最新的编译器中编译
适用于 Windows、OSX 和 Linux。

您正在描述“元对象”类型的功能,例如编译时或运行时定义的属性,例如那些可以通过“Java beans”或“.NET 反射”或任何多种具有高性能的方式实现的属性。 - 级脚本语言,例如 Python 和 Perl。

例如,您所描述的内容(编译时和/或运行时属性)是通过 QMetaObject 在 Qt (C++) 库中实现的。您可以直接实例化它,将其用作类中的“成员”,或者从 QObject 派生以“自动”获取元对象行为(以及其他一些东西,例如“强制转换”帮助,和信号/槽跨线程)。当然,这些都是跨平台的(例如,Win、Mac、Posix)。

我不太喜欢 __declspec() 用法,除了非常特定于平台的用法,例如通过“Microsoft Extension DLL”显式导出类型(我通常会尽量避免,如果可能的)。我认为没有任何方法可以使这种用法“跨平台”(因为该特定用法特定于 MS DLL)。

同样,编写自己的“MyMetaObject”类型类也不是很困难,它本质上是一个“字典”或“散列”或“关联数组”,您的对象使用它,并且它是在运行时动态填充,即使使用内部类型(例如 MyColorMyTimeMyFilePath 等)我已经这样做过好几次了,而且不需要做很多工作,而且可以非常优雅地工作。 (QMetaObject 通常比这些简单的方法强大得多,但它需要“moc”编译步骤,这是生成快速查找代码的非常强大的步骤了解其属性,并启用信号/槽)。

最后,您将开始稍微接触“动态 C++”领域,这意味着 C++ 语法的更轻、几乎类似脚本的用法。这里有一个提案深入探讨了这种动态用法,您可以使用这些属性编写脚本,而无需重新编译。 (这个特定的提案恰好基于QMetaObject类型行为,但还有其他具有类似使用思想的提案):

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

如果您搜索“Dynamic C++”或“C++ Scripting”,您可能会得到更多信息想法。其中一些东西中有一些邪恶而聪明的想法。

I am looking for a portable and
relatively clean method of declaring
syntactically sugared properties that
will compile in the latest compilers
for Windows, OSX and Linux.

You're describing "meta-object" type capabilities, like compile-time or run-time defined properties, such as those that may be otherwise implemented through "Java beans" or ".NET reflection", or any number of ways with high-level scripting languages, like Python and Perl.

For example, what you're describing (compile-time and/or run-time properties) is implemented in the Qt (C++) libraries through the QMetaObject. You can instantiate it directly, use it as a "member" in your classes, or derive from QObject to "automatically" get that meta-object behavior (and some other things, like "casting" helps, and signals/slots cross-threads). Of course, these are quite cross-platform (e.g., Win, Mac, Posix).

I'm not a big fan of the __declspec() usage, except for very platform-specific use, such as explicit exporting of types through a "Microsoft Extension DLL" (which I generally try to avoid if possible). I don't think there's any way to make such usage "cross-platform" (since that particular usage is specific to MS DLLs).

Similarly, it wouldn't be very difficult to write your own "MyMetaObject" type class that is essentially a "dictionary" or "hash" or "associative array", which your objects use, and which is populated dynamically at runtime, even with your internal types (such as MyColor, MyTime, MyFilePath, etc.) I've done that several times, and it need not be lots of work, and it can work quite elegantly. (The QMetaObject is typically quite a bit more powerful than these simple approaches, but it requires the "moc" compilation step, which is a very powerful step to generate fast lookup code for its properties, and to enable signals/slots).

Finally, you're starting to touch lightly into the "Dynamic C++" domain, which implies lighter, almost script-like usage of C++ syntax. Here's one proposal that goes into a bit of depth about this dynamic usage, where you script with these properties, not needing to re-compile. (This particular proposal happens to be based on the QMetaObject type behavior, but there are other proposals with similar usage thoughts):

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

If you google "Dynamic C++" or "C++ Scripting", you might get some more ideas. There's some wickedly clever thoughts in some of that stuff.

沉默的熊 2024-11-10 09:52:19

我喜欢 6502 的答案。它比我将介绍的解决方案使用更少的内存并且速度更快。只有我的会有一点语法糖。

我希望能够写出这样的东西(使用 PIMPL 习惯用法):

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

public:
    A();
    ~A();

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

这是完整的代码(我很确定它符合标准):

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(); }
};    

以及一些实现细节:

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;
}

我正在为任何想了解 UnicodeString 类的人使用 C++ Builder 。
希望它可以帮助其他人尝试符合标准的 c++ 属性。
基本机制与 6502 相同,具有相同的限制。

I like the answer of 6502. It uses both less memory and is faster than the solution i will present. Only mine will have a bit syntactic sugar.

I wanted to be able to wite something like this (with PIMPL idiom):

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

public:
    A();
    ~A();

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

Here comes the completet code (I am quite sure it is standard conformant):

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(); }
};    

And some implementation details:

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;
}

I am using C++ Builder for anyone who wonders about the UnicodeString class.
Hope it helps others for experimentation of Standard conforming c++ Properties.
The basic mechanism is the same as 6502, with the same limitations.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文