ill-formed reference to pointer

M

m0shbear

I'm trying to use this code to check the compiler's output to see if
the inlining is aggressive enough to use inline functions instead of
#defines and I'm getting the following error: "invalid initialization
of reference of type ‘const unsigned char*&’ from expression of type
‘u8*’".
Does the inline lowercase match the macro uppercase?

code:
extern "C" {
#include <stdint.h>
}

typedef uint8_t u8;
typedef uint64_t u64be;

template <typename T>
struct itype {
typedef T value;
typedef T& reference;
typedef T const& const_reference;
typedef T* pointer;
typedef T const* const_pointer;
};

#define PTR_CAST(T, p) (reinterpret_cast<T*>(p))

#define XF64(dst,src) *PTR_CAST(u64be,dst) ^= *PTR_CAST(u64be
const,src); (src) += 8

template<typename T, typename P> inline T* ptr_cast(P* p) { return
reinterpret_cast<T*>(p); }

inline void xf64(itype<u8>::pointer dst,
itype<itype<u8>::const_pointer>::reference src) {
*ptr_cast<u64be>(dst) ^= *ptr_cast<u64be const>(src); src += 8;
}

int main() {
u8 fop[64], foq[64], fos[64],fot[64];
u8* foP=fop;
u8* foQ=foq;
u8* foS=fos;
u8* foT=fot;
xf64(foP,foQ);
XF64(foS, foT);
}
 
I

Ian Collins

I'm trying to use this code to check the compiler's output to see if
the inlining is aggressive enough to use inline functions instead of
#defines

It's highly unlikely that you would have to resort to macros, unless
your compiler is very old and scummy.
and I'm getting the following error: "invalid initialization
of reference of type ‘const unsigned char*&’ from expression of type
‘u8*’".

That's right, the types don't match.
Does the inline lowercase match the macro uppercase?

Not really, macro arguments don't have types.
code:
extern "C" {
#include<stdint.h>
}

You shouldn't have to add the extern "C" here.
typedef uint8_t u8;
typedef uint64_t u64be;

These are horrible!
template<typename T>
struct itype {
typedef T value;
typedef T& reference;
typedef T const& const_reference;
typedef T* pointer;
typedef T const* const_pointer;
};

#define PTR_CAST(T, p) (reinterpret_cast<T*>(p))

Why do this?
#define XF64(dst,src) *PTR_CAST(u64be,dst) ^= *PTR_CAST(u64be
const,src); (src) += 8

template<typename T, typename P> inline T* ptr_cast(P* p) { return
reinterpret_cast<T*>(p); }

Or this?
 
M

m0shbear

It's highly unlikely that you would have to resort to macros, unless
your compiler is very old and scummy.


That's right, the types don't match.


Not really, macro arguments don't have types.


You shouldn't have to add the extern "C" here.


These are horrible!

I use the 'be' suffix to note that the variable is explictly big-
endian instead of native-endian. Why is that bad?
Why do this?

Long symbol names become annoying to read, and reinterpret_cast is
used enough that creating a wrapper macro for it is clearer.

The macro I'm trying to convert into an inline function.
What I'm doing is a wordsize-optimized endian-agnostic XOR of a set of
bytes, to minimize the amount of instructions generated. And the C++
version of the C-style cast maps to reinterpret_cast.

Now why does the compiler auto-constify any non-const pointer used as
the second argument in memcpy() but refuses to do so here?



A somewhat typesafe version of PTR_CAST.
 
M

m0shbear

I use the 'be' suffix to note that the variable is explictly big-
endian instead of native-endian. Why is that bad?





Long symbol names become annoying to read, and reinterpret_cast is
used enough that creating a wrapper macro for it is clearer.




The macro I'm trying to convert into an inline function.
What I'm doing is a wordsize-optimized endian-agnostic XOR of a set of
bytes, to minimize the amount of instructions generated. And the C++
version of the C-style cast maps to reinterpret_cast.

Now why does the compiler auto-constify any non-const pointer used as
the second argument in memcpy() but refuses to do so here?

Ugh. G++ can constify or make-reference, but can't automagically do
the former then the latter. Is there a workaround?
 
I

Ian Collins

I use the 'be' suffix to note that the variable is explictly big-
endian instead of native-endian. Why is that bad?

I guess that's OK, but the typedef doesn't give you any protection form
mixing u64be and uint64_t types.
Long symbol names become annoying to read, and reinterpret_cast is
used enough that creating a wrapper macro for it is clearer.

Using reinterpret_cast anything other than infrequently should set alarm
bells wringing.
The macro I'm trying to convert into an inline function.
What I'm doing is a wordsize-optimized endian-agnostic XOR of a set of
bytes, to minimize the amount of instructions generated. And the C++
version of the C-style cast maps to reinterpret_cast.

Why don't you just write

template <typename S, typename D>
void xf64( S* dst, const D* src )
{
*reinterpret_cast<uint64_t*>(dst) ^= *reinterpret_cast<const
uint64_t*>(src);
src += 8;
}

Which is closer to your macro?

It either case, things could turn nasty if the pointers aren't correctly
aligned for uint64_t.
Now why does the compiler auto-constify any non-const pointer used as
the second argument in memcpy() but refuses to do so here?

Because the parameter is a reference.
 
J

Joshua Maurice

Long symbol names become annoying to read, and reinterpret_cast is
used enough that creating a wrapper macro for it is clearer.

Meh. I'm going to have to disagree. First, reinterpret_cast ought to
be rare enough that this isn't even an issue. static_cast and
dynamic_cast, or fix your likely broken code, please.

Second, I think it's really poor form to wrap any standard library
feature or function. It's just obfuscation.
 
M

m0shbear

I guess that's OK, but the typedef doesn't give you any protection form
mixing u64be and uint64_t types.

It's like a #define which evaluates to nothing - it serves more as a
pre-/post-condition note than a compiler verifiable pre-/post-
condition.
Using reinterpret_cast anything other than infrequently should set alarm
bells wringing.

It's only in the bitwise portion of the inner loop. It was faster to
macro it and I was too lazy to regex it via s/Ptr_cast<([a-z0-9_]+ ?
Why don't you just write

template <typename S, typename D>
void xf64( S* dst, const D* src )
{
   *reinterpret_cast<uint64_t*>(dst) ^= *reinterpret_cast<const
uint64_t*>(src);
   src += 8;

}

Which is closer to your macro?

Nope: src will be unchanged since it's copied by value instead of by
reference.
In the macro, the ability to assume that the src token is a reference
is a precondition.

In any case, the function's arguments are (uint8_t *, const uint8_t
*), so it works. I was just playing around with constness and
references and got bit.
It either case, things could turn nasty if the pointers aren't correctly
aligned for uint64_t.

Indeed, which is why I'm making a padding wrapper for unaligned xor,
one of the reasons being the ability to add simd intrinsics at a later
time. Not all cpus support unaligned 64/128-bit load-store between mem
and simd reg(s).
Because the parameter is a reference.

References do a damn good job of killing off C tricks I've learned
over the years :p
 
M

m0shbear

Meh. I'm going to have to disagree. First, reinterpret_cast ought to
be rare enough that this isn't even an issue. static_cast and
dynamic_cast, or fix your likely broken code, please.

Second, I think it's really poor form to wrap any standard library
feature or function. It's just obfuscation.

Polymorphic casting is unused - dynamic_cast is unsuitable;
The goal is pointer coercion in order to grab the bits - static_cast
is unsuitable.
Static_cast would also not work for returning function pointers from
dlopen or run-time named parameters.
 
I

Ian Collins

Nope: src will be unchanged since it's copied by value instead of by
reference.
In the macro, the ability to assume that the src token is a reference
is a precondition.
In any case, the function's arguments are (uint8_t *, const uint8_t
*), so it works. I was just playing around with constness and
references and got bit.

OK,

const uint8_t* xf64( uint8_t* dst, const uint8_t* src )
{
*reinterpret_cast<uint64_t*>(dst) ^= *reinterpret_cast<const
uint64_t*>(src);
return src + 8;
}

But wouldn't you want both src and dst to advance?
References do a damn good job of killing off C tricks I've learned
over the years :p

You'd hit the same problem with pointers to pointers.
 
V

Vaclav Haisman

m0shbear wrote, On 30.1.2011 6:03:
I'm trying to use this code to check the compiler's output to see if
the inlining is aggressive enough to use inline functions instead of
#defines and I'm getting the following error: "invalid initialization
of reference of type ‘const unsigned char*&’ from expression of type
‘u8*’".
Does the inline lowercase match the macro uppercase?

code:
extern "C" {
#include <stdint.h>
}

typedef uint8_t u8;
typedef uint64_t u64be;

template <typename T>
struct itype {
typedef T value;
typedef T& reference;
typedef T const& const_reference;
typedef T* pointer;
typedef T const* const_pointer;
};

#define PTR_CAST(T, p) (reinterpret_cast<T*>(p))

#define XF64(dst,src) *PTR_CAST(u64be,dst) ^= *PTR_CAST(u64be
const,src); (src) += 8

template<typename T, typename P> inline T* ptr_cast(P* p) { return
reinterpret_cast<T*>(p); }

inline void xf64(itype<u8>::pointer dst,
itype<itype<u8>::const_pointer>::reference src) {
*ptr_cast<u64be>(dst) ^= *ptr_cast<u64be const>(src); src += 8;
}

int main() {
u8 fop[64], foq[64], fos[64],fot[64];
u8* foP=fop;
u8* foQ=foq;
u8* foS=fos;
u8* foT=fot;
xf64(foP,foQ);
XF64(foS, foT);
Do you realise that casting from this u8* to u64be* and then accessing the
value as if it were u64be* is undefined behaviour?
 
M

m0shbear

OK,

const uint8_t* xf64( uint8_t* dst, const uint8_t* src )
{
   *reinterpret_cast<uint64_t*>(dst) ^= *reinterpret_cast<const
uint64_t*>(src);
   return src + 8;

}

But wouldn't you want both src and dst to advance?

Suppose dst points to a memory-mapped register, and src to an array.
Then dst points to the xor-reduction of src.

The goal is to evaulate the expression, where [x:y] is byte range:
*dst = *src[0:7] ^ *src[8:15] ^ ... ^ *src[n-8:n-1].

So it is useful to advance src, but not dst.

However, the double-or-nothing approach is also useful. This allows
for the elimination of the reference and simply use a pointer, and do
the post-increment of src in parallel with the increment of the loop
counter. It will also potentially stop enough off-by-one errors to
make it worth spending a minute re-coding.
You'd hit the same problem with pointers to pointers.

Actually, no. I've managed to hack it into working with const_cast and
reinterpret_cast.
 
M

m0shbear

m0shbear wrote, On 30.1.2011 6:03:
I'm trying to use this code to check the compiler's output to see if
the inlining is aggressive enough to use inline functions instead of
#defines and I'm getting the following error: "invalid initialization
of reference of type ‘const unsigned char*&’ from expression of type
‘u8*’".
Does the inline lowercase match the macro uppercase?
code:
extern "C" {
#include <stdint.h>
}
typedef uint8_t u8;
typedef uint64_t u64be;
template <typename T>
struct itype {
   typedef T value;
   typedef T& reference;
   typedef T const& const_reference;
   typedef T* pointer;
   typedef T const* const_pointer;
};
#define PTR_CAST(T, p) (reinterpret_cast<T*>(p))
#define XF64(dst,src) *PTR_CAST(u64be,dst) ^= *PTR_CAST(u64be
const,src); (src) += 8
template<typename T, typename P> inline T* ptr_cast(P* p) { return
reinterpret_cast<T*>(p); }
inline void xf64(itype<u8>::pointer dst,
itype<itype<u8>::const_pointer>::reference src) {
   *ptr_cast<u64be>(dst) ^= *ptr_cast<u64be const>(src); src += 8;
}
int main() {
   u8 fop[64], foq[64], fos[64],fot[64];
   u8* foP=fop;
   u8* foQ=foq;
   u8* foS=fos;
   u8* foT=fot;
   xf64(foP,foQ);
   XF64(foS, foT);

Do you realise that casting from this u8* to u64be* and then accessing the
value as if it were u64be* is undefined behaviour?

A contiguous region of memory is a contiguous region of memory. If the
address is 8/16-aligned, going from byte access to 64-bit access has a
noticeable performance boost. It's like using 64-bit copy/cmp in mem/
strXXX() for aligned bytes and falling back to 8-bit after alignment.
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

Forum statistics

Threads
473,755
Messages
2,569,537
Members
45,021
Latest member
AkilahJaim

Latest Threads

Top