views:

109

answers:

3

Purpose and craziness aside, is there a way to achieve this in C++?

template <typename P>
void Q void_cast(P Q *p) const
{
    return static_cast<P Q *>(p);
}

I'm effectively trying to cast a pointer to a void pointer type whilst keeping any const, restrict and other qualifiers (denoted by Q).

I was under the impression there was stuff in the C++ standard library (or less desirably, in Boost), that allowed you to "tweak" properties of types, with finer granularity than say const_cast or static_cast.

+6  A: 

So, you want const X* -> const void*, volatile X* -> volatile void*, etc.

You can do this with a set of overloads:

template<typename P>
void* void_cast(P* p)
{
    return p;
}

template<typename P>
void const* void_cast(P const* p)
{
    return p;
}

template<typename P>
void volatile* void_cast(P volatile* p)
{
    return p;
}

template<typename P>
void const volatile* void_cast(P const volatile* p)
{
    return p;
}

The new type traits are things like add_const, add_volatile, remove_const and remove_volatile. They work for transforming the cv-qualifiers of a type in a known way, not for applying the cv-qualifiers of one type to another.

Anthony Williams
You've identified precisely what I'm after, and explained how type traits relate to it. I'm still hopeful there is a less verbose way to achieve this.
Matt Joiner
@Matt: The only less verbose way is probably not to use a cast at all, because you can't capture the cv-qualifiers in a separate template parameter. Conversion to suitably qualified void pointer happens implicitly, and only a C-cast can remove the qualifiers.
visitor
Ultimately, I think these don't need to be templates. `void const* void_cast(void const* v) { return v; }` and friends are enough, I believe.
Johannes Schaub - litb
Possibly. The templates require a pointer, and won't work with a class that is implicitly convertible to a pointer unless you specify the pointer type (e.g. `void_cast<Y>(x)`) or cast the type to a pointer first (e.g. `void_cast((Y*)x)`), whereas plain functions will accept implicit conversions to the parameter type.
Anthony Williams
@visitor: I've removed the unnecessary `static_cast`s
Anthony Williams
How can you drop the static_casts? That was my whole reason for considering a void_cast in the first place: C++ doesn't allow converting to and from `void *` without casting (but C does).
Matt Joiner
In C++, a `T*` will implicitly convert to a `void*`, but not the other way round. cv-qualifiers must be preserved, so a `const T*` will implicitly convert to a `const void*`, but not to a `void*`.
Anthony Williams
@Anthony Williams: Never knew that (although I do it without noticing), thanks.
Matt Joiner
+1  A: 

In the boost type traits lib are some tools to remove qulifiers

http://www.boost.org/doc/libs/1_44_0/libs/type_traits/doc/html/index.html

MarcoH
+1  A: 
template<class From>
typename copy_rpcv<void, From*>::type void_cast(From *p) {
  return p;  // implicit conversion works
}

With a little TMP utility:

// "copy ref/pointer/const/volatile"
template<class To, class From>
struct copy_rpcv {
  typedef To type;
};
template<class To, class From> struct copy_rpcv<To, From&        > { typedef typename copy_rpcv<To, From>::type&         type; };
template<class To, class From> struct copy_rpcv<To, From*        > { typedef typename copy_rpcv<To, From>::type*         type; };
template<class To, class From> struct copy_rpcv<To, From const   > { typedef typename copy_rpcv<To, From>::type const    type; };
template<class To, class From> struct copy_rpcv<To, From volatile> { typedef typename copy_rpcv<To, From>::type volatile type; };

You'd have to add another case for the restrict specifier; it probably would work just fine, but I don't know how it, as a non-standard feature in 03 (is it in 0x?), interacts:

template<class To, class From> struct copy_rpcv<To, From* restrict> { typedef typename copy_rpcv<To, From>::type* restrict type; };

My solution differs from Anthony's in preserving "nested" type info:

int *const *const p = 0;
void *const *const v = void_cast(p);
Roger Pate