Showing posts with label Code. Show all posts
Showing posts with label Code. Show all posts

Tuesday, April 28, 2015

Properties in C++ Part II



Last week I posed about making properties in C++. That code sample had some issues that I fine tuned with the code sample in this post. Here is the new class(s).

template <typename ObjectType, typename ValueType, ValueType (ObjectType::*getter)(void), void (ObjectType::*setter)(ValueType)>
class Property {
private:
    ObjectType* FObject;
    
public:
    Property() {
        FObject = NULL;
    }
    
    void SetInstance(ObjectType* Value) {
        FObject = Value;
    }
    
    // To set the value using the set method.
    ValueType operator =(const ValueType& Value) {
        assert(FObject != NULL);
        (FObject->*setter)(Value);
        return Value;
    }
    
    // The Property class is treated as the internal type.
    operator ValueType() {
        assert(FObject != NULL);
        return (FObject->*getter)();
    }
};

template <typename ObjectType, typename ValueType, ValueType (ObjectType::*getter)(void)>
class ReadProperty {
private:
    ObjectType* FObject;
    
public:
    ReadProperty() {
        FObject = NULL;
    }
    
    void SetInstance(ObjectType* Value) {
        FObject = Value;
    }
    
    // The Property class is treated as the internal type.
    operator ValueType() {
        assert(FObject != NULL);
        return (FObject->*getter)();
    }
};

template <typename ObjectType, typename ValueType, void (ObjectType::*setter)(ValueType)>
class WriteProperty {
private:
    ObjectType* FObject;
    
public:
    WriteProperty() {
        FObject = NULL;
    }
    
    void SetInstance(ObjectType* Value) {
        FObject = Value;
    }
    
    // To set the value using the set method.
    ValueType operator =(const ValueType& Value) {
        assert(FObject != NULL);
        (FObject->*setter)(Value);
        return Value;
    }
};

template <typename ObjectType, typename ValueType, ValueType (ObjectType::getter)(void), void (ObjectType::setter)(ValueType)>
class StaticProperty {
private:
    
public:
    StaticProperty() {
    }
    
    // To set the value using the set method.
    ValueType operator =(const ValueType& Value) {
        (*setter)(Value);
        return Value;
    }
    
    // The Property class is treated as the internal type which is the getter.
    operator ValueType() {
        return (*getter)();
    }
};

template <typename ObjectType, typename ValueType, ValueType (ObjectType::getter)(void)>
class StaticReadProperty {
private:
    
public:
    StaticReadProperty() {
    }
    
    // The Property class is treated as the internal type which is the getter.
    operator ValueType() {
        return (*getter)();
    }
};

template <typename ObjectType, typename ValueType, void (ObjectType::setter)(ValueType)>
class StaticWriteProperty {
private:
    
public:
    StaticWriteProperty() {
    }
    
    // To set the value using the set method.
    ValueType operator =(const ValueType& Value) {
        (*setter)(Value);
        return Value;
    }
};

As you can see there are several classes. I moved the read/write/readwrite enum from being a field of the property class to being a class. I also added static versions. Oh, why would one want a static property? Well, here are a couple examples of using these:

class Environment {
public:
    Environment() {
        PropertyTest.SetInstance(this);
    }
    
// Property
private:
    std::string FPropertyTest;
    
public:
    void SetPropertyTest(std::string Value) { FPropertyTest = Value; }
    std::string GetPropertyTest() { return FPropertyTest; }
    
    Property<Environmentstd::string, &Environment::GetPropertyTest, &Environment::SetPropertyTest> PropertyTest;

// Static Property
private:
    static std::string FTest;

public:
    static void SetTest(std::string Value) { FTest = Value; }
    static std::string GetTest() { return FTest; }
    
    static StaticProperty<Environmentstd::string, &Environment::GetTest, &Environment::SetTest> Test;
};

StaticProperty<Environmentstd::string, &Environment::GetTest, &Environment::SetTest> Environment::Test;
std::string Environment::FTest = "foo";


There are a few limitations still. For example, the dot (.) operator cannot be overridden. So doing:

printf("%s\n", Environment::Test.data());

isn't going to work. The arrow (->) operator can be overridden, so this behavior could be supported that way if one was so inclined. Seems a little odd. I could go either way.

Monday, April 20, 2015

Properties in C++

A long time ago when I was working on Borland C++ and Delphi was just a baby, we added properties as a language extension to C++. Nobody at the C++ committee liked the notion (not sure why, but Java didn't like it either), but I came up with a way of doing properties in C++ using templates. Here is the property class:

enum PropertyType {READ_ONLY, WRITE_ONLY, READ_WRITE};

template <typename Container, typename ValueType, PropertyType Type>
class Property {
private:
    Container* FObject;
    void (Container::*FSetter)(ValueType value);
    ValueType (Container::*FGetter)();
    
public:
    Property() {
        FObject = NULL;
        FSetter = NULL;
        FGetter = NULL;
    }
    
    void InitializeSetterGetter(Container* Value,
                                void (Container::*Setter)(ValueType Value),
                                ValueType (Container::*Getter)()) {
        assert(Type == READ_WRITE);
        FObject = Value;
        FSetter = Setter;
        FGetter = Getter;
    }

    void InitializeSetter(Container* Value,
                          void (Container::*Setter)(ValueType Value)) {
        assert(Type == WRITE_ONLY);
        FObject = Value;
        FSetter = Setter;
    }

    void InitializeGetter(Container* Value,
                          ValueType (Container::*Getter)()) {
        assert(Type == READ_ONLY);
        FObject = Value;
        FGetter = Getter;
    }
    
    // To set the value using the set method.
    ValueType operator =(const ValueType& Value) {
        assert(FObject != NULL);
        assert(FSetter != NULL);
        (FObject->*FSetter)(Value);
        return Value;
    }
    
    // The Property class is treated as the internal type.
    operator ValueType() {
        assert(FObject != NULL);
        assert(FGetter != NULL);
        return (FObject->*FGetter)();
    }

};

Now, to use this, call one of the Initializer* functions from within your classes constructor, provide getter and setter methods and instantiate the property as follows:

class TestProperty {
protected:
    std::string FMyProperty;
    
public:
    TestProperty() {
        MyProperty.InitializeGetter(this,
                                    &TestProperty::SetMyProperty
                                    &TestProperty::GetMyProperty);
    }

    std::string GetMyProperty() {
        return FMyProperty;
    }

    void SetMyProperty(std::string Value) {
        FMyProperty = Value
    }
    
    Property<Process, std::string, READ_WRITEMyProperty;
};

It's a bit more work than if you were to use a language that supports properties such as Delphi or C#, but it can be a very handy syntactic sugar.

Wednesday, February 18, 2015

C++ Ordered Map

The std::map class is not ordered. Coming from any other programming language with a nice standard library this seems like a serious hole in C++. Boost has one, but not everyone can use Boost. So, here is one a threw together. Sure it's missing things, but that's what you get for free. Enjoy!

#ifndef ORDEREDMAP_H
#define ORDEREDMAP_H

#include <map>
#include <vector>
#include <assert .h="">


template <typename _t1="" _t2="" typename="">
struct pair
{
    typedef _T1 first_type;
    typedef _T2 second_type;
    
    first_type first;
    second_type second;
    
    pair(first_type Value1, second_type Value2) {
        first = Value1;
        second = Value2;
    }
};

template <typename tkey="" tvalue="" typename="">
class OrderedMap {
public:
    typedef TKey key_type;
    typedef TValue mapped_type;
    typedef pair<key_type mapped_type=""> container_type;
    
private:
    typedef std::map<key_type container_type=""> map_type;
    typedef std::vector<container_type> list_type;
    
    map_type FMap;
    list_type FList;
    
    typename list_type::iterator FindListItem(const key_type Key) {
        typename list_type::iterator result = FList.end();
        
        for (typename list_type::iterator iterator = FList.begin(); iterator != FList.end(); iterator++) {
            container_type *item = *iterator;
            
            if (item->first == Key) {
                result = iterator;
                break;
            }
        }
        
        return result;
    }
    
public:
    OrderedMap() {
    }
    
    ~OrderedMap() {
        for (typename list_type::iterator iterator = FList.begin(); iterator != FList.end(); iterator++) {
            container_type *item = *iterator;
            delete item;
        }
    }
    
    void Append(key_type Key, mapped_type Value) {
        container_type *item = new container_type(Key, Value);
        item->first = Key;
        item->second = Value;
        
        FMap.insert(std::pair<key_type container_type="">(Key, item));
        FList.push_back(item);
    }
    
    void Insert(size_t Index, key_type Key, mapped_type Value) {
        container_type *item = new container_type(Key, Value);
        item->first = Key;
        item->second = Value;
        
        FMap.insert(std::pair<key_type container_type="">(Key, item));
        FList.insert(FList.begin() + Index, item);
    }
    
    void Remove(key_type Key) {
        typename list_type::iterator iterator = FindListItem(Key);
        
        if (iterator != FList.end()) {
            FMap.erase(Key);
            FList.erase(iterator);
        }
    }
    
    void Remove(size_t Index) {
        typename list_type::iterator iterator = FList.begin() + Index;
        
        if (iterator != FList.end()) {
            container_type* item = *iterator;
        
            if (item != NULL) {
                FMap.erase(item->first);
                FList.erase(iterator);
            }
        }
    }
    
    mapped_type &operator[](key_type Key) {
        container_type* item = FMap[Key];
        assert(item != NULL);
        
        if (item != NULL) {
            return item->second;
        }
        
        throw std::out_of_range("Index out of range");
    }
    
    mapped_type &operator[](size_t Index) {
        assert(Index >= 0 && Index < Count());
        container_type* item = FList[Index];
        assert(item != NULL);
        
        if (item != NULL) {
            return item->second;
        }
        
        throw std::out_of_range("Index out of range");
    }
    
    size_t Count() {
        return FList.size();
    }
};

#endif //ORDEREDMAP_H

Update: March, 25 2015 - The code was not escaped properly and didn't show up right. Now it should be better.