Here are some other interesting ideas. I am posting them not as 'things I want', but as interesting ideas for discussion:
1) friend access attribute customized on classes/functions that can have access. Example:
class MyClass {
public:
protected:
private:
int a;
friend(MyOtherClass1, MyOtherClass2, MyFunction1):
int b;
};
2) truly private implementations. The header files will contain 'interface' classes which might or might not have all the members, and the implementation file will declare the actual internal implementation of the class as well as private fields/methods not visible in the header file. Example:
//header file
interface MyTruePrivateClass {
public:
void publicMethod();
private:
};
//implementation file
implementation MyTruePrivateClass {
public:
void publicMethod() {
}
private:
int truePrivateData;
void truePrivateMethod();
};
The compiler should keep a hidden table of object sizes to use with
operator new
or member declaration, as well as for checking if implementation of object already exists.
3) custom pointer syntax. Example:
template <class T> class SmartPtr@ : _SmartPtrBase {
public:
...
};
template <class T> class WeakPtr^ : _WeakPtrBase {
public:
...
};
//using weak and smart pointers
Foo @foo1 = new Foo;
Bar ^bar1 = new Bar;
(@foo1).data = 5;
(^bar1).data = 5;
4) more functionality in
operator new
, using a special class
new<T>
. For those classes that have a specialization of
new
, it is the New<T> that is called instead of
operator new
. This will allow various actions on an object not possible now. Example:
class Foo {
};
template <> new<Foo> {
public:
//called instead of operator 'new'
static Foo *operator new(size_t size) {...}
//called instead of operator 'delete'
static void operator delete(Foo *foo) {...}
//called implicitely after object constructor
static void created(Foo *foo) {...}
//called implicitely after object destructor
static void deleted(Foo *foo) {...}
};
The above will allow things like this:
a) performing actions that depend on allocated type; for example registering a finalizer in a garbage collector.
b) performing actions that depend on after constructors have been called; for example, registering an object somewhere, while calling a virtual method of the object (not possible before instantiation).
5) accessing struct/class members with array syntax; operator
lengthof
that returns number of elements of array/tuple. This is necessary for compile-time introspection. Example:
struct MyStruct {
int field1;
double field2;
float field3;
};
template <> void doSomething(MyStruct *s, int field);
template <> void doSomething(MyStruct *s, double field);
template <> void doSomething(MyStruct *s, float field);
void processMyStruct(MyStruct *s) {
//intrinsic function 'lengthof' returns length of tuple
for(int i = 0; i < lengthof(*s); ++i) {
//using fields of a struct with array syntax; different version of 'doSomething' is invoked for each field
doSomething(s, (*s)[i]);
}
}
6) aspects/introspection by classes for classes. Example:
//my class
class Foo {
public:
void doSomething();
};
//class of class Foo
class<Foo> {
public:
//aspect overloading
void doSomething() {
log << "doSomething was called\n";
Foo::doSomething();
}
};
Foo *foo = new Foo;
foo->doSomething(); //the aspect is invoked, instead of the method.
Very useful for debugging purposes and other operations. With this capability, point #4 above is reduntant, as
operator new
can be a member of the aspect classes.
7) instance creation by namespace string. Example:
Object *myObject = new "MyApp::MyClasses::Foo"(1, 2, 3);
Useful for loading modules at run-time. The compiler should place an internal array of class information in each executable/library.
8) classes that can be defined/extended on the point of declaration. For example:
class Foo {
public:
virtual void action();
};
Foo *foo1 = new Foo() {
void action() {
printf("subclassed!\n");
}
};
Foo foo2() {
void action() {
printf("subclassed!\n");
}
};
sort(mydata, class MyFunctor() { bool operator ()(int a, int b) { return false }});
Useful for for callbacks, etc.
9) local functions. Example:
void outterFunction() {
int i;
void innerFunction() {
int j = i + 1;
}
innerFunction();
}
It's quite easy to achieve. The inner function's stack should come right after the outter function's stack; think about is as struct inheritance. Stack frames should be dealt with as locally defined structs, and local variables should be accessed as member objects of the function. For example:
outterFunction.i = 5;
Local functions allow for interesting algorithms.
10) formalize CPU context. The standard library should provide a CPU context class as well as methods to get/set CPU context. Since C++ is a high-level language for a low-level environment, C++ should provide such functionality. This functionality will allow for user threads, user multithreaded garbage collection, co-routines, tail call optimization etc. The API should be fairly abstract: the CPU context should be handled as an array of bytes, but also as a struct with named registers. The API should offer the following functionalities:
a) setting and getting the CPU context.
b) getting the stack information (top and bottom address).
c) using the stack: push, pop.
d) setting up the next address to execute.
11) arbitrary length integer, real arithmetic; fractions; ranges. I think the standard library should have those.
12) sets. Like enumerations, but member values can be objects. Example:
setdef MyColor : Color {
Red = Color(255, 0, 0),
Green = Color(0, 255, 0),
Blue = Color(0, 0, 255)
};
setdef Day : const char * {
Sun = "Sunday",
Mon = "Monday",
Tue = "Tuesday"
};
Sets could be used like arrays or enumerations. Example:
MyColor c1 = Blue;
Color c2 = MyColor[2];
The reason sets are useful is the same as enumerations: they limit the chances of using the wrong value (as well as provide names for standard values).