开发者

Property like features in C++?

My use is pretty complicated. I have a bunch of objs and they are all passed around by ptr (not reference or value unless its an enum which is byval). At a specific point in time i like to call CheckMembers() which will check if each member has been set or is null. By default i cant make it all null because i wouldnt know if i set it to null or if it is still null bc i havent touch it since the ctor.

To assign a variable i still need the syntax to be the normal var = p; var->member = new Type;. I generate all the classes/members. So my question is how can i implement a property like feature where i can detect if the value has been set or left as the default?

I am 开发者_开发技巧thinking maybe i can use C++ with CLR/.NET http://msdn.microsoft.com/en-us/library/z974bes2.aspx but i never used it before and have no idea how well it will work and what might break in my C++ prj (it uses rtti, templates, etc).


Reality (edit): this proved to be tricky, but the following code should handle your requirements. It uses a simple counter in the base class. The counter is incremented once for every property you wish to track, and then decremented once for every property that is set. The checkMembers() function only has to verify that the counter is equal to zero. As a bonus, you could potentially report how many members were not initialized.

#include <iostream>

using namespace std;

class PropertyBase
{
    public:
        int * counter;
        bool is_set;
};

template <typename T>
class Property : public PropertyBase
{
    public:
        T* ptr;
        T* operator=(T* src)
        {
            ptr = src;
            if (!is_set) { (*counter)--; is_set = true; }
            return ptr;
        }
        T* operator->() { return ptr; }
        ~Property() { delete ptr; }
};

class Base
{
    private:
        int counter;
    protected:
        void TrackProperty(PropertyBase& p)
        {
            p.counter = &counter;
            counter++;
        }
    public:
        bool checkMembers() { return (counter == 0); }
};

class OtherObject : public Base { }; // just as an example

class MyObject : public Base
{
    public:
        Property<OtherObject> x;
        Property<OtherObject> y;
        MyObject();
};

MyObject::MyObject()
{
    TrackProperty(x);
    TrackProperty(y);
}

int main(int argc, char * argv[])
{
    MyObject * object1 = new MyObject();
    MyObject * object2 = new MyObject();

    object1->x = new OtherObject();
    object1->y = new OtherObject();

    cout << object1->checkMembers() << endl; // true
    cout << object2->checkMembers() << endl; // false

    delete object1;
    delete object2;

    return 0;
}


There are a number of ways to do this, with varying tradeoffs in terms of space overhead. For example, here's one option:

#include <iostream>

template<typename T, typename OuterClass>
class Property
{
public:
    typedef void (OuterClass::*setter)(const T &value);
    typedef T &value_type;
    typedef const T &const_type;
private:
    setter set_;
    T &ref_;
    OuterClass *parent_;
public:
    operator value_type() { return ref_; }
    operator const_type() const { return ref_; }

    Property<T, OuterClass> &operator=(const T &value)
    {
        (parent_->*set_)(value);
        return *this;
    }

    Property(T &ref, OuterClass *parent, setter setfunc)
        : set_(setfunc), ref_(ref), parent_(parent)
    { }
};


struct demo {
    private:
        int val_p;
        void set_val(const int &newval) {
            std::cout << "New value: " << newval << std::endl;
            val_p = newval;
        }

    public:
        Property<int, demo> val;

        demo()
            : val(val_p, this, &demo::set_val)
        { }
};

int main() {
    demo d;
    d.val = 42;
    std::cout << "Value is: " << d.val << std::endl;
    return 0;
}

It's possible to get less overhead (this has up to 4 * sizeof(void*) bytes overhead) using template accessors - here's another example:

#include <iostream>


template<typename T, typename ParentType, typename AccessTraits>
class Property
{
private:
    ParentType *get_parent()
    {
        return (ParentType *)((char *)this - AccessTraits::get_offset());
    }
public:
    operator T &() { return AccessTraits::get(get_parent()); }
    operator T() { return AccessTraits::get(get_parent()); }
    operator const T &() { return AccessTraits::get(get_parent()); }
    Property &operator =(const T &value) {
        AccessTraits::set(get_parent(), value);
        return *this;
    }
};

#define DECL_PROPERTY(ClassName, ValueType, MemberName, TraitsName) \
    struct MemberName##__Detail : public TraitsName { \
        static ptrdiff_t get_offset() { return offsetof(ClassName, MemberName); }; \
    }; \
    Property<ValueType, ClassName, MemberName##__Detail> MemberName;

struct demo {
    private:
        int val_;

        struct AccessTraits {
            static int get(demo *parent) {
                return parent->val_;
            }

            static void set(demo *parent, int newval) {
                std::cout << "New value: " << newval << std::endl;
                parent->val_ = newval;
            }
        };
    public:
        DECL_PROPERTY(demo, int, val, AccessTraits)

        demo()
        { val_ = 0; }
};

int main() {
    demo d;
    d.val = 42;
    std::cout << "Value is: " << (int)d.val << std::endl;
    return 0;
}

This only consumes one byte for the property struct itself; however, it relies on unportable offsetof() behavior (you're not technically allowed to use it on non-POD structures). For a more portable approach, you could stash just the this pointer of the parent class in a member variable.

Note that both classes are just barely enough to demonstrate the technique - you'll want to overload operator* and operator->, etc, as well.


Here's my temporary alternative. One that doesn't ask for constructor parameters.

#include <iostream>
#include <cassert>

using namespace std;

template <class T>
class Property
{
    bool isSet;
    T v;
    Property(Property&p) { }
public:
    Property() { isSet=0; }
    T operator=(T src) { v = src; isSet = 1; return v; }
    operator T() const { assert(isSet); return v; }
    bool is_set() { return isSet; }
};

class SomeType  {};
enum  SomeType2 { none, a, b};
class MyObject
{
public:
    Property<SomeType*> x;
    Property<SomeType2> y;
    //This should be generated. //Consider generating ((T)x)->checkMembers() when type is a pointer
    bool checkMembers() { return x.is_set() && y.is_set(); }
};

int main(int argc, char * argv[])
{
    MyObject* p = new MyObject();
    p->x = new SomeType;
    cout << p->checkMembers() << endl; // false
    p->y = a;
    cout << p->checkMembers() << endl; // true
    delete p->x;
    delete p;
}
0

上一篇:

下一篇:

精彩评论

暂无评论...
验证码 换一张
取 消

最新问答

问答排行榜