Author Topic: Light-weight classes  (Read 1850 times)

bas

  • Jr. Member
  • **
  • Posts: 89
    • View Profile
Light-weight classes
« on: April 28, 2016, 09:38:34 PM »
There was recently a discussion on Embedded.com (http://www.embedded.com/electronics-blogs/break-points/4441819/Fixing-C).
On of the often heard ideas was C++, but without all the fluff. But something like lightweight classes.

C++ has (multiple)inheritance, no real interfaces (like Java), operator overloading, method overloading etc etc
If you just want the basics, eg a struct with functions, that should be possible to create. It's just syntactic sugar,
s
Code: [Select]
func void MyStruct::myfunction() {..}
just becomes:
Code: [Select]
func void myfunction(MyStruct* A) {..}

Of course, when you also want interfaces, it becomes harder.
And when you also want to use the same function name with different parameter types, it again becomes harder..

My conclusion is that any 'lightweight' class construction will quickly lead to a lot of fluff, because people except
all those features in languages with classes. So it might just be easier to just leave them out altogether...

ckaygusu

  • Newbie
  • *
  • Posts: 2
    • View Profile
Re: Light-weight classes
« Reply #1 on: April 30, 2016, 10:03:48 PM »
"Lightweight class" concept can be realized without introducing any new syntax, but for inheritance and interfaces to be convenient, I believe we need to come up with something new.

I'll briefly explain a quick and dirty little design I come up with:

Functions that is declared with the signature having it's first element a reference (pointer or not) to a structure that is defined in the same module or file are allowed to be called with the dot operator.

Code: [Select]
type Person struct {
     int age;
     char* name;
}

func void sayname(Person* person) {
     io.printf("My name is %s", person.name);
}

// Which then allows a call like this:
Person* p = { 10, "Cengiz" };
p.sayname();

Inheritance is as follows, assuming we allow anonymous nested structs (refer http://stackoverflow.com/a/1237302).

Code: [Select]
type Creature struct {
     int age;
}

func void sayAge(Creature* creature) {
    io.printf("My age is %s", creature.age);
}


type Person struct {
    struct Creature;  // anonymous struct
    char* name;
}

Person* p = { 10, "Cengiz" };
p.sayage();  // prints "My age is 10"

io.printf("Person with name %s is %d years old.", p.name, p.age);
// And no, I'm not 10 years old :)


With this scheme, even multiple inheritance should be possible.

Interfaces can be realized by using empty structs.

Code: [Select]
type IMyInterface struct {

}

func void foo(IMyInterface* intf) {
    // Call a builtin function that says this method should be overridden and halts the program.
}


type MyStruct struct {
    struct IMyInterface;
    int bar;
}

func void foo(MyStruct* strc) {
    io.printf("I have proudly implemented the interface");
}

MyStruct* strc = { 10 };
IMyInterface* intf = strc;
intf.foo();  // prints "I have proudly implemented the interface"

Regarding the last bit, we may introduce new syntax that allows anonymously allocating structs.

Code: [Select]
IMyInterface* intf = <MyStruct> { 10 };
intf.foo();

I'll later post what can go wrong with this design.

ckaygusu

  • Newbie
  • *
  • Posts: 2
    • View Profile
Re: Light-weight classes
« Reply #2 on: May 03, 2016, 06:11:08 PM »
I've thought about this issue.

Implementing "lightweight classes" by just sugaring syntax seems doable. Only obstacle seems to be struct field shadowing, as demonstrated in the following;

Code: [Select]
type MyStruct struct {
    int foo;
}

func void foo(MyStruct* mstr {
    printf("I have calleth");
}

MyStruct mstr = { 10 };
mstr.foo()  // foo resolves to both the method and the field.

In my opinion, this is trivial and it could be easily detected at compile-time.

About inheritance though, it implies resolving method calls at runtime which I think is unacceptable.

Code: [Select]

type IMyInterface struct {

}

func void foo(*IMyInterface myint) {
     // show message and terminate program
}


type MyStruct_1 struct {
     int bar;
}

func void foo(MyStruct_1 *mystr1 {
    printf("Number ONE reporting");
}


type MyStruct_2 struct {
    int barrer;
}

func void foo(MyStruct_2 *mystr2) {
    printf("Number TWO reporting");
}

void test_1() {
     IMyInterface *myint;
     MyStruct_1 mystr = { 10 };
     myint = (*MyInterface) &mystr;
     myint.foo();  // This call can be resolved at compile-
}

void test_2(int flag) {
    IMyInterface *myint;
    if (flag) {
         MyStruct_1 mystr = { 10 };
         myint = (*IMyInterface) &mystr;
    } else {
        MyStruct_2 mystr = { 10 };
        myint = (*IMyInterface) &mystr;
    }
    myint.foo();  // This call CANNOT be resolved at compile time. Implicit code must run on runtime.
}


My final conclusion is, we can proceed with lightweight class syntax I described in the previous post. But for the time being, let's not implement inheritance.
« Last Edit: May 04, 2016, 03:20:44 PM by ckaygusu »

kyle

  • Newbie
  • *
  • Posts: 48
    • View Profile
Re: Light-weight classes
« Reply #3 on: December 22, 2016, 01:17:53 AM »
(and still catching up...)

Interesting points.

I tend to like having a limited form of classes.   I programmed in "C with classes" briefly back in my college days.  It was simple and clear and easy to understand.

Go seems to hit a lot of the benefits of "OO"-style programming without really implementing anything special.  One of the things I like about it is that a function and a method are NOT shown in the same syntax. 

I like this.