Topic: [C++0x or later] C# Properties, inner classes and CRTP


Author: stroumpf2004-gtalk@yahoo.de
Date: Fri, 15 Dec 2006 17:26:19 CST
Raw View
I want to propose and discuss the following new language features that,
as a side effect, allow an easy and flexible definition of C# like
properties (that is even better than C#) in C++ classes.

QUICK SUMMARY

I propose to introduce

- A new syntax for anonymous inner classes that derive from a base
class.

- The keyword "nested", that tells a nested class to store a hidden
reference to the enclosing object (like in java)

- The keyword "enclosing_object" using either the hidden reference, or
a mechanism equivalent to the "offsetof" macro trick described in 1.2
below. (possible alternative: "::this" ?)

- The keyword "fragmental", introducing a mechanism to replace the CRTP
(curiously recurring template pattern). Even more, fragmental classes
allow multiple inheritance without the usual bloat of the vtable
architecture.


TOC.

1) C# like Properties in classic C++.
1.1) Implementation using runtime polymorphism.
1.2) Implementation using CRTP and the offsetof Macro.

2) The proposed new language features.
2.1)  Anonymous classes derived from base class
2.2) The "nested" keyword.
2.3) The "enclosing_object" keyword.
2.4) The "fragmental" keyword - a syntactical replacement for the CRTP
mechanism.

3) C# like Properties using the new language features.

4) Other examples that benefit from the new language features.
4.1) A Simple State Machine
4.2) A nice Workaround for the lack of virtual function templates in
C++




================================================

1)  C# like Properties in classic C++
Consider the following two examples that try to implement C# like
properties with classic C++ syntax.

-------------------------------------------------------------

1.1)  Implementation using runtime polymorphism.

struct Float {
  virtual void set(float f)=0;
  virtual float get()=0;
  Float& operator += (float f) {
    set(get()+f);
    return *this;
  }
  Float& operator = (float f) {
    set(f);
    return *this;
  }
  ...
};

class C {
  float angle_;
 public:
  C() {angle.enclosing_object=this;}
  struct Angle : Float {
    C* enclosing_object;
    void set(float f) {
        while(f>=360) f-=360;
        while(f<0) f+=360;
        enclosing_object->angle_=f;
    }
    float get() {return enclosing_object->angle_;}
  } angle;
};

int main() {
  C c;
  c.angle = -45;  // ->  c.angle_ == 315
  c.angle += 180;  // ->  c.angle_ == 135
  c.angle += 270;  // ->  c.angle_ == 45
}

Problems:
- Performance overhead because of vtable lookup that is not nessecary
from a technical perspective.
- We need to store and take care of the "enclosing_object" pointer ->
memory overhead, and possibility of errors.

-------------------------------------------------------------

1.2)  Implementation using CRTP and the offsetof Macro.

template<class Derived> struct Float {
  Derived* derived() {
    return static_cast<Derived*>(this);
  }
  Float& operator += (float f) {
    derived()->set(derived()->get()+f);
    return *this;
  }
  Float& operator = (float f) {
    derived()->set(f);
    return *this;
  }
  ...
};

class C {
  float angle_;
 public:
  struct Angle : Float<C::Angle> {
    C* enclosing_object() {
      const static int offset = offsetof(C, angle);
      return
reinterpret_cast<C*>(reinterpret_cast<char*>(this)-offset);
    }
    void set(float f) {
        while(f>=360) f-=360;
        while(f<0) f+=360;
        enclosing_object()->angle_=f;
    }
    float get() {return enclosing_object()->angle_;}
  } angle;
};

int main() {
  C c;
  c.angle = -45;  // ->  c.angle_ == 315
  c.angle += 180;  // ->  c.angle_ == 135
  c.angle += 270;  // ->  c.angle_ == 45
}

Problem: This solution is quite efficient, but the code is not that
nice to read. Especially, we don't like to depend on CRTP and macros
for an everyday problem.



===========================================

2)  The new language features

I propose to introduce
- A new syntax for anonymous inner classes that derive from a base
class.
- The keyword "nested", that tells a nested class to store a hidden
reference to the enclosing object (like in java)
- The keyword "enclosing_object" using either the hidden reference, or
a mechanism equivalent to the "offsetof" macro trick described above.
- The keyword "fragmental", introducing a mechanism to replace the CRTP
(curiously recurring template pattern). Even more, fragmental classes
allow multiple inheritance without the usual bloat of the vtable
architecture.

You don't need to like the wording, at this point *g*.
I was talking about two, not four new features, because I think the
first three of them should actually be seen as one.

-------------------------------------------------------------

2.1)  Anonymous classes derived from base class
In classic C++, anonymous classes cannot derive from a base class.

struct Base {};

class C {

  struct {
  } m0, m1, m2;  // no base class

  struct M : Base {
  } m0, m1, m2;  // not anonymous
};

I propose to allow the following syntax.

struct Base {};

class C {
  Base m {
  };  // [*]. The (anonymous) type of member m is derived from class
Base
  Base {
  } m0, m1, m2;  // [**]. The (anonymous) type of members m0, m1, m2 is
derived from class Base
};

Currently this would be invalid code, so it will not break existing
programs.

2.2) The "nested" keyword. This keyword is used in a class or struct
definition, to indicate that there should be a hidden reference to the
enclosing object, that is initiated automatically, like in Java.

class C {
  nested struct M {};
};

-------------------------------------------------------------

2.3) The "enclosing_object" keyword. This keyword returns a pointer to
the enclosing object. Technically, this can work in two ways:
- for member classes that are defined as "nested", the
"enclosing_object" keyword will just return the hidden reference.
- for anonymous member classes defined in the first way (2.1 [*]), the
"enclosing_object" keyword uses a mechanic that is equivalent to the
offsetof macro trick. The anonymous definition guarantees a fixed
adress difference to the enclosing object.

struct Base {};

class C {

  void foo() {..}

  //-----------------------------------------------------------------
  // classic C++ inner class definitions

  nested struct M : Base {
    void foo() {enclosing_object->foo();}  // use the hidden reference
  };

  struct M : Base {
    void foo() {enclosing_object->foo();}  // error: M needs to be
declared 'nested'.
  };

  struct {
    void foo() {enclosing_object->foo();}  // uses adress difference of
C and m.
  } m;

  struct {
    void foo() {enclosing_object->foo();}  // uses adress difference of
C and m0.
  } m0, m1, m2;  // error: no more instances than m0 allowed, if you
use the enclosing_object keyword

  //-----------------------------------------------------------------
  // new way to define anonymous inner classes

  Base m {
    void foo() {enclosing_object->foo();}  // use the adress difference
of C and m
  };

  nested Base m {..};  // error: invalid use of keyword "nested" -
hidden reference would be redundant

  nested Base {
    void foo() {enclosing_object->foo();}  // use the hidden reference
  } x0, x1, x2;

  Base {
    void foo() {enclosing_object->foo();}  // use the adress difference
of C and x.
  } x;

  Base {
    void foo() {enclosing_object->foo();}  // use the adress difference
to x3.
  } x3, x4, x5;   // error: no more instances than x3 allowed, if you
use the enclosing_object keyword
};

An alternative to the keyword "enclosing_object" would be the syntax
::this == enclosing_object
::::this == enclosing_object->enclosing_object


-------------------------------------------------------------

2.4)  The "fragmental" keyword - a syntactical replacement for the CRTP
mechanism.
A fragmental class is not a type itself, but is used as a building
block to create new types by (multiple) inheritance. Similar to a CRTP
(curiously recurring template pattern), a fragmental class has
compile-time-access to (specified) methods of the child class.

In a class or struct definition ("fragmental class F {...};") the
keyword means
- It is not possible to use F, F& or F* as a type, except for methods
and members of F itself. There can be neither instances, nor
references, nor pointers.
- F can derive from any other classes or structs (fragmental or not),
and any other class or struct (fragmental or not) can derive from F,
with the usual restrictions.
- The compiler waits for a non-fragmental child class to derive from F,
before any methods are compiled.
- F derives the vtable pointers from all its base classes, but it does
not define any vtable pointer for its own virtual methods.

For any class C deriving from one or more classes (fragmental and
non-fragmental),
- Declarations in the fragmental base classes hide/override
declarations with the same name/signature in the non-fragmental base
classes.
- C stores only one vtable pointer for all the virtual methods defined
in fragmental base classes, or in C itself. For the rest of direct or
indirect base classes that are not fragmental, the compiler tries to
minimize the number of vtable pointers in C (as usual).
- For members of a fragmental base class F of C, any occurance of F, F&
or F* in this declaration is replaced by C, C& or C*.

For methods of a fragmental class F ("fragmental void foo()=0;") the
keyword means
- Any non-fragmental class deriving from F must give an implementation
of the method foo().
- Any other method in F can call the method foo(). If doing so, the
function call is redirected to the first implementation of foo() in a
non-fragmental class deriving (directly or indirectly) from F. Unlike
with virtual functions, this happens at compile time (and thus is
equivalent to a CRTP mechanism).



=========================================================

3) C# like Properties using the proposed new language features.

fragmental struct Float {
  fragmental void set(float f)=0;
  fragmental float get()=0;
  Float& operator += (float f) {
    set(get()+f);
    return *this;
  }
  Float& operator = (float f) {  // return type will be replaced by C&,
when deriving C from Float
    set(f);
    return *this;
  }
  ...
};

class C {
  float angle_;
 public:
  // anonymous inner class derived from 'Float', instantiated as member
'angle'
  Float angle {
    void set(float f) {
        while(f>=360) f-=360;
        while(f<0) f+=360;
        enclosing_object->angle_=f;  // using the adress difference of
C and angle
    }
    float get() {
      return enclosing_object->angle_;  // using the adress difference
of C and angle
    }
  };
};

int main() {
  C c;
  c.angle = -45;  // ->  c.angle_ == 315
  c.angle += 180;  // ->  c.angle_ == 135
  c.angle += 270;  // ->  c.angle_ == 45
}

This is much more efficient than 1.1, and much easier to read than 1.2
imo.



=========================================================

4) Other examples that benefit from the new language features.
Here is just two of them, but I'm sure more is possible.

-----------------------------------------------------------------

4.1)  A Simple State Machine
The new mechanisms allow the easy translation of a state machine into
code, using

struct SimpleStateMachine
{
    SimpleStateMachine() {current_state=state_A;}
    void step() {current_state->step();}

  private:
    struct State {
        virtual void step()=0;
    }  *current_state;

    State state_A {  // derived from struct State
        virtual void step() {
            enclosing_object->current_state=state_B;
        }
    };

    State state_B {  // derived from struct State
        virtual void step_v() {
            enclosing_object->current_state=state_A;
        }
    };
};

-----------------------------------------------------------------

4.2)  A nice Workaround for the lack of virtual function templates in
C++

template<
    typename T
> fragmental struct F_Interface_T {
    // overloading instead of function template
    virtual void foo_v(T)=0;
};

// multi-inheritance, but only one vtable pointer needed, thanks
fragmental classes
struct Interface : F_Interface_T<C0>, F_Interface_T<C1>,
F_Interface_T<C2>  {
    template<typename T> void foo() {foo_v(T);}
};

template<
    typename T
> fragmental struct F_Imp_T : public virtual Base {
    virtual void foo_v(T) {
        Final::foo_imp<T>();
    }
};

struct F_Imp : F_Imp_T<C0>, F_Imp_T<C1>, F_Imp_T<C2> {
};

// user code
struct MyClass : F_Imp {
    template<typename T> void foo_imp() {
        ... // implementation
    }
};

---
[ comp.std.c++ is moderated.  To submit articles, try just posting with ]
[ your news-reader.  If that fails, use mailto:std-c++@ncar.ucar.edu    ]
[              --- Please see the FAQ before posting. ---               ]
[ FAQ: http://www.comeaucomputing.com/csc/faq.html                      ]





Author: Mathias Gaunard <loufoque@remove.gmail.com>
Date: Tue, 19 Dec 2006 01:57:06 CST
Raw View
stroumpf2004-gtalk@yahoo.de wrote:

> 1.1)  Implementation using runtime polymorphism.
>
> struct Float {
>   virtual void set(float f)=0;
>   virtual float get()=0;
>   Float& operator += (float f) {
>     set(get()+f);
>     return *this;
>   }
>   Float& operator = (float f) {
>     set(f);
>     return *this;
>   }
>   ...
> };
>
> class C {
>   float angle_;

Did you mean Float?

>  public:
>   C() {angle.enclosing_object=this;}
>   struct Angle : Float {
>     C* enclosing_object;
>     void set(float f) {
>         while(f>=360) f-=360;
>         while(f<0) f+=360;
>         enclosing_object->angle_=f;
>     }
>     float get() {return enclosing_object->angle_;}
>   } angle;


Could have been

C() : angle(*this) {}

with

struct Angle : Float
{
     C& enclosing_object;

     Angle(C& c) : enclosing_object(c) { }

     /* ... */
} angle;

> };
>
> int main() {
>   C c;
>   c.angle = -45;  // ->  c.angle_ == 315
>   c.angle += 180;  // ->  c.angle_ == 135
>   c.angle += 270;  // ->  c.angle_ == 45
> }
>
> Problems:
> - Performance overhead because of vtable lookup that is not nessecary
> from a technical perspective.

You could remove that need if you forward the operators.
Or using CRTP.


> - We need to store and take care of the "enclosing_object" pointer ->
> memory overhead, and possibility of errors.

The possibility of errors can be almost nullified by using a reference
and not a pointer.

A very smart compiler could maybe optimize it using offsets directly,
but I saw none doing so.
Adding a way saying we're referencing the enclosing object could be
interesting to spare a few bytes.


> 2)  The new language features
>
> I propose to introduce
> - A new syntax for anonymous inner classes that derive from a base
> class.

This looks quite weird and I don't really see what it brings.


> - The keyword "nested", that tells a nested class to store a hidden
> reference to the enclosing object (like in java)
> - The keyword "enclosing_object" using either the hidden reference, or
> a mechanism equivalent to the "offsetof" macro trick described above.

The name of the variable referencing the enclosing object could be
declared by the user while declaring a member with a special keyword.

In that case, only one keyword would be required.

There is also ::this, which is quite interesting.
nested classes could also just work like closures.


> - The keyword "fragmental", introducing a mechanism to replace the CRTP
> (curiously recurring template pattern). Even more, fragmental classes
> allow multiple inheritance without the usual bloat of the vtable
> architecture.

That's something quite separate.
I'm not sure it's worth the effort though, CRTP is pretty ok.


> - For members of a fragmental base class F of C, any occurance of F, F&
> or F* in this declaration is replaced by C, C& or C*.

This is a bit weird and is clearer when using templates IMO.

---
[ comp.std.c++ is moderated.  To submit articles, try just posting with ]
[ your news-reader.  If that fails, use mailto:std-c++@ncar.ucar.edu    ]
[              --- Please see the FAQ before posting. ---               ]
[ FAQ: http://www.comeaucomputing.com/csc/faq.html                      ]