C # -подобные свойства в нативном C ++? - PullRequest
21 голосов
/ 19 ноября 2010

В C # / .NET вы можете сделать что-то вроде этого:

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

Однако приведенный выше код фактически не взаимодействует с текстовой строкой someThing.напрямую, он использует свойство get и set.Точно так же можно использовать свойства только для чтения.

Есть ли способ сделать что-то подобное в нативном C ++ ?(не C ++ .NET)

Ответы [ 10 ]

42 голосов
/ 19 ноября 2010

ВНИМАНИЕ: Это насмешливый ответ, и он ужасен !!!

Да, это возможно:)

template<typename T>
class Property
{
private:
    T& _value;

public:
    Property(T& value) : _value(value)
    {
    }   // eo ctor

    Property<T>& operator = (const T& val)
    {
        _value = val;
        return *this;
    };  // eo operator =

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

Тогдаобъявите свой класс, объявив свойства для ваших членов:

class Test
{
private:
    std::string _label;
    int         _width;

public:
    Test() : Label(_label)
           , Width(_width)
    {
    };

    Property<std::string> Label;
    Property<int>         Width;
};

и вызовите стиль C #!

Test a;
a.Label = "blah";
a.Width = 5;

std::string label = a.Label;
int width = a.Width;
19 голосов
/ 19 ноября 2010

Свойства .NET являются синтаксическим сахаром для реальных функций get и set, которые генерируются за кулисами (на самом деле они больше, чем синтаксический сахар, потому что свойства генерируются в полученном IL и могут использоваться с Reflection).Так что в C ++ вам нужно было бы явно написать эти функции, так как нет такого понятия, как свойство.

16 голосов
/ 19 ноября 2010

Предупреждаю: это не нативный C ++; только для Microsoft . Но вы можете использовать declspec(property):

struct S {
   int i;
   void putprop(int j) { 
      i = j;
   }

   int getprop() {
      return i;
   }

   __declspec(property(get = getprop, put = putprop)) int the_prop;
};

int main() {
   S s;
   s.the_prop = 5;    // THERE YOU GO
   return s.the_prop;
}

cf MSDN, declspec (свойство) .

7 голосов
/ 26 декабря 2013

Ответ Moo-Juice выглядит действительно круто, но имеет недостаток: вы не можете использовать эти свойства как обычные выражения типа T, как в C #.

Например,

  • a.text.c_str() не скомпилируется (‘class Property<std::basic_string<char> >’ has no member named ‘c_str’)
  • std::cout << a.text тоже не скомпилируется (template argument deduction/substitution failed)

Я бы предложилпосле улучшения template<typename T> class Property:

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

Затем вы можете получить доступ к членам свойства с помощью (), например:

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

И вы можете использовать свойство в выражениях, гдетип должен быть выведен:

std::cout << a.text();
2 голосов
/ 19 ноября 2010

Да, но это зависит от поставщика. У Microsoft есть declspec (собственность). Реализация в C ++ Builder немного более продвинута (через ключевое слово __property, специфичное для поставщика), поскольку вы можете иметь индексированные средства доступа (которые могут быть любого типа).

Также проверьте это (не полагаясь на ключевые слова поставщика): http://www.codeproject.com/KB/cpp/cpp_property_indexer.aspx

2 голосов
/ 19 ноября 2010

Свойство в .NET связано с get и / или set функцией-членом, так что на самом деле это просто синтаксический сахар. Самое близкое, что вы можете получить с C ++, это использовать перегрузку, чтобы присвоить получателю и установщику одно и то же имя:

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

Очевидно, вам все равно придется указать скобки для вызова:

someThing.text("blah");
String blah = someThing.text();
1 голос
/ 23 февраля 2012
#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 голосов
/ 26 августа 2018

Вероятно, лучшим вариантом в настоящее время является использование атрибута __declspec( property( get=get_func_name, put=put_func_name ) ) PropertyType PropertyName в Microsoft.

  • также поддерживается clang,
  • он преобразуется в ваш метод получения / установки при компиляции (не добавляет никаких новых переменных),
  • используется, это самая близкая вещь к реальному свойству (может получить доступ к свойству свойства ...).

Но если вы используете другие компиляторы, вы можете использовать макросы:

#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)

Затем используйте их как:

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

Вы также можете добавить другие варианты макросов для свойств только для чтения, только для записи и неконстантных методов получения только для чтения.Чтобы получить доступ к под-свойствам через ->, вы можете добавить оператор-> перегрузки в макрос.

По сравнению с __declspec (свойство (...)) в Microsoft методы getter и setter можно сделать закрытымино это не является реальным преимуществом, поскольку клиенту иногда может потребоваться получить адрес получателя / установщика.Недостатком является также наличие дополнительной переменной _parent для каждого свойства, и вам необходимо явно определить конструкторы копирования для родительских классов, если они используются.

0 голосов
/ 18 декабря 2014

Почему бы не использовать язык C # вместо C ++ для нативной разработки? Для этого вы можете использовать утилиту IL2BC для генерации собственного кода из исходного кода C # и / или байт-кода MSIL?

IL2BC можно найти на этом сайте

http://csnative.codeplex.com

0 голосов
/ 19 ноября 2010

Нет, нет.Вы бы просто создали функции получения и установки:

someThing.setText("blah");
std::string blah = someThing.getText();
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...