Topic: run-time type checking, freezing, and thawing


Author: maxtal@extro.ucc.su.OZ.AU (John MAX Skaller)
Date: Sat, 8 Aug 1992 00:37:09 GMT
Raw View
In article <SRS56GM@netmbx.netmbx.de> jrobie@netmbx.netmbx.de (Jonathan Robie) writes:
>chip@tct.com (Chip Salzenberg) writes:
>
>>According to jrobie@netmbx.netmbx.de (Jonathan Robie):
>>>chip@tct.com (Chip Salzenberg) writes:
>>>>Compiler-supplied structure info is not sufficient.
>>>
>>>In our system the programmer can declare members to be transient...
>
>>That looks like a reasonable feature.  But it goes _way_ beyond the
>>RTTI information that can reasonably be expected from a C++ compiler.
>>After all, "transience" isn't a C++ concept.  So you're going to need
>>a preprocessor anyway, to do what you want to do.
>
>Especially since there seems to be a lot of reluctance to add new
>keywords to the language.  The argument is that they break existing code.
>
>It remains to be seen whether the various people who need RTTI can agree
>on a proposal which will meet all of our needs.  If not, we will not throw
>out our precompiler....
>

 Dont throw it out ANYWAY. It does exactly what YOU want.
You have CONTROL over it. YOU can port it. Avoid trusting C++
compiler vendors, none have actually written a compiler that
works to my knowledge :-)

>
>Well, the full polymorphic type should contain precisely the information
>found in the class declarations, no more and no less.  That seems clear
>enough...

 So use the source code: that is the class declaration,
no more no less, by definition. In other words, your precompiler
is the best way!

 (But I will support your quest for XRTTI all the same!)

 No actually, dynamic compilation and linkage is an even
better way. (You can then add types to a running database server
on a network  ... )

--
;----------------------------------------------------------------------
        JOHN (MAX) SKALLER,         maxtal@extro.ucc.su.oz.au
 Maxtal Pty Ltd, 6 MacKay St ASHFIELD, NSW 2131, AUSTRALIA
;--------------- SCIENTIFIC AND ENGINEERING SOFTWARE ------------------




Author: jrobie@netmbx.netmbx.de (Jonathan Robie)
Date: Mon, 10 Aug 1992 07:40:09 GMT
Raw View
chip@tct.com (Chip Salzenberg) writes:

>According to jrobie@netmbx.netmbx.de (Jonathan Robie):
>>chip@tct.com (Chip Salzenberg) writes:
>>>That phrase "full polymorphic type" brings back The Princess Bride:
>>>"I don't think it means what you think it means."  C++ compilers aren't
>>>going to provide all the type information that POET needs.
>>
>>Well, the full polymorphic type should contain precisely the information
>>found in the class declarations, no more and no less.

>Yes, but is that the >>C++<< class declaration, or the >>POET<< class
>declaration?

The C++ class declaration, of course.


Jonathan

=======================================================================

Jonathan Robie  jrobie@netmbx.UUCP
Arnold-Zweig-Str. 44 jrobie@netmbx.in-berlin.de
O-1100 Berlin
Deutschland  Phone:  +37 (2) 472 04 19  (Home, East Berlin)
    +49 (30) 342 30 66 (Work, West Berlin)


--
Jonathan

===========================================================================





Author: jrobie@netmbx.netmbx.de (Jonathan Robie)
Date: Mon, 10 Aug 1992 07:42:48 GMT
Raw View
maxtal@extro.ucc.su.OZ.AU (John MAX Skaller) writes:

> So use the source code: that is the class declaration,
>no more no less, by definition. In other words, your precompiler
>is the best way!

> (But I will support your quest for XRTTI all the same!)

> No actually, dynamic compilation and linkage is an even
>better way. (You can then add types to a running database server
>on a network  ... )

Well, make a proposal to the ANSI subcommittee for dynamic compilation
and linkage....Oops, I guess you'll have to found one...make a concrete
proposal....


Jonathan

=======================================================================

Jonathan Robie  jrobie@netmbx.UUCP
Arnold-Zweig-Str. 44 jrobie@netmbx.in-berlin.de
O-1100 Berlin
Deutschland  Phone:  +37 (2) 472 04 19  (Home, East Berlin)
    +49 (30) 342 30 66 (Work, West Berlin)


--
Jonathan

===========================================================================





Author: chip@tct.com (Chip Salzenberg)
Date: Mon, 17 Aug 1992 13:32:56 GMT
Raw View
According to jrobie@netmbx.netmbx.de (Jonathan Robie):
>chip@tct.com (Chip Salzenberg) writes:
>>According to jrobie@netmbx.netmbx.de (Jonathan Robie):
>>>Well, the full polymorphic type should contain precisely the information
>>>found in the class declarations, no more and no less.
>
>>Yes, but is that the >>C++<< class declaration, or the >>POET<< class
>>declaration?
>
>The C++ class declaration, of course.

But a C++ doesn't provide enough information for persistence.  That's
why POET has the "transient" keyword, for example.  So the "complete
polymorphic type information" required for thawing an object is _not_
the C++ type information, but rather the POET type information (though
the latter is a superset of the former, to be sure).
--
Chip Salzenberg at Teltronics/TCT  <chip@tct.com>, <73717.366@compuserve.com>
   "Do Rush place weird subliminal backmasked messages in their songs to
 compel unwilling geeks to commit evil .sig atrocities?"  -- Dean Engelhardt




Author: dan@BofA.com (Dan Brockman)
Date: Wed, 19 Aug 92 01:25:44 GMT
Raw View
In article <l7u53jINN17a@exodus.Eng.Sun.COM> chased@rbbb.Eng.Sun.COM (David Chase) writes:
>Assertions of Turing equivalence are not very interesting.  Stopwatch
>benchmarks of program speed and programmer speed are where it is at.
>Do what makes you more productive.

Any good empirical evidence available regarding the productivity
of C++ versus, say, C ?  In practical commercial environments?

--
 ------------------------------------------------------
 Daniel Brockman                       tel 415-953-0406, fax 415-622-2892
 Bank of America, Dept 5906, 555 Calif St
 San Francisco 94104 USA              email uunet!odinba!dan dan@BofA.COM




Author: chip@tct.com (Chip Salzenberg)
Date: Thu, 30 Jul 1992 19:39:37 GMT
Raw View
According to jrobie@netmbx.netmbx.de (Jonathan Robie):
>chip@tct.com (Chip Salzenberg) writes:
>>But once you've frozen a C++ object into a dead array of bytes, it's
>>no longer a C++ object, so the C++ type system is no longer operative.
>
>RTTI is needed to properly freeze a C++ object into a "dead array of
>bytes" ...

Virtual functions are sufficient for freezing.

>... and to turn a "dead array of bytes" into a C++ object.

RTTI is of zero relevance to thawing.  It doesn't make this code work:

    int i;
    char *buf;
    read(fd, &i, sizeof(int));
    buf = new char[i];
    read(fd, buf, i);
    XXX *foo = thaw_arbitrary_object(buf, i);

For one thing, what is the XXX type? -- since there is no universal
base class for C++ objects, there is no valid candidate.  For another
thing, how can thaw_arbitrary_object know the complete set of classes
I might have written to the given file? -- it can't.

>It is also needed to follow references among objects in RAM.

I can do that without RTTI right now.

>RTTI and persistence are related enough that all of the major C++
>database systems use precompilers to get at it.

The mere fact that RTTI can be used for freezing proves neither (1)
that it is necessary for freezing, nor (2) that it is useful -- much
less necessary -- for thawing.
--
Chip Salzenberg at Teltronics/TCT  <chip@tct.com>, <73717.366@compuserve.com>
   "Do Rush place weird subliminal backmasked messages in their songs to
 compel unwilling geeks to commit evil .sig atrocities?"  -- Dean Engelhardt




Author: jrobie@netmbx.netmbx.de (Jonathan Robie)
Date: Fri, 31 Jul 1992 12:49:56 GMT
Raw View
chip@tct.com (Chip Salzenberg) writes:

>According to jrobie@netmbx.netmbx.de (Jonathan Robie):
>>
>>RTTI is needed to properly freeze a C++ object into a "dead array of
>>bytes" ...

>Virtual functions are sufficient for freezing.

Somebody out there is not listening.  When I say "necessary" I do not
mean that it is impossible for an application programmer to write lots
of code that I could use to do a task.  I simply mean that this info is
not out there unless the compiler or the application programmer supplies it.
Since the compiler has the info and then throws it away I would rather get
it from the compiler, and information gotten from the compiler is also more
likely to be correct...

>>... and to turn a "dead array of bytes" into a C++ object.

>RTTI is of zero relevance to thawing.  It doesn't make this code work:

>    int i;
>    char *buf;
>    read(fd, &i, sizeof(int));
>    buf = new char[i];
>    read(fd, buf, i);
>    XXX *foo = thaw_arbitrary_object(buf, i);

Good thing, too!  Would you *want* it to make that code work? :->
It *does*, however, make this code work:

 PersonAllSet.Get(pPerson);

That is the code to read the next person out of the Person AllSet in
a database.  It works properly regardless of the derived type of the
person (plumber, presidential candidate, gigolo, software developer...)
and restores the object which was originally stored.  Note that the
actual offsets of an object in memory may vary from one executable to
another, so you have to find the location and type of the data members
at run time.

This *can* be done using virtual functions, two for every single data
member in the class (one to get the member, one to set it), plus an
additional member function to identify the parent of the class.  But
these virtual functions make all the data members of the object accessible
in the whole program, and therefore break encapsulation!  You *do* want
private and protected members to be stored in the database without making
them publically accessible, don't you??

If these members are accessed only through run time type identification
then encapsulation and data hiding are preserved *except* for those
functions which use run time type identification.

If virtual functions are written then you have opened your objects
up to everybody.  This is true whether the programmer must write all
these functions (ugh!) or a precompiler does the work (ugh! -- how many
precompilers do we want to replace the information thrown away by the
compiler!).  Of course, you could make all these functions private and
make yourself a friend of each class.  Maybe two or three other libraries
will want to do the same, and maybe their naming conventions will clash,
the signatures of the member functions will be incompatible, the precompilers
will be incompaible with each other...

>>It is also needed to follow references among objects in RAM.

>I can do that without RTTI right now.

An object oriented database or other general purpose tool which does not
know your data types can not.  This was the context of my previous message.
If you want the database to store, retrieve, or query a network of objects
then it needs access to run time type identification.

>>RTTI and persistence are related enough that all of the major C++
>>database systems use precompilers to get at it.

>The mere fact that RTTI can be used for freezing proves neither (1)
>that it is necessary for freezing, nor (2) that it is useful -- much
>less necessary -- for thawing.

No, but the fact that nobody seems to have done it successfully without it
might mean something, given that nobody writes a precompiler if they have
a choice.


Jonathan

======================================================================

Jonathan Robie  jrobie@netmbx.UUCP
Arnold-Zweig-Str. 44 jrobie@netmbx.in-berlin.de
O-1100 Berlin
Deutschland  Phone:  +37 (2) 472 04 19  (Home, East Berlin)
    +49 (30) 342 30 66 (Work, West Berlin)

The opinions expressed in this message are not necessarily those
of my employer, but they should be yours.



--
Jonathan

===========================================================================





Author: tmb@arolla.idiap.ch (Thomas M. Breuel)
Date: 31 Jul 92 20:58:14 GMT
Raw View
In article <FAM5HFE@netmbx.netmbx.de> jrobie@netmbx.netmbx.de (Jonathan Robie) writes:

   chip@tct.com (Chip Salzenberg) writes:
   >According to jrobie@netmbx.netmbx.de (Jonathan Robie):
   >>RTTI is needed to properly freeze a C++ object into a "dead array of
   >>bytes" ...
   >Virtual functions are sufficient for freezing.

   Somebody out there is not listening.  When I say "necessary" I do not
   mean that it is impossible for an application programmer to write lots
   of code that I could use to do a task.  I simply mean that this info is
   not out there unless the compiler or the application programmer supplies it.

For many classes it is simply wrong to use structural type information
for "freezing" (just as it is wrong in general to use structural type
information for determining equality). At the very least, the
implementor of a class should be able to specify whether and to whom
structural type information about a class should be accessible.

Note also that you do not need runtime typing to make structural I/O
work. You could provide generic structural I/O that requires knowledge
of the object's type at compile time and does not have to rely on
runtime type tags (as opposed to polymorphic structural I/O that
requires RTTI).

     Thomas.




Author: chased@rbbb.Eng.Sun.COM (David Chase)
Date: 31 Jul 92 17:59:59 GMT
Raw View
In article <FAM5HFE@netmbx.netmbx.de> jrobie@netmbx.netmbx.de (Jonathan Robie) writes:
>Somebody out there is not listening.

I'll say (but with different meaning).  Perhaps, people have looked at
the problems of freezing and thawing data structures before in other
languages?  Perhaps, people have even built tools to allow easy
generation of "freeze" and "thaw" code for data types in the past,
or libraries that colluded with the compiler so that it was just
another run-time facility? Perhaps people have even built systems that
used these tools?

Reading about what other people have already done is so dull and
boring.  It's much more fun to re-invent the wheel, I think.

What I am sarcastically referring to is the work done, years ago, at
both Olivetti and DEC on "pickling" data structures.  DEC uses the
library approach; at Olivetti we used the tool approach.  We used
pickled data structures as part of the input to the pre-linker.  This
was done for a couple of languages in the Modula family (2+ and 3, I
believe).

David Chase
Sun




Author: jrobie@netmbx.netmbx.de (Jonathan Robie)
Date: Mon, 3 Aug 1992 07:34:29 GMT
Raw View
chased@rbbb.Eng.Sun.COM (David Chase) writes:

>Reading about what other people have already done is so dull and
>boring.  It's much more fun to re-invent the wheel, I think.

>What I am sarcastically referring to is the work done, years ago, at
>both Olivetti and DEC on "pickling" data structures.  DEC uses the
>library approach; at Olivetti we used the tool approach.  We used
>pickled data structures as part of the input to the pre-linker.  This
>was done for a couple of languages in the Modula family (2+ and 3, I
>believe).

We certainly did lots of reading before designing our OODBMS.  The
conclusion we reached was that approaches used for standard data structures
were not adequate for objects in which polymorphism must be preserved.

I am not familiar with the approaches of Olivetti or DEC.  If you have
some specific references that I should read I would be very interested
in looking at them.


Jonathan

=======================================================================

Jonathan Robie  jrobie@netmbx.UUCP
Arnold-Zweig-Str. 44 jrobie@netmbx.in-berlin.de
O-1100 Berlin
Deutschland  Phone:  +37 (2) 472 04 19  (Home, East Berlin)
    +49 (30) 342 30 66 (Work, West Berlin)


--
Jonathan

===========================================================================





Author: chip@tct.com (Chip Salzenberg)
Date: Mon, 3 Aug 1992 20:55:39 GMT
Raw View
According to jrobie@netmbx.netmbx.de (Jonathan Robie):
>chip@tct.com (Chip Salzenberg) writes:
>>Virtual functions are sufficient for freezing.
>
>When I say "necessary" I do not mean that it is impossible for an
>application programmer to write lots of code that I could use...

So, when you write "necessary", you actually mean "preferable to me."
Humpty-dumpty would be proud.

>... this info is not out there unless the compiler or the application
>programmer supplies it.  Since the compiler has the info and then throws
>it away I would rather get it from the compiler...

Compiler-supplied structure info is not sufficient.  Consider this
class:

    class Foo {
      public:
        void foo();
      private:
        int a, b;
 char c[1000];
    };

Now, which instance variable(s) _need_ to be saved in order to retain
the meaning of a class instance?  Only the programmer knows for sure.

In this case, "b" and "c" merely cache the text value of "a", so a
programmer-supplied freeze function will obviously be superior, as it
will avoid saving data unnecessarily.

So since RTTI is not sufficient and it's not necessary, I'll just keep
going without it, thank you very much.

>[RTTI does] make this code work:
>
> PersonAllSet.Get(pPerson);

Handwave alert!

Show us the skeleton of "PersonAllSet::Get(Person *&)", and then maybe
we'll have something to talk about.  The resolution of pointer members
(including circular reference) should be particularly entertaining.

>This *can* be done using virtual functions, two for every single data
>member in the class...

Strawman alert!

Freezing can be done using _one_ virtual function (Person::Put()), and
thawing can be done using _one_ function that knows how to thaw any
derived class of Person.

I've done this with serial line protocol objects.  There is one
function that does dynamic protocol determination ("what is talking to
me?").  From that point on, everyone works with a "Proto*" pointer.

>An object oriented database or other general purpose tool which does not
>know your data types can not.

Even if it does know your data types, it can't do everything.  And a
partial job is, in my opinion, not worth the hassle.

>>The mere fact that RTTI can be used for freezing proves neither (1)
>>that it is necessary for freezing, nor (2) that it is useful -- much
>>less necessary -- for thawing.
>
>No, but the fact that nobody seems to have done it successfully without
>it might mean something...

Restated: "I've never allowed myself to consider that others' programs
might work even though they gore my favorite bull."
--
Chip Salzenberg at Teltronics/TCT  <chip@tct.com>, <73717.366@compuserve.com>
   "Do Rush place weird subliminal backmasked messages in their songs to
 compel unwilling geeks to commit evil .sig atrocities?"  -- Dean Engelhardt




Author: jrobie@netmbx.netmbx.de (Jonathan Robie)
Date: Tue, 4 Aug 1992 09:07:56 GMT
Raw View
chip@tct.com (Chip Salzenberg) writes:

>According to jrobie@netmbx.netmbx.de (Jonathan Robie):
>>chip@tct.com (Chip Salzenberg) writes:

>>... this info is not out there unless the compiler or the application
>>programmer supplies it.  Since the compiler has the info and then throws
>>it away I would rather get it from the compiler...

>Compiler-supplied structure info is not sufficient.  Consider this
>class:

>    class Foo {
>      public:
>        void foo();
>      private:
>        int a, b;
> char c[1000];
>    };

>Now, which instance variable(s) _need_ to be saved in order to retain
>the meaning of a class instance?  Only the programmer knows for sure.

>In this case, "b" and "c" merely cache the text value of "a", so a
>programmer-supplied freeze function will obviously be superior, as it
>will avoid saving data unnecessarily.

In our system the programmer can declare members to be transient, which
means they are not stored in the database.  In general, the programmer
will supply a constructor which initializes transient members for those
classes which contain them.  This looks like this:

    persistent class Foo {
      public:
        void foo();
      private:
        transient int a, b;
 char c[1000];
    };

Since persistent class declarations contain non-standard code they
must be kept in separate files which are preprocessed.  The output is
included into your vanilla C++ programs.

>>[RTTI does] make this code work:
>>
>> PersonAllSet.Get(pPerson);

>Handwave alert!

>Show us the skeleton of "PersonAllSet::Get(Person *&)", and then maybe
>we'll have something to talk about.  The resolution of pointer members
>(including circular reference) should be particularly entertaining.

I have discussed this in another message that I just posted, but the
basic structure for Get is something like:

 If object is loaded
  return a pointer to the existing object in memory
 Else
  Determine the full polymorphic type for the object
  Call the database constructor for the object
  Stuff the object with data members
  Mark the object as loaded
  Load each referenced object and initialize pointer
  or reference with its address

This is a description of an existing commercial OODBMS, POET.  It
resolves pointer members correctly, even if there are circular
references.

I am leaving out lots of details, including class versioning issues,
which are not essential to your understanding of the basic functionality.

Now do we have something to talk about?  :->

>>An object oriented database or other general purpose tool which does not
>>know your data types can not.

>Even if it does know your data types, it can't do everything.  And a
>partial job is, in my opinion, not worth the hassle.

What can't it do?  I have already answered the objections you mentioned
above.




Jonathan

=======================================================================

Jonathan Robie  jrobie@netmbx.UUCP
Arnold-Zweig-Str. 44 jrobie@netmbx.in-berlin.de
O-1100 Berlin
Deutschland  Phone:  +37 (2) 472 04 19  (Home, East Berlin)
    +49 (30) 342 30 66 (Work, West Berlin)


My employer won't read this article, so he doesn't have to agree with
the opinions expressed here.  Chip may not agree with the article
either, but at least he will read it and write a reasonable response!

--
Jonathan

===========================================================================





Author: chased@rbbb.Eng.Sun.COM (David Chase)
Date: 4 Aug 92 23:36:19 GMT
Raw View
In article <2A7D9D4B.6CFC@tct.com> chip@tct.com (Chip Salzenberg) writes:
>>>Virtual functions are sufficient for freezing.

>Compiler-supplied structure info is not sufficient.  ...
>Now, which instance variable(s) _need_ to be saved in order to retain
>the meaning of a class instance?  Only the programmer knows for sure.

>So since RTTI is not sufficient and it's not necessary, I'll just keep
>going without it, thank you very much.

Assembly language is sufficient for all programming purposes.
(Therefore, C++ is not necessary.)

Sometimes assembly language is necessary to get something done.
(Therefore, C++ is not sufficient.)

By your reasoning, I conclude that you'll keep going without C++?

In practice, the pickling systems (Olivetti Modula-3) that I used made
use of automatically generated information (run-time type
information), except where the defaults were overridden by the
programmer.  The programmer's contribution was to translate one data
type into another data type, and then pickle the second data type in
the standard way.  This worked very well (gainsay it if you want to,
but I was there, and it worked).

Ultimately, it all relied on run-time type information.  At a minimum,
this effectively prevented you from accidentally unpickling the
contents of a file as the wrong data structure.

Now, as has already been asserted, none of this is necessary.  In
fact, you could do it in C, because we translated our Modula-3 into C.
(Note that the C was then translated into assembly language.)

Assertions of Turing equivalence are not very interesting.  Stopwatch
benchmarks of program speed and programmer speed are where it is at.
Do what makes you more productive.

David Chase
Sun




Author: chip@tct.com (Chip Salzenberg)
Date: Wed, 5 Aug 1992 13:08:14 GMT
Raw View
According to jrobie@netmbx.netmbx.de (Jonathan Robie):
>chip@tct.com (Chip Salzenberg) writes:
>>Compiler-supplied structure info is not sufficient.
>
>In our system the programmer can declare members to be transient...

That looks like a reasonable feature.  But it goes _way_ beyond the
RTTI information that can reasonably be expected from a C++ compiler.
After all, "transience" isn't a C++ concept.  So you're going to need
a preprocessor anyway, to do what you want to do.

> If object is loaded
>  return a pointer to the existing object in memory
> Else
>  Determine the full polymorphic type for the object
>  Call the database constructor for the object
>  Stuff the object with data members
>  Mark the object as loaded
>  Load each referenced object and initialize pointer
>  or reference with its address

I see.  Still, that phrase "full polymorphic type" brings back The
Princess Bride: "I don't think it means what you think it means."  C++
compilers aren't going to provide all the type information that POET
needs.  For that matter, POET '92 won't provide the type information
that POET '93 needs (in all likelyhood).  So I'd expect that C++ RTTI
won't much matter to OODB vendors.

>>Even if it does know your data types, it can't do everything.  And a
>>partial job is, in my opinion, not worth the hassle.
>
>What can't it do?

It can't figure out what "char *p" points to.
--
Chip Salzenberg at Teltronics/TCT  <chip@tct.com>, <73717.366@compuserve.com>
   "Do Rush place weird subliminal backmasked messages in their songs to
 compel unwilling geeks to commit evil .sig atrocities?"  -- Dean Engelhardt




Author: chip@tct.com (Chip Salzenberg)
Date: Wed, 5 Aug 1992 13:10:27 GMT
Raw View
According to chased@rbbb.Eng.Sun.COM (David Chase):
>In article <2A7D9D4B.6CFC@tct.com> chip@tct.com (Chip Salzenberg) writes:
>>So since RTTI is not sufficient and it's not necessary, I'll just keep
>>going without it, thank you very much.
>
>Assertions of Turing equivalence are not very interesting.

David is right, of course.  Sorry for flaming.
--
Chip Salzenberg at Teltronics/TCT  <chip@tct.com>, <73717.366@compuserve.com>
   "Do Rush place weird subliminal backmasked messages in their songs to
 compel unwilling geeks to commit evil .sig atrocities?"  -- Dean Engelhardt




Author: chip@tct.com (Chip Salzenberg)
Date: Fri, 7 Aug 1992 13:46:21 GMT
Raw View
According to jrobie@netmbx.netmbx.de (Jonathan Robie):
>chip@tct.com (Chip Salzenberg) writes:
>>That phrase "full polymorphic type" brings back The Princess Bride:
>>"I don't think it means what you think it means."  C++ compilers aren't
>>going to provide all the type information that POET needs.
>
>Well, the full polymorphic type should contain precisely the information
>found in the class declarations, no more and no less.

Yes, but is that the >>C++<< class declaration, or the >>POET<< class
declaration?
--
Chip Salzenberg at Teltronics/TCT  <chip@tct.com>, <73717.366@compuserve.com>
   "Do Rush place weird subliminal backmasked messages in their songs to
 compel unwilling geeks to commit evil .sig atrocities?"  -- Dean Engelhardt