I'm just wondering how boost have implemented BOOST_TYPEOF (in C++03) which seems to be a very useful tool. Anyone has any idea?
Also, I'm thinking C++03 itself could have provided typeof
operator, especially when it already has sizeof(expr)
which must be knowing the type of the expr
also, otherwise how else could it tell us the size, without knowing the type?Is it really possible to know the size, without knowing the type of an expression?
If it doesn't know the type, then compiler tells us the size of what (if not type)? I mean, sizeof(unknowntype)
wouldn't make sense to the compilers (and humans as well)!
The typeof keyword is a new extension to the C language. The Oracle Developer Studio C compiler accepts constructs with typeof wherever a typedef name is accepted, including the following syntactic categories: Declarations.
In other languages, such as C# or D and, to some degree, in C (as part of nonstandard extensions and proposed standard revisions), the typeof operator returns the static type of the operand. That is, it evaluates to the declared type at that instant in the program, irrespective of its original form.
It just uses compiler magics. Like, GCC's __typeof__
. For compilers that don't provide such magic, it provides an emulation that can detect the type of some expressions, but fails with completely unknown types.
A possible implementation could be to have a list of functions that accept an expression of a given type, and then dispatch from that type to a number using a class template. To make the function template return the number as a compile time entity, we put it into an array dimension
template<typename> struct type2num;
template<int> struct num2type;
template<typename T> typename type2num<T>::dim &dispatch(T const&);
Then it goes from that number back to the type, so that our EMUL_TYPEOF
could directly name the type. So to register a type, we write
#define REGISTER_TYPE(T, N) \
template<> \
struct type2num<T> { \
static int const value = N; \
typedef char dim[N]; \
}; \
template<> \
struct num2type<N> { typedef T type; }
Having this in place, you can write
#define EMUL_TYPEOF(E) \
num2type<sizeof dispatch(E)>::type
Whenever you need to register a type, you write
REGISTER_TYPE(int, 1);
REGISTER_TYPE(unsigned int, 2);
// ...
Of course, now you find you need a mechanism to accept vector<T>
, where you don't know T
in advance and then it gets arbitrary complex. You could create a system where the numbers mean more than just a type. This could probably work:
#define EMUL_TYPEOF(E) \
build_type<sizeof dispatch_1(E), sizeof dispatch_2(E)>::type
This could detect types like int
and also types like shared_ptr<int>
- in other words, types that aren't class template specializations, and class template specializations with one template argument, by doing some kind of systematical mapping
So this becomes
template<int N, int M>
struct build_type {
typedef typename num2tmp<N>::template apply<
typename num2type<M>::type>::type type;
};
template<int N>
struct build_type<1, N> {
typedef num2type<N>::type type;
};
We also need to change the dispatch
template and split it up in two versions, shown below, alongside the REGISTER_TEMP1
for registering one-argument templates
template<typename T> typename type2num<T>::dim1 &dispatch_1(T const&);
template<typename T> typename type2num<T>::dim2 &dispatch_2(T const&);
#define REGISTER_TYPE(T, N) \
template<> \
struct type2num<T> { \
static int const value_dim1 = 1; \
static int const value_dim2 = N; \
typedef char dim1[value_dim1]; \
typedef char dim2[value_dim2]; \
}; \
template<> \
struct num2type<N> { typedef T type; }
#define REGISTER_TMP1(Te, N) \
template<typename T1> \
struct type2num< Te<T1> > { \
static int const value_dim1 = N; \
static int const value_dim2 = type2num<T1>::value_dim2; \
typedef char dim1[value_dim1]; \
typedef char dim2[value_dim2]; \
}; \
template<> struct num2tmp<N> { \
template<typename T1> struct apply { \
typedef Te<T1> type; \
}; \
}
Registering the std::vector
template and both int
variants now look like
REGISTER_TMP1(std::vector, 2);
// ... REGISTER_TMP1(std::list, 3);
REGISTER_TYPE(int, 1);
REGISTER_TYPE(unsigned int, 2);
// ... REGISTER_TYPE(char, 3);
You probably also want to register multiple numbers with each type, one number for each combination of const/volatile or may need more than one number per type for recording *
, &
and such. You also want to support vector< vector<int> >
, so you need more than one number for the template argument too, making build_type
call itself recursively. As you can create an arbitrary long list of integers, you can encode anything into that sequence anyway, so it's just up to your creativity on how to represent these things.
In the end, you are probably reimplementing BOOST_TYPEOF :)
From memory, boost::typeof is implemented via some ?: hacks. First, you start with a class that can be converted to any other class, like
class something {
public:
template<typename T> operator const T&() {
return *(T*)0;
}
};
The ?: rules state that if both sides have the same type, then the result is that type. Else, if one type can be converted to the other type, that is the result type. So by doing
true ? something() : expr;
the result type is (a const reference to) the type of expr- but expr is never actually evaluated, because it's on the false branch. So then you pass it to some place that already has argument deduction, such as function arguments.
template<typename T> void x(const T& t) {
// T is the type of expr.
}
This is somewhat more complex because from memory, C++03 doesn't have reference collapsing, so it is likely somewhat more convoluted than this example- most likely using SFINAE and type traits.
How this is converted into an actual compile-time type that you can pass into a template, I have no idea. As for C++03 providing decltype(), well, there are far bigger problems with the C++03 language, like ODR and declaration/definition orders, no move semantics, etc, which are much worse than not providing decltype.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With