Showing posts with label programming. Show all posts
Showing posts with label programming. 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.

Monday, October 27, 2014

Strip Invalid Identifier Characters

I don't do much Delphi anymore (any really) but this has been hanging around in my inbox for a while so I cleaned it up. This is a simple function that is a modification of IsValidIdent to strip invalid characters from a string.

function StripInvalidCharacters(const Ident: string): string;

  function Alpha(C: Char): Boolean; inline;
  begin
    Result := TCharacter.IsLetter(C) or (C = '_');
  end;

  function AlphaNumeric(C: Char): Boolean; inline;
  begin
    Result := TCharacter.IsLetterOrDigit(C) or (C = '_');
  end;

var
  I, L: Integer;
begin
  L := Length(Ident);
  if (L > 0) and Alpha(Ident[1]) then
    Result := Ident[1];

  for I := 2 to L do
    if AlphaNumeric(Ident[I]) then

  Result := Result + Ident[I];
end;

var
  LocalVal: string;
  LocalHash: Integer;
begin
  if IsValidIdent(Value) then
  LocalVal := StripInvalidCharacters(Value)
else

Tuesday, October 21, 2014

Simplicity

A simple design always takes less time to finish than a complex one. Maybe not always. But it should. So always do the simplest thing that could possibly work. If you find something that is complex replace it with something simple. It's always faster and cheaper to replace complex code now, before you waste a lot more time on it. Keep things as simple as possible as long as possible by never adding functionality before it is scheduled. Beware though, keeping a design simple is hard work.

Simplicity!

Thursday, October 16, 2014

GDB

Oh GDB. I have not much to say. You are there.

The most useful GDB command is printing a string:

(gdb)p /c *str@20

http://www.yolinux.com/TUTORIALS/GDB-Commands.html