RAII, value classes and implicit conversion
Boris Kolar
boris.kolar at globera.com
Tue Nov 14 04:21:00 PST 2006
Proposed extensions, that should solve most RAII (and other) problems:
1 Structs
=========
Structs are a lot like classes, except they can NOT:
- have virtual methods
- override inherited methods
(technically: they can't have VMT table)
1.1 Immutable structs
---------------------
In addition to normal structs, immutable structs can be defined with:
const struct Point {
int x;
int y;
}
They can be constructed as:
void test() {
Point p = Point(10, 20); // x = 10, y = 20
Point p = Point(x: 10, y: 20); // same as above
p.x = 30; // COMPILER ERROR: Point is immutable
}
1.2 Structs can have constructor(s) and destructor
--------------------------------------------------
Also, structs may have constructor(s), destructor:
struct File {
this() {
printf("File.this");
}
this(char[] name) {
handle = openFile(name);
// handle can be modified only in constructor
}
~this() {
closeFile(handle);
}
HANDLE handle;
}
Invocation is deterministic (at the beginning and end of variable scope):
void test() {
File f; // prints "File.this" (default constructor is invoked)
// destructor of f is called here
}
1.3 Inheritance and implicit conversions of structs
---------------------------------------------------
Structs can inherit from one or more other structs:
struct A {
int a;
}
struct B {
int b;
}
struct C: A, B {
// inherited: int a;
// inherited: int b;
int c;
}
Structs (and classes too) can define implicit conversions:
// A may be struct, interface, class, ...
struct D: A {
// nothing is inherited because implicit conversion is defined
this.A {
return A(a: 10);
}
}
Inheritance and implicit conversion test:
void test() {
C c;
B b = c; // allowed
D d;
A a = d; // allowed
}
2 Classes
=========
2.1 Explicit implementation of interfaces
-----------------------------------------
Classes can explicitly implement interface methods or whole interfaces:
interface IFoo {
void foo();
}
interface IBar {
void bar();
}
class Foo: IFoo, IBar {
// explicitly implement IFoo interface methods
void IFoo.foo() {
}
// explicitly implement interface IBar by delegation
this.IBar {
return getBar();
}
}
2.2 Value classes
-----------------
Value classes are like ordinary classes, except:
- only constructor or destructor can change class state
- they can not have null value (at least default constructor is called)
- they have deterministic finalization if they implement 'Local'
Example:
const class Foo: Local {
this() {
foo = 5; // foo can be modified (only) in constructor/destructor
}
~this() {
printf("Foo.~this");
}
int foo;
}
void test() {
Foo foo; // foo.foo = 5 (default constructor is invoked, foo can't be null)
// prints "Foo.~this" and destroys foo here
}
Value classes can easily support deterministic finalization, because no cycles are
possible (without hacking, anyway) and simple reference counting is sufficient.
2.3 Custom allocators
---------------------
Custom allocators allow any allocation strategy:
// in Phobos
interface Allocator {
void* allocate(Type type);
void deallocate(Type type, void* mem);
}
// test application
void test() {
Foo foo = new(myAllocator) Foo();
// if Foo is value class and implements 'Local', then foo is deleted here
// ... else foo is deleted when myAllocator is deleted
}
3 Summary
=========
It seems that scoped variables are unnecessary, because value classes that
implement 'Local' interface, together with implicit conversions, are sufficient
in all of the following scenarios:
1. Finalization is required:
const class File: Local {
~this() {
close;
}
}
2. Garbage collection must be avoided:
const class Scoped(T): T, Local {
this(T object) {
_object = object;
}
this.T {
return _object;
}
private T _object;
}
Scoped(T) scoped(T)(T object) {
return new Scoped!(T)(object);
}
class Foo {
void foo() { ... }
}
void test() {
Foo foo = scoped(new Foo());
foo.foo();
// foo is deleted here :)
}
void test2() {
scoped(new Foo()).foo();
// Foo instance is deleted here too! :D
}
3. Value class semantics is desired:
const class Point {
int x;
int y;
}
Another (minor) suggestion is allowing method declarations without '()':
class Foo: Bar {
int foo {
// no ambiguity here, consistent with 'this.Bar' definition
return 5;
}
this.Bar {
return bar;
}
private Bar bar;
}
Also, get rid of static opCall
More information about the Digitalmars-d
mailing list