From: tmb@arolla.idiap.ch (Thomas M. Breuel)
Newsgroups: comp.lang.c++
Subject: Re: zero-length datatype
Date: 8 Sep 92 18:15:23 GMT
Article-I.D.: arolla.TMB.92Sep8141523
Reply-To: tmb@idiap.ch
Distribution: comp
Organization: IDIAP (Institut Dalle Molle d'Intelligence Artificielle
	Perceptive)
In-reply-to: mcgrant@rascals.stanford.edu's message of 7 Sep 92 21:24:56 GMT

In article <MCGRANT.92Sep7142456@rascals.stanford.edu> mcgrant@rascals.stanford.edu (Michael C. Grant) writes:

   In article <TMB.92Sep7171355@arolla.idiap.ch>
   tmb@arolla.idiap.ch (Thomas M. Breuel) writes:

      Consider, for example, the following template class definition:

	      template <class A,class B>
	      struct Bar {
		      A x;
		      B y;
	      };


      Now, the straightforward declaration obviously fails.

	      Bar<int,void> s;

   I don't find this straightforward at all. It seems to me that a
   parametrized type is not properly parametrized if some of the types
   may contain a 'void' entry like that.

"void" (or its equivalent) is a perfectly good type in many
programming languages. It has exactly one value and does not need to
be represented in the data structure at all precisely because it has
only one value.

   Perhaps there should be two
   different parametrized types, one that contains B and one that
   doesn't, and sufficient conversion operators, if necessary, between
   the two of them.

I thought C++ was about code reuse and not imposing arbitrary
restrictions.

It is quite common that dictionary classes are used only for their key
values, not for their corresponding data values. For the simplest
example, consider "awk" arrays, where you don't even have a choice.
There is nothing "unclean" or "strange" about this: you are simply not
interested in associating a non-trivial value with your keys. It would
be nice if the data values that you are not using didn't occupy any
memory.

   It is also quite necessary. Suppose that 'void' had zero length,
   and you instantiated

   Bar<void,int>s.

   Then the pointer to member A would have the same address as the pointer
   to member B! That should certainly not happen, since they are two different
   objects. The ARM has something quite specific to say about that.

I don't see why that is a problem. In particular, existing programs
don't use the feature, and whether I use it in new programs would be
up to me.

      One fix might be to allow definitions of objects of type "void", give
      them zero length, and introduce a single value for objects of type
      "void", denoted "void()". Of course, there would be some minor
      conflicts with the "function(void)" syntax, but those could be
      resolved compatibly. Alternatively, another built-in special type
      could be introduced, say, called "unit", that would be available
      (only) after "#include <unit.h>".

   I certainly should not have to 'include' any definition of a built-in
   type.

You already have to "#include" lots of things in order to get at
compiler primitives (including some built-in types in some compilers).
For example, some of the definitions in "math.h", "alloca.h" and
"setjmp/longjmp" are often compiler built-ins.

The reason for making such language extensions dependent on a
"#include" is to reduce the potential for name conflicts with existing
code. Commonly, such "#include" files contain something like the
following:

	/* __builtin_alloca is recognized as a keyword by the compiler */
	#define alloca(x) __builtin_alloca(x)

   I don't understand why those 4 bytes are causing you such a stink. Perhaps
   you could enlighten us as to why a truly zero length data type is even
   necessary?

Under the current rules,

      struct nothing {};
      HashTable<int,nothing> table(1000000);

table takes up 8Mbytes (on most machines). If you could write

      HashTable<int,void> table(1000000);

table takes up 4Mbytes.

   Also, I would like to see what templated member functions that
   operate on the above object would have to do to deal with the case of
   a 'void' type.
	
Consider:

       template <class Key,class Value>
       class HashTable {
       public:
            void add(Key key,Value value);
    	    int is_member(Key key);
    	    Value lookup(Key key);
       };

If you instantiate this using "HashTable<int,void>", you would get
the equivalent of:

       class HashTable_int_void {
       public:
            void add(int &key,void &value);
    	    int key_is_member(int &key);
    	    void lookup(int &key);
       };

Now, this is a perfectly useful data type. Consider:

      Bag<int> intersect_integers(Bag<int> &a,Bag<int> &b) {
              HashTable<int,void> table;
	      Bag<int> result;
	      for(int i=0;i<a.length();i++)
	              table.add(a(i),void());
	      for(i=0;i<b.length();i++)
	              if(key_is_member(table.b(i)))
		              result.add(b(i));
              return result;
      }

Obviously, there is little reason to call the function "lookup" now,
but everything would still be consistent. Consider, for example,
the following code fragment:

       Value x = lookup(some_key);

This would simply turn into:

       void x = lookup(some_key);

Obviously, a "void" variable could be initialized by the return value
("void()") of a "void" function.

It's not a major thing, but I still find the current restriction
pretty arbitrary (there are lots of sentences devoted in the ARM to
saying "you can't do this with 'void' and you can't do that with
'void'"), and it is inconvenient. Other languages have the equivalent
of the "void" value and "void" variables; why not C++?

	    			Thomas.
Newsgroups: comp.lang.c++