Hi,

I have 2 ideas about C++ language, maybe they could be implemented as GCC C++ extensions (Microsoft has many his own and no one complains). :-) If it doesn't have them already (I don't know, but I have never heard about this ideas).

1. void as first class type

Current status of void is somewhat strange. It is a type but you cannot create objects of type void. So, it is type of what? But you can have memory pointer to void, so object (or array) of type void has its memory position but cannot be here created. Pointer arithmetics for void* is forbidden. Despite this all, we are allowed to use void as a template type parameter. It is not elegant and
makes C/C++ harder to understand for beginners.

My idea is as follows:

Lets allow to create void objects.
Let sizeof(void) == 1
Should void variable occupy address space? Yes, but physical memory will be never accessed by this variable. So what void variable contains? Nothing - "void" indeed. It is like an empty box on the table. So, void is similar to char. char is a box containing 1 character, void is the same size box containing nothing.
Can we put something inside that box? No, it is compiler error, for example:

void a;
a = 'a'; // wrong! type  mismatch
a = 10; // wrong! type mismatch
a = 0; // wrong! type mismatch

We cannot get anything from empty box either:

void a;
char c = a; // wrong! type  mismatch
int n= a; // wrong! type mismatch

but:

void a;
void b;

a = b; // OK, but because both a and b are empty, nothing happens.

So, if you wish you can create void objects:

int a;
void void_array[32] ;
int z;

In the above, between variables a and z, there is 32 bytes of memory
reserved for void_array. That chunk of memory will never be accessed
by void_array object.

Then, we have

void function(void x);

it is legal to write:

void arg;
void c = function(arg);

nothing is ever copied on call and return.

The only drawback on having fun(void x)
argument x declaration, is that to be correct 1 byte is
reserved on the stack for x argument. (it will never
be accessed anyway). But if we never plan to pass
that x argument we can write as before fun(void)

In addition, we achieve the possibility to
perform void* pointer arithmetics,
remembering that sizeof(void) == 1;

We can allocate memory this way:

void* area = new void[1000];

and then do a cast

char*  string  =  reinterpret_cast<char*>(area);

and now the access to that memory (or rather lack of
any access) via area pointer obeys all the rules stated,
while access to the same memory by the string pointer
obeys all C++ rules for char[] array.

Maybe a stupid question at the end - do we need any new
keyword or literal to name the "value" contained inside the
void variable?
Of course no, we cannot name something what doesn't even exist.

:-).

2. new 'callback' type

Some time ago I tried to write a class implementing idea of
callbacks (called events or signal/slot mechanism by others)
Lets assume we have a Clock object. Every full hour clock rings.
Many other active objects of different types want to be notified about
that interesting event. So every object says to Clock object:
here is my 'this' pointer and a name of one (or more) of my methods.
When you start to ring I want you to call that method(s) with an int
argument containing current hour. So all these objects have prepared
special methods of different names they liked, but of the same signature: void (int hour).
Object First has the method void ClockRings(int hour), Object Second has the
method OldClockMakesNoise(int hour), and so on. Some objects can have two
or more their own methods of said signature and want all of them to be called
for any reason. I thought it would be easy.

To ask the Clock for ring notifcation every object of type ObjectN would
call function CallMe() with 2 parameters: his "this" pointer of type ObjectN* and pointer to its chosen method of type void(ObjectN::*member)(int hour). But every object is of different type, so we need rather template<T> void Clock::CallMe(T* object, void(T::*method)(int hour)) Clock would save somwhere all object&method pairs, and when for example 13:00 hour rings,
it would take each pair in turn and call:

object->*method(13);

But problem is: where to store all those pairs and use them in turn - they are of different types?
I thought for some time and went with rather straightforward idea:
I would cast every ObjectN* to void* object, then by analogy cast every
void(ObjectN::*)(int hour) to void(void::*method)(int hour) :-D, store them all in: std::vector<std::pair<void*, void(void::*)(int)> > and when time comes iterate through the vector and call (*iter).first->*(*iter.second)(13); I thought that as void* is "generic pointer" then void(void::*)(int) is "generic pointer to member function of type void(int)". In C++ it is safe to assign Object* to void and then cast back by static_cast to Object*. I thought that it should be safe to call object->*method(13) even without casting them back to appropriate ObjectN types - for every pair they came from the same object, so they must match. But after some time i discovered that void::* pointer would be a hole in C++ type system. By programmer mistake I could take object* from first vector pair, and use it to call method pointer taken from last vector pair. They can just be from different ObjectN classess and without any warning C++ type system crashes. I looked for elegant
and effective solution on Internet, but didn't find one.

Here is my idea - lets add "callback" pointer data type. It would require to introduce
new data type symbol: @. Internally it would contain 2 pointers:
pointer to object and pointer to member of that object.

so we would have:

int @ptr
ptr = &&object1.int_member1; //get 2 pointers at once! their types always match
int nn = @ptr;

void (@callback)(int); callback=&&object2.ClockRings // get 2 pointers at once
(@callback)(13) ; or just callback(13)

My example would be easy now:

typedef void(@RingEvent)(int hour);

Clock::CallMe(RingEvent callback);
container is now std::vector<RingEvent>
calling events: (*iter)(13);
objects subscribing for notifications: clock->CallMe(&&this->ClockRings) // &&ClockRings for short

&& would be indeed implemented as 4 variants:

&&. for callback = && Object . Member
&&-> for callback = && PointerToObject -> Member
&&.*  for callback = && Object.*PointerToMember
&&->* for callback = &&PointerToObjects->*PointerToMember

I hope this makes sense :-)

Ireneusz Szpilewski

Reply via email to