Google Groups no longer supports new Usenet posts or subscriptions. Historical content remains viewable.
Dismiss

void** casting

67 views
Skip to first unread message

mark

unread,
Sep 14, 2015, 2:18:07 PM9/14/15
to
I'm using a C library that is using void** arrays (array of pointers to
a struct).

Something like:

typedef struct {
void** data;
unsigned int length;
} Vector;

typedef struct {
...
} S1;

typedef struct {
...
} S2;

Vector::data is an array of S1* or S2*.

Is it safe to cast Vector::data to S1** / S2**?

Victor Bazarov

unread,
Sep 14, 2015, 2:23:02 PM9/14/15
to
Generally, no.

Are you required to be source-code compatible with C? If not, don't use
this stuff. Consider standard containers.

V
--
I do not respond to top-posted replies, please don't ask

mark

unread,
Sep 14, 2015, 2:26:45 PM9/14/15
to
On 2015-09-14 20:22, Victor Bazarov wrote:
> On 9/14/2015 2:17 PM, mark wrote:
>> Is it safe to cast Vector::data to S1** / S2**?
>
> Generally, no.
>
> Are you required to be source-code compatible with C? If not, don't use
> this stuff. Consider standard containers.

I'm stuck with using that C library. Converting the data structures to
C++ ones is not an option.

How unsafe is it?

mark

unread,
Sep 14, 2015, 2:37:45 PM9/14/15
to
To clarify, it would be good enough if it is semi-portable to platforms
where sizeof(char*) == sizeof(int*) == sizeof(void*) == sizeof(S1*).

Luca Risolia

unread,
Sep 14, 2015, 2:49:46 PM9/14/15
to
Il 14/09/2015 20:26, mark ha scritto:
>>> Is it safe to cast Vector::data to S1** / S2**?


> How unsafe is it?

Different types may be aligned to different boundaries. Unless you know
what the original type being pointed is, accessing data members via such
casted pointers may crash your program (if you are lucky).

Martin Shobe

unread,
Sep 14, 2015, 3:18:15 PM9/14/15
to
Technically no. Even getting data to point to an array of S1* or S2* is
already a problem. However, chances are if it works in that direction,
it will work in reverse and I don't see any better options. (Assuming
you can't fix the C library.)

Martin Shobe

Paavo Helde

unread,
Sep 14, 2015, 4:08:46 PM9/14/15
to
mark <ma...@invalid.invalid> wrote in news:mt741o$eq2$1...@dont-email.me:
In this case there is a good chance it might work. Maybe the C library is
itself already stepping over the line and using unportable casts, if so,
then there is not much point to avoid them in your code.

Cheers
Paavo

Öö Tiib

unread,
Sep 14, 2015, 9:43:02 PM9/14/15
to
When you know that exactly S1* is (implicit or static_) cast to void*
then it is guaranteed to be safe to cast it back.

Casting pointers to arrays of pointers you run into territory that
standard does not guarantee:

"A pointer to void shall have the same representation and
alignment requirements as a pointer to a character type.
Similarly, pointers to qualified or unqualified versions of
compatible types shall have the same representation and
alignment requirements. All pointers to structure types shall
have the same representation and alignment requirements as each
other."

So it does not say that void and struct pointer have same requirements.
When you don't know if void pointers and struct pointers have same
size and alignment requirements then you can at least static assert that.

static_assert( alignof(void*)==alignof(S1*)
&& sizeof(void*)==sizeof(S1*)
, "We need to cast between void** and S1** here!" );

I do not know any way to static assert any bitwise representation
however since 'reinterpret_cast' is not required to work compile time.

Bitwise representation is same on all implementations I know of
to make the casts cheap for C programs so it is perhaps risk worth
taking being stuck like you describe.

Jorgen Grahn

unread,
Sep 15, 2015, 5:01:57 AM9/15/15
to
On Mon, 2015-09-14, Martin Shobe wrote:
> On 9/14/2015 1:17 PM, mark wrote:
>> I'm using a C library that is using void** arrays (array of pointers to
>> a struct).
>>
>> Something like:
>>
>> typedef struct {
>> void** data;
>> unsigned int length;
>> } Vector;
>>
>> typedef struct {
>> ...
>> } S1;
>>
>> typedef struct {
>> ...
>> } S2;
>>
>> Vector::data is an array of S1* or S2*.
>>
>> Is it safe to cast Vector::data to S1** / S2**?
>
> Technically no. Even getting data to point to an array of S1* or S2* is
> already a problem.

Aren't we talking about two different things?

void** foo = something_with_S1();
S1* s1 = static_cast<S1*>(foo[0]); // ok, if you're sure there are
// S1 objects there
S2* s2 = static_cast<S2*>(foo[0]); // not ok

S1** bar = static_cast<S1**>(foo); // error

That last line was what the OP asked for. And it doesn't even compile
-- I have to replace it with a C-style cast (or maybe
reinterpret_cast would be enough?)

I think of it as if sizeof(void*) may be larger than sizeof(S1*).

> However, chances are if it works in that direction,
> it will work in reverse and I don't see any better options. (Assuming
> you can't fix the C library.)

If he really needs an array of S1*, he could also copy into one,
element by element.

/Jorgen

--
// Jorgen Grahn <grahn@ Oo o. . .
\X/ snipabacken.se> O o .

mark

unread,
Sep 15, 2015, 7:36:28 AM9/15/15
to
The void** casting is a bit close to UB, so I'll probably avoid that.

I've come up with a nice kludge to wrap the C vector with zero overhead
(GCC optimizes the C++ wrapper completely away). This should have
well-defined behavior with C++11.


#include <boost/iterator/transform_iterator.hpp>
#include <iostream>
#include <memory>

typedef struct {
void** data;
unsigned int length;
} Vector;

typedef struct {
int i;
} S1;

typedef struct {
const char* s;
} S2;

static const int elem_count = 5;
static const char* int_str[] = {"0", "1", "2", "3", "4"};

void initTestVecS1(Vector& vec) {
vec.data = static_cast<void**>(malloc(sizeof(void*) * elem_count));
vec.length = elem_count;
for(int i = 0; i < elem_count; i++) {
vec.data[i] = new S1{i};
}
}

void initTestVecS2(Vector& vec) {
vec.data = static_cast<void**>(malloc(sizeof(void*) * elem_count));
vec.length = elem_count;
for(int i = 0; i < elem_count; i++) {
vec.data[i] = new S2{int_str[i]};
}
}

template<typename node_type>
node_type& cast_node(void* ptr) {
return *static_cast<node_type*>(ptr);
}

template<typename node_type>
struct VectorWrapper {
Vector data;
auto begin() {
return boost::make_transform_iterator(
&data.data[0], cast_node<node_type>);
}
auto end() {
return boost::make_transform_iterator(
&data.data[data.length], cast_node<node_type>);
}
static VectorWrapper<node_type>& toWrapper(Vector& vec) {
return *reinterpret_cast<VectorWrapper<node_type>*>(&vec);
}
};

int main() {
Vector v1; initTestVecS1(v1);
auto vw1 = VectorWrapper<S1>::toWrapper(v1);

for(int i = 0; i < elem_count; i++)
std::cout << static_cast<S1*>(v1.data[i])->i << " ";
std::cout << std::endl;

for(const auto& elem : vw1) std::cout << elem.i << " ";
std::cout << std::endl;

Vector v2; initTestVecS2(v2);
auto vw2 = VectorWrapper<S2>::toWrapper(v2);

for(const auto& elem : vw2) std::cout << elem.s << " ";
std::cout << std::endl;
}

Martin Shobe

unread,
Sep 15, 2015, 9:10:54 AM9/15/15
to
On 9/15/2015 4:01 AM, Jorgen Grahn wrote:
> On Mon, 2015-09-14, Martin Shobe wrote:
>> On 9/14/2015 1:17 PM, mark wrote:
>>> I'm using a C library that is using void** arrays (array of pointers to
>>> a struct).
>>>
>>> Something like:
>>>
>>> typedef struct {
>>> void** data;
>>> unsigned int length;
>>> } Vector;
>>>
>>> typedef struct {
>>> ...
>>> } S1;
>>>
>>> typedef struct {
>>> ...
>>> } S2;
>>>
>>> Vector::data is an array of S1* or S2*.
>>>
>>> Is it safe to cast Vector::data to S1** / S2**?
>>
>> Technically no. Even getting data to point to an array of S1* or S2* is
>> already a problem.
>
> Aren't we talking about two different things?

To a certain degree. The comments above are directed more to the
situation itself, as opposed to what can be done to retrieve the asked
for information. To the best of my knowledge, neither C nor C++ make any
provisions for passing around arrays of S1 * (or S2 *) as arrays of void *.

> void** foo = something_with_S1();
> S1* s1 = static_cast<S1*>(foo[0]); // ok, if you're sure there are
> // S1 objects there

No, even this isn't certain to be ok. It's ok if the array is an array
void * that happen to point to S1 objects. But the original description
is that it's an array of S1 *. (In all fairness, it's about as likely to
work as the last line, but neither C nor C++ require it to.)

> S2* s2 = static_cast<S2*>(foo[0]); // not ok
>
> S1** bar = static_cast<S1**>(foo); // error
>
> That last line was what the OP asked for.

Yes, and that's what I was talking about (except, as you note later, it
would need a c-style or reinterpret_cast).

> And it doesn't even compile
> -- I have to replace it with a C-style cast (or maybe
> reinterpret_cast would be enough?)

> I think of it as if sizeof(void*) may be larger than sizeof(S1*).

There are other potential issues as well.

>> However, chances are if it works in that direction,
>> it will work in reverse and I don't see any better options. (Assuming
>> you can't fix the C library.)
>
> If he really needs an array of S1*, he could also copy into one,
> element by element.

At this point, even that isn't certain to work.

Martin Shobe

mark

unread,
Sep 15, 2015, 9:59:36 AM9/15/15
to
On 2015-09-15 15:10, Martin Shobe wrote:
>
>> void** foo = something_with_S1();
>> S1* s1 = static_cast<S1*>(foo[0]); // ok, if you're sure there are
>> // S1 objects there
>
> No, even this isn't certain to be ok. It's ok if the array is an array
> void * that happen to point to S1 objects. But the original description
> is that it's an array of S1 *. (In all fairness, it's about as likely to
> work as the last line, but neither C nor C++ require it to.)

The original description is sloppy. It's not strictly an array of S1*,
but rather an array of void* which point to S1.

The array is allocated for the proper void** size and accessed in line
with those static_casts. So the void* <-> S1* conversion magic happens.

Scott Lurndal

unread,
Sep 15, 2015, 10:49:04 AM9/15/15
to
In any case, on any architecture that matters for C++ programming,
an array of pointers is an array of pointers whether they're pointers
to void or pointers to an object. Leaving aside the standard and UB,
casting (using reinterpret_cast or c-style casting) "void **" to "S1 **"
will work correctly.

Martin Shobe

unread,
Sep 15, 2015, 11:40:06 AM9/15/15
to
On 9/15/2015 8:59 AM, mark wrote:
> On 2015-09-15 15:10, Martin Shobe wrote:
>>
>>> void** foo = something_with_S1();
>>> S1* s1 = static_cast<S1*>(foo[0]); // ok, if you're sure there are
>>> // S1 objects there
>>
>> No, even this isn't certain to be ok. It's ok if the array is an array
>> void * that happen to point to S1 objects. But the original description
>> is that it's an array of S1 *. (In all fairness, it's about as likely to
>> work as the last line, but neither C nor C++ require it to.)
>
> The original description is sloppy. It's not strictly an array of S1*,
> but rather an array of void* which point to S1.

Then the cast above is ok, and what I would recommend. Even if Scott
Lurndal is correct in his response, it's better to not make that
assumption unless there's a significant gain. For most uses, it's too
easy to wrap in something that does the correct thing for there to be a
significant gain.

Martin Shobe

mark

unread,
Sep 15, 2015, 12:45:15 PM9/15/15
to
On 2015-09-15 17:39, Martin Shobe wrote:

> Then the cast above is ok, and what I would recommend. Even if Scott
> Lurndal is correct in his response, it's better to not make that
> assumption unless there's a significant gain. For most uses, it's too
> easy to wrap in something that does the correct thing for there to be a
> significant gain.

I'm happy with the C++ wrapper I posted - no overhead (and no UB
according to my reading of the standard).

Having been burned by aggressive standard interpretations of UB that GCC
has done in the past (resulting in miscompiled code), I try to avoid
grey areas.

Jorgen Grahn

unread,
Sep 16, 2015, 2:53:25 AM9/16/15
to
On Tue, 2015-09-15, Martin Shobe wrote:
> On 9/15/2015 8:59 AM, mark wrote:
>> On 2015-09-15 15:10, Martin Shobe wrote:
>>>
>>>> void** foo = something_with_S1();
>>>> S1* s1 = static_cast<S1*>(foo[0]); // ok, if you're sure there are
>>>> // S1 objects there
>>>
>>> No, even this isn't certain to be ok. It's ok if the array is an array
>>> void * that happen to point to S1 objects. But the original description
>>> is that it's an array of S1 *. (In all fairness, it's about as likely to
>>> work as the last line, but neither C nor C++ require it to.)
>>
>> The original description is sloppy. It's not strictly an array of S1*,
>> but rather an array of void* which point to S1.
>
> Then the cast above is ok, and what I would recommend.

Ah, now I see the different interpretations, too. Yes, "array of
void* which point to S1" was how I read the original posting.
0 new messages