Static classes/single objects and constructor code.

In some cases, static objects are desired, e.g., if we a modelling a specific single entity in terms of an object (say a part of the hardware...).

This is solved somewhat differently in between languages. In C# it is possible to refer to an object without having declared an instance to the object. However, as the object ay contain state, it is implicitly created.
(This raises the question on when the constructor code is called, but more on that later).

Some languages allow creating multiple instances, which all refer to the same instance (what about the constructor code in this case... confusing maybe ...)

In -cOOre, the class arguments amounts to bindings of identifiers to primitive types, tasks, and methods, made available in the scope of the object (i.e., to the methods and functions belonging to the object).

In the following we will discuss a candidate solution for -cOOre (you may implement this, or invent your own.)

Let us look at and example and break it down to pieces, ExtTest6.coore:
// ExtTest6.coore
// Per Lindgren (C) 2014
//
// State access in -core binding

class P()  {
  int a := 0;

  void f(int i) {
    RT_printf("a %d, i %d\n", a, i);
    a := a + 1;
  }
}

static class S2() extern "ExtS2_6.core" {
   int j := 0;
   P() p;
   
   extern void k(int);
   void q(int a, int b) {
     RT_printf("a %d, b %d\n", a, b);
     p.f(54);
     p.f(54);
   }
}

class O(int cv) extern "ExtO6.core" {
  int ci := cv + 5;

  extern Task t(int);
  extern void f(int);
}

static class S() {
  const int s := 3;
  int i := 7;
  static S2; 
  O(1) o; 

  void f() {
    S2.q(s, i);
    S2.k(100);
    async o.t(12);
  } 
}

class Root() {
  static S, S2;
  
  Reset {
    S.f();
    S.f();
    
    S2.q(1, 2);
    S2.k(101);  
  }
}


We find that class P() is an ordinary class.
class P()  {
  int a := 0;

  void f(int i) {
    RT_printf("a %d, i %d\n", a, i);
    a := a + 1;
  }
}


Class S2() follows the C# way of defining the complete class as static. (We opt not to go the partly static way, a la Java, see later discussion of pros and cons...). We see also that a static class can refer to external -core code.

static class S2() extern "ExtS2_6.core" {
   int j := 0;
   P() p;
   
   extern void k(int);
   void q(int a, int b) {
     RT_printf("a %d, b %d\n", a, b);
     p.f(54);
     p.f(54);
   }
}

The method q synchronously invokes f on p (twice to showcase the side-effect inside p).

We have the corresponding -core file:
// ExtS2_6.core
// Per Lindgren (C) 2014
//

Func void k(int v) {
  claim {
      #>
      <#_STATE_#>j = <#_STATE_#>j + v;
      printf("core Func S2.k: arg v=%d state i=%d\n", v, <#_STATE_#>j); 
      <#
  } 
}


O is an ordinary class with external -core implementation.
class O(int cv) extern "ExtO6.core" {
  int ci := cv + 5;

  extern Task t(int);
  extern void f(int);
}

With the corresponding -core code:
// ExtO6.core
// Per Lindgren (C) 2014
//

Func void f(int v) {
  claim {
      #> printf("core Func O.f: arg v=%d, class arg cv=%d, state ci=%d\n", v, <#_STATE_#>cv, <#_STATE_#>ci); <#
  } 
}

Task t(int v) {
  claim {
      #> printf("core Task O.f: arg v=%d, class arg cv=%d, state ci=%d\n", v, <#_STATE_#>cv, <#_STATE_#>ci); <#
  } 
}



S is another static class, with an internal reference to the static class S2 (as well as an inner instance of O).
static class S() {
  const int s := 3;
  int i := 7;
  static S2; 
  O(1) o; 

  void f() {
    S2.q(s, i);
    S2.k(100);
    async o.t(12);
  } 
}

Notice, here we gave static S2, to indicate that we are referring to the static class S2. (This may be omitted, see later discussion.)

And finally, the Root class:
class Root() {
  static S, S2;
  
  Reset {
    S.f();
    S.f();
    
    S2.q(1, 2);
    S2.k(101);  
  }
}

Since it refers to both S and S2, we give static S, S2.

The expected behaviour is that we get single instances of S and S2, where S2 holds a single instance of P even though we refer to it both from S and the Root.

The example also showcase that external bindings can be made arbitrarily in objects, even those instantiated as a singleton, or themselves being a singleton.

The output is as follows:
PTCORE run-time options :
-----------------------------------------------------------------------------------------------------------------------
a 3, b 7
a 0, i 54
a 1, i 54
core Func S2.k: arg v=100 state i=100
a 3, b 7
a 2, i 54
a 3, i 54
core Func S2.k: arg v=100 state i=200
a 1, b 2
a 4, i 54
a 5, i 54
core Func S2.k: arg v=101 state i=301
-----------------------------------------------------------------------------------------------------------------------
core Task O.f: arg v=12, class arg cv=1, state ci=6
core Task O.f: arg v=12, class arg cv=1, state ci=6

Discussion.

There are some important design decisions taken on there road. A more detailed discussion will follow:
... to be continued...


Per Lindgren, founder of RTFM, October 2014.

Last edited Oct 7, 2014 at 8:00 AM by RTFMPerLindgren, version 2