C#-like properties in native C++?

后端 未结 11 1867
南笙
南笙 2020-11-29 04:46

In C# / .NET you can do something like this:

someThing.text = \"blah\";
String blah = someThing.text;

However, the above code does not actu

相关标签:
11条回答
  • 2020-11-29 05:27

    In .NET properties are syntactic sugar for the real get and set functions which are emitted behind the scenes (in fact they are more than syntactic sugar because properties are emitted in the resulting IL and could be used with Reflection). So in C++ you would need to explicitly write those functions as there's no such notion as property.

    0 讨论(0)
  • 2020-11-29 05:30

    A property in .NET is associated with a get and/or a set member function, so it's really just syntactic sugar. The closest you can get with C++ is to use overloading to give the getter and setter the same name:

    const std::string &test() const { return text_; }
    void test(const std::string &value) { text_ = value; }
    

    Obviously, you will still have to provide parenthesis for the call:

    someThing.text("blah");
    String blah = someThing.text();
    
    0 讨论(0)
  • 2020-11-29 05:30

    Probably the best option currently is to use the microsoft's __declspec( property( get=get_func_name, put=put_func_name ) ) PropertyType PropertyName attribute.

    • it is also supported by clang,
    • it is converted into your getter/setter when compiled (won't add any new variables),
    • in use, it is the closest thing to a real property (can access property of a property...).

    But if you're using other compilers, you could use macros:

    #define PROPERTY_GEN(Class, Type, Name, GetMethod, SetMethod) \
        class Property_##Name { \
        public: \
            Property_##Name(Class* parent) : _parent(parent) { } \
            Type operator = (Type value) \
            { \
                _parent->SetMethod(value); \
                return _parent->GetMethod(); \
            } \
            operator Type() const \
            { \
                return static_cast<const Class*>(_parent)->GetMethod(); \
            } \
            Property_##Name& operator =(const Property_##Name& other) \
            { \
                operator=(other._parent->GetMethod()); return *this; \
            }; \
            Property_##Name(const Property_##Name& other) = delete; \
        private: \
            Class* _parent; \
        } Name { this };
    
    
        // PROPERTY - Declares a property with the default getter/setter method names.
        #define PROPERTY(Class, Type, Name) \
            PROPERTY_GEN(Class, Type, Name, get_##Name, set_##Name)
    

    Then use them like:

    class SomeClass
    {
    public:
        PROPERTY(SomeClass, int, Value)
        int get_Value() const { return _value; }
        void set_Value(int value) { _value = value; }
    
    private:
        int _value = 0;
    };
    
    
    int main()
    {
        SomeClass s, c;
        s.Value = 5;
        c.Value = 3 * s.Value;
        s.Value = c.Value;
    }
    

    You could also add other macro variants for read-only, write-only properties and read-only non-const getters. To be able to access sub-properties via ->, you could add operator-> overloads to the macro.

    Compared to microsoft's __declspec(property(...)), getter and setter methods can be made private but this isn't a real advantage since client might need to take the address of a getter/setter sometimes. There is also a disadvantage of having an additional _parent variable for every property, and you would need to explicitly define copy constructors for parent classes if they are used.

    0 讨论(0)
  • 2020-11-29 05:31

    Moo-Juice's answer looks really cool, but has a drawback: you can't use these properties like normal expressions of type T, as you can in C#.

    For instance,

    • a.text.c_str() won't compile (‘class Property<std::basic_string<char> >’ has no member named ‘c_str’)
    • std::cout << a.text won't compile either (template argument deduction/substitution failed)

    I would suggest the following enhancement to template<typename T> class Property:

    T& operator() ()
    {
        return _value;
    }
    T const& operator() () const
    {
        return _value;
    }
    

    Then you can access the property's members with (), such as:

     char const *p = a.text().c_str();
    

    And you can use the property in expressions where the type must be deduced:

    std::cout << a.text();
    
    0 讨论(0)
  • 2020-11-29 05:32
    #include <iostream>
    #include <string>
    
    using namespace std;
    
    // ------------------------------------------------------------------
    
    #define PROPERTY_GET_SET(CLASS, NAME, TYPE) GetSetProperty<CLASS, TYPE> NAME() { return GetSetProperty<CLASS, TYPE>(this, &CLASS::get_##NAME, &CLASS::set_##NAME); }
    #define PROPERTY_GET(CLASS, NAME, TYPE)     GetProperty<CLASS, TYPE> NAME()    { return GetProperty<CLASS, TYPE>(this, &CLASS::get_##NAME); }
    #define PROPERTY_SET(CLASS, NAME, TYPE)     SetProperty<CLASS, TYPE> NAME()    { return SetProperty<CLASS, TYPE>(this, &CLASS::set_##NAME); }
    
    template <typename CLASS, typename TYPE>
    struct GetSetProperty {
        typedef TYPE (CLASS::*Getter_t)() const;
        typedef void (CLASS::*Setter_t)(TYPE);
        GetSetProperty(CLASS* instance, Getter_t getter, Setter_t setter) : m_instance(instance), m_getter(getter), m_setter(setter) {}
        operator TYPE() const { return (this->m_instance->*this->m_getter)(); }
        GetSetProperty<CLASS, TYPE>& operator=(TYPE value) { (this->m_instance->*this->m_setter)(value); return *this; }
        CLASS* const   m_instance;
        const Getter_t m_getter;
        const Setter_t m_setter;
    };
    
    template <typename CLASS, typename TYPE>
    struct GetProperty {
        typedef TYPE (CLASS::*Getter_t)() const;
        GetProperty(CLASS* instance, Getter_t getter) : m_instance(instance), m_getter(getter) {}
        operator TYPE() const { return (this->m_instance->*this->m_getter)(); }
        CLASS* const   m_instance;
        const Getter_t m_getter;
    };
    
    template <typename CLASS, typename TYPE>
    struct SetProperty {
        typedef void (CLASS::*Setter_t)(TYPE);
        SetProperty(CLASS* instance, Setter_t setter) : m_instance(instance), m_setter(setter) {}
        SetProperty<CLASS, TYPE>& operator=(TYPE value) { (this->m_instance->*this->m_setter)(value); return *this; }
        CLASS* const   m_instance;
        const Setter_t m_setter;
    };
    
    template <typename CLASS, typename TYPE>
    ostream& operator<<(ostream& ostr, const GetSetProperty<CLASS, TYPE>& p) { ostr << (p.m_instance->*p.m_getter)(); return ostr; }
    
    template <typename CLASS, typename TYPE>
    ostream& operator<<(ostream& ostr, const GetProperty<CLASS, TYPE>& p) { ostr << (p.m_instance->*p.m_getter)(); return ostr; }
    
    // ------------------------------------------------------------------
    
    class Dummy
    {
    public:
    
        Dummy() : m_value1(42) {}
    
        PROPERTY_GET_SET(Dummy, Value1, int);
        PROPERTY_GET_SET(Dummy, Value2, const string&);
    
    protected:
    
        virtual int           get_Value1() const { return this->m_value1; }
        virtual void          set_Value1(int value) { this->m_value1 = value; }
    
        virtual const string& get_Value2() const { return this->m_value2; }
        virtual void          set_Value2(const string& value) { this->m_value2 = value; }
    
    private:
    
        int    m_value1;
        string m_value2;
    };
    
    
    int main(int argc, char* argv[]) {
    
        Dummy d;
    
        cout << d.Value1() << endl;
        d.Value1() = 3;
        cout << d.Value1() << endl;
    
        cout << d.Value2() << endl;
        d.Value2() = "test";
        cout << d.Value2() << endl;
    
        return 0;
    }
    
    // ------------------------------------------------------------------
    
    0 讨论(0)
提交回复
热议问题