Topic: Poor man's typeof( ) implementation


Author: bparker@mailbox.uq.edu.au (Brian Parker)
Date: 1997/11/10
Raw View
For what it's worth, following this post is a rewrite of the typeof( )
code using only portable constructs (it doesn't use non-portable
typeid( ) behaviour) and which works with polymorphic types. As
before, this code is really only good for playing around with
typeof()- hopefully a full implementation of typeof( ) will become a
common (non-standard) compiler extension.

,Brian Parker.

---8<----------------------------------------------------------------------------------------------

// Portable (limited) typeof( ) emulation.
// Its limitation is that any type used with it must be
// preregistered using the macro REGISTER_TYPEx- this effectively
// renders this implementation useless for any purpose but
// experimentation with typeof( ).

#include <iostream>
using namespace std;

class ErrorNotRegistered {};

// Template to look up a unique ID for a type.
// (by default, use class member "id" to get unique ID)
template<class T>
struct GetID {
 enum {id = T::id};
};

// Template to lookup the associated type given a unique ID,
template<int id>
struct GetType {
 typedef ErrorNotRegistered type;
};

// Register a class that supplies a unique ID using a member "id"
#define REGISTER_TYPE(CLASS)  \
template<>     \
struct GetType<GetID<CLASS >::id > {  \
 typedef CLASS type;   \
};      \

// Register a class and also supply a unique ID
#define REGISTER_TYPE2(CLASS, ID)  \
template<>     \
struct GetID<CLASS > {    \
 enum {id = ID};    \
};      \
REGISTER_TYPE(CLASS)   \

// The unique ID is encoded as the size of a struct
// so that sizeof() can be used to avoid evaluating the expression.
template<class T>
struct Encode_ID_as_size {
 char buf[GetID<T >::id];
};

template<class T>
Encode_ID_as_size<T> EncodeID(const T& t)
{
 return Encode_ID_as_size<T>();
}

#define typeof(exp) GetType<sizeof(EncodeID(exp))>::type

// Helper macro to handle classes without default constructors.
// (expr should be defined as follows, but VC++ 5.0 doesn't like it)
//#define expr(A) (*(A *)0)
#define expr(A) (A())

// register some built-in types
REGISTER_TYPE2(float, 5)
REGISTER_TYPE2(double, 6)

// an example function using typeof( )
template<class A, class B>
typeof(expr(A) * expr(B))  MyFunc(A a, B b)
{
 return a * b;
}


template<class T, int ID = 1234>
struct MyClass {
 MyClass(T t):f(t) {}
 T f;
 enum {id = ID};
};

REGISTER_TYPE(MyClass<double>)

template<class T>
MyClass<T> operator*(const MyClass<T>& t1, const MyClass<T>& t2)
{
 return MyClass<T>(t1.f * t2.f);
}

int main()
{
 float d1 = 3.0;
 float d2 = 4.0;

 typeof(d1 * d2) g1 = d1 * d2;

 cout << g1 << '\n';

 typeof(MyFunc(d1, d2)) g2 = MyFunc(d1, d2);

 cout << g2 << '\n';

 MyClass<double> c1 = 4.5;
 MyClass<double> c2 = 2.0;

 typeof(c1 * c2) g3 = c1 * c2;

 cout << g3.f << '\n';
}

---end----------------------------
---
[ comp.std.c++ is moderated.  To submit articles: try just posting with      ]
[ your news-reader.  If that fails, use mailto:std-c++@ncar.ucar.edu         ]
[ FAQ:      http://reality.sgi.com/employees/austern_mti/std-c++/faq.html    ]
[ Policy:   http://reality.sgi.com/employees/austern_mti/std-c++/policy.html ]
[ Comments? mailto:std-c++-request@ncar.ucar.edu                             ]





Author: bparker@mailbox.uq.edu.au (Brian Parker)
Date: 1997/10/29
Raw View
On 29 Oct 97 05:27:53 GMT, I  bparker@mailbox.uq.edu.au (Brian Parker)
wrote:
>...
>This implementation is more for its novelty value and experimentation
>than for any serious use as it has a number of major limitations-
>
>(1) It only works with non-polymorphic types. If a class has a virtual
>function then typeid( ) switches to run-time behaviour; in that case,
>this code will give a compile-time error.
>...

Actually, it has just occurred to me that by modifying the code to use
pointers throughout, the code should also work for polymorphic types
(because pointers themselves are always non-polymorphic).

The modified code follows this post.

I say "should work" because VC++ 5.0 gives an internal error when this
new code is used  on polymorphic types .

Can anyone get this code to work on another compiler?

> (3) It relies on implementation-defined behaviour- in particular it
> assumes that typeid returns a pointer to a different type_info object
> for different types, which is not guaranteed by the CD2.

I no longer think that that is a problem as the type_info's for
different types clearly must be different, and within a single
compilation unit it would be truly perverse for an implementation to
return different type_infos for the same type- in fact, perhaps the
standard could specify this?.

> It also
> assumes that typeid is evaluated at compile-time for non-polymorphic
> types, which I believe is the intent of the draft standard and
> certainly is what VC++ 5.0 does (can anyone clarify this?)

On re- reading the CD2, it seems to me that this issue is not properly
specified- I think the standard should explicitly state that for
non-polymorphic types typeid is a constant expression. As it stands,
it only hints at this.

Whilst this is still a god-awful hack, it would almost be usable if
the new code changes worked, and would ease portablity to compilers
that provided a full implementation of typeof( ).

I note that the Gnu C compiler supports this extension; has typeof()
been considered for the upcoming C standard revision?

The updated code follows-

--8<----------------------------------------------------------------------------------------
// Emulate typeof for non-polymorphic types
// Compiled under Visual C++ v 5.0

template<class T>
T** kludge(const T&) {return (T**)0;}

#define typeof(X) Type_lookup_trait<&typeid(kludge(X) )>::type

class ERROR_TYPE_NOT_REGISTERED {};

template<const type_info*>
struct Type_lookup_trait {
 typedef ERROR_TYPE_NOT_REGISTERED type;
};

#define REGISTER_TYPE(X)    \
template<>      \
struct Type_lookup_trait<&typeid(X **)>   \
{       \
 typedef X type;     \
};       \
//----------------------------------------------------
// remaining code unchanged...

,Brian Parker
---
[ comp.std.c++ is moderated.  To submit articles: try just posting with      ]
[ your news-reader.  If that fails, use mailto:std-c++@ncar.ucar.edu         ]
[ FAQ:      http://reality.sgi.com/employees/austern_mti/std-c++/faq.html    ]
[ Policy:   http://reality.sgi.com/employees/austern_mti/std-c++/policy.html ]
[ Comments? mailto:std-c++-request@ncar.ucar.edu                             ]





Author: bparker@mailbox.uq.edu.au (Brian Parker)
Date: 1997/10/29
Raw View
The following is a partial implementation of typeof( ) based on
typeid( ) (which, for non-polymorphic types does not evaluate its
expression and generates its result at compile-time).

This implementation is more for its novelty value and experimentation
than for any serious use as it has a number of major limitations-

(1) It only works with non-polymorphic types. If a class has a virtual
function then typeid( ) switches to run-time behaviour; in that case,
this code will give a compile-time error.

(2) Every type used with typeof( ) must be registered once using the
REGISTER_TYPE macro. This largely defeats the purpose of typeof( ) in
template code, though it does beat adding a traits class for every
operator and permutation of type promotions.

(3) It relies on implementation-defined behaviour- in particular it
assumes that typeid returns a pointer to a different type_info object
for different types, which is not guaranteed by the CD2. It also
assumes that typeid is evaluated at compile-time for non-polymorphic
types, which I believe is the intent of the draft standard and
certainly is what VC++ 5.0 does (can anyone clarify this?)

The most interesting thing about the following code snippet is that it
indicates how trivial a complete implementation of typeof( ) would
be-- the bulk of the implementation is there already.

The more I think about typeof( ) the more I realise just how important
it is for generic functions. As others have pointed out in this
newsgroup, with general mixed-mode arguments a template function has
no other mechanism to discover the required return type, except by
using a combinatorial explosion of traits classes; I know that I could
get rid of a lot of traits classes in my code if it were available.

It's a shame typeof( ) didn't make it into this standard round, IMHO.

Anyway, the code follows-

--8<--------------------------------------------------------------------------------------------
#include <iostream>
#include <typeinfo>

//---------------------------------------------------
// Emulate typeof for non-polymorphic types
// Compiled under Visual C++ v 5.0
#define typeof(X) Type_lookup_trait<&typeid(X)>::type

class ERROR_TYPE_NOT_REGISTERED {};

// use address of returned type_info to index into a traits class
template<const type_info*>
struct Type_lookup_trait {
 typedef ERROR_TYPE_NOT_REGISTERED type;
};

// specialise traits class for every type used
#define REGISTER_TYPE(X)  \
template<>    \
struct Type_lookup_trait<&typeid(X)> \
{     \
 typedef X type;   \
};     \
//----------------------------------------------------


// Define example classes
class C{
public:
 void f();
 C(int) {}
 C() {}
};

class D{
public:
 D() {}
 D(const C& a) {} // promote from C to D
};

// example operation on C's and D's
D operator*(const D& lhs, const D& rhs)
{
 return D();
}

// need to register types- yeech!
REGISTER_TYPE(double)
REGISTER_TYPE(float)
REGISTER_TYPE(int)
REGISTER_TYPE(C)
REGISTER_TYPE(D)

// An example function using typeof
template<class A, class B>
typeof(A() * B()) mult(A a, B b)
{
 typeof(a * b) r = a * b;
 std::cout << typeid(r).name() << '\n'; // debug output
 return r;
}

int main()
{
 int i = 1;
 double f = 2.0;
 double fr;
 fr = mult(i, f); // prints "double"

 C c;
 D d;
 D r;
 r = mult(c, d);    // prints "class D"

 return 0;
}
--------------------------------------------------------------------

,Brian Parker
---
[ comp.std.c++ is moderated.  To submit articles: try just posting with      ]
[ your news-reader.  If that fails, use mailto:std-c++@ncar.ucar.edu         ]
[ FAQ:      http://reality.sgi.com/employees/austern_mti/std-c++/faq.html    ]
[ Policy:   http://reality.sgi.com/employees/austern_mti/std-c++/policy.html ]
[ Comments? mailto:std-c++-request@ncar.ucar.edu                             ]