Array operator overloading from typedef?

J

Jeff M.

Sorry if this is a fairly simple thing... I'm an ol' C timer, not much
of one for C++. But, I do have an interesting problem here, and not
sure if there's a solution to it [the way I'd like]. Perhaps someone
here can show me how - or perhaps present me with other options on
getting the behavior I'm looking for.

Also, let me apologize in advance for the "cryptic" post. The codebase
is rather large that I'm working in, and instead of pasting lots of
code, I'll first try just a simplified explanation (and hopefully I'll
do a decent job).

I have a type that's - for all intensive purposes - a kind of variant.
We'll call this class Foo. There's nothing really special in it,
except that it ends with byte[0], and we always allocate more space
for the data that Foo manages.

struct Foo
{
// stuff

char byte[0];
};

Now, we always have a pointer to a Foo - never a reference or a static
allocation. And because Foo is actually hidden a bit, there's a
different type that is exposed to all the other code... let's call it
Thing:

typedef Foo* Thing;

Now, what I'd _like_ to do is overload the [] operator in such a way
that, given a Thing, I can index into Foo::byte and get what I need. I
can obviously put the [] operator inside of Foo, but then I end up
with a lot of code that looks like this:

Thing p = ... ;
doSomething((*p)[4]);

Which isn't exactly very readable. I've also toyed with making Thing
something other than a typedef and putting the [] operator inside of
that. In my side test app, that works, but there would be a lot of
code to change if I decided to go down that road; I'd rather not.

If all else fails, I'm just gonna stick a stupid accessor method in
there (get(i) or similar) and be done with it. But, I'm hoping there's
some college student out there who knows all the wonderful C++
syntactic tricks who can show me a solution. :)

Any ideas?

Thanks!

Jeff M.
 
S

SG

I have a type that's - for all intensive purposes - a kind of variant.
We'll call this class Foo. There's nothing really special in it,
except that it ends with byte[0], and we always allocate more space
for the data that Foo manages.

struct Foo
{
    // stuff

    char byte[0];

};

Now, we always have a pointer to a Foo - never a reference or a static
allocation. And because Foo is actually hidden a bit, there's a
different type that is exposed to all the other code... let's call it
Thing:

typedef Foo* Thing;

Now, what I'd _like_ to do is overload the [] operator in such a way
that, given a Thing, I can index into Foo::byte and get what I need. I
can obviously put the [] operator inside of Foo, but then I end up
with a lot of code that looks like this:

Thing p = ... ;
doSomething((*p)[4]);

Which isn't exactly very readable. I've also toyed with making Thing
something other than a typedef and putting the [] operator inside of
that. In my side test app, that works, but there would be a lot of
code to change if I decided to go down that road; I'd rather not.

This is more of a C99 hack that isn't and won't be supported by C++
IIRC. By "this" I mean the array size of 0 plus an allocation a la

Foo* p = (Foo*)malloc(sizeof(Foo)+sizeof(char)*123);

Also, you can't overload operator[] on pointers. You should write
your own class "Thing" or simply use a vector<char> instead which is
smart enough to manage its array and clean up on destruction.


Cheers!
SG
 
J

joshuamaurice

I have a type that's - for all intensive purposes - a kind of variant.
We'll call this class Foo. There's nothing really special in it,
except that it ends with byte[0], and we always allocate more space
for the data that Foo manages.
struct Foo
{
    // stuff
    char byte[0];

Now, we always have a pointer to a Foo - never a reference or a static
allocation. And because Foo is actually hidden a bit, there's a
different type that is exposed to all the other code... let's call it
Thing:
typedef Foo* Thing;
Now, what I'd _like_ to do is overload the [] operator in such a way
that, given a Thing, I can index into Foo::byte and get what I need. I
can obviously put the [] operator inside of Foo, but then I end up
with a lot of code that looks like this:
Thing p = ... ;
doSomething((*p)[4]);
Which isn't exactly very readable. I've also toyed with making Thing
something other than a typedef and putting the [] operator inside of
that. In my side test app, that works, but there would be a lot of
code to change if I decided to go down that road; I'd rather not.

This is more of a C99 hack that isn't and won't be supported by C++
IIRC.  

Given C++'s goal of being as compatible with C as reasonable, I would
expect C++ to sometime support this behavior, assuming it currently
doesn't. I think it does in part. In C++, you are allowed to access a
POD struct through a pointer to a different POD struct as long you
only access the common leading part (if any). As for declaring or
accessing that ending array, I don't know if it's formally supported,
but it should be supported by all implementations I imagine (possibly
have to change it to size 1 to get it to compile, but otherwise
supported.)
Also, you can't overload operator[] on pointers. You should write
your own class "Thing" or simply use a vector<char> instead which is
smart enough to manage its array and clean up on destruction.

Indeed. For example:

struct thing
{ foo * f;
char& operator[] (size_t x) { return f.byte[x]; }
};

And as SG said, do you really need such hacky code? If this is not a
performance critical application, consider the development and
maintenance costs which could be saved by using something like
std::vector.
 
J

James Kanze

Sorry if this is a fairly simple thing... I'm an ol' C timer,
not much of one for C++. But, I do have an interesting problem
here, and not sure if there's a solution to it [the way I'd
like]. Perhaps someone here can show me how - or perhaps
present me with other options on getting the behavior I'm
looking for.
Also, let me apologize in advance for the "cryptic" post. The
codebase is rather large that I'm working in, and instead of
pasting lots of code, I'll first try just a simplified
explanation (and hopefully I'll do a decent job).
I have a type that's - for all intensive purposes - a kind of
variant. We'll call this class Foo. There's nothing really
special in it, except that it ends with byte[0], and we always
allocate more space for the data that Foo manages.
struct Foo
{
// stuff

char byte[0];

This isn't legal C nor legal C++. Arrays can't have 0 as a
dimension. And the only think you could do with it if you could
declare it is take the address of one past the end.
Now, we always have a pointer to a Foo - never a reference or
a static allocation. And because Foo is actually hidden a bit,
there's a different type that is exposed to all the other
code... let's call it Thing:
typedef Foo* Thing;
Now, what I'd _like_ to do is overload the [] operator in such
a way that, given a Thing, I can index into Foo::byte and get
what I need. I can obviously put the [] operator inside of
Foo,

The only place you can put an operator[] is inside a class. C++
requires it to be a non-static member function. But you still
can't declare byte to have a dimension of 0, and if you declare
it to have a dimension of 1, you can't access beyond the first
element without incurring undefined behavior.
but then I end up with a lot of code that looks like
this:
Thing p = ... ;
doSomething((*p)[4]);
Which isn't exactly very readable.

So make Thing a class which wraps Foo, dand define the
operator[] there.
 
J

James Kanze

I have a type that's - for all intensive purposes - a kind
of variant. We'll call this class Foo. There's nothing
really special in it, except that it ends with byte[0], and
we always allocate more space for the data that Foo manages.
struct Foo
{
// stuff
char byte[0];
};
Now, we always have a pointer to a Foo - never a reference
or a static allocation. And because Foo is actually hidden a
bit, there's a different type that is exposed to all the
other code... let's call it Thing:
typedef Foo* Thing;
Now, what I'd _like_ to do is overload the [] operator in
such a way that, given a Thing, I can index into Foo::byte
and get what I need. I can obviously put the [] operator
inside of Foo, but then I end up with a lot of code that
looks like this:
Thing p = ... ;
doSomething((*p)[4]);
Which isn't exactly very readable. I've also toyed with
making Thing something other than a typedef and putting the
[] operator inside of that. In my side test app, that works,
but there would be a lot of code to change if I decided to
go down that road; I'd rather not.
This is more of a C99 hack that isn't and won't be supported
by C++ IIRC.

It's known as the struct hack, and it's not legal C either. C99
introduced support for it, but not with the syntax he's using
(and C99 doesn't seem to be that widespread anyway).
By "this" I mean the array size of 0 plus an allocation a la
Foo* p = (Foo*)malloc(sizeof(Foo)+sizeof(char)*123);

The allocation isn't the problem:). Accessing the elements.

In this case, there is actually a solution in C++, although not
in C:

class StructHack
{
public:
char& element( int i )
{
return reinterpret_cast< char* >( this + 1 )[ i ] ;
}
} ;

You still have to ensure that all StructHack are allocated
dynamically, with enough additional memory, but you can ensure
this by making the constructor private, and providing a private
member get to take care of the allocation and construction.

And if the actual type of the elements isn't a character type,
you also have to ensure correct alignment. (G++ uses this
technique in its implementation of std::basic_string. Without
ensuring correct alignment, and on my machine,
std::basic_string said:
Also, you can't overload operator[] on pointers. You should
write your own class "Thing" or simply use a vector<char>
instead which is smart enough to manage its array and clean up
on destruction.

Yes, but that would result in maintainable code.
 
M

Michael DOUBEZ

SG said:
I have a type that's - for all intensive purposes - a kind of variant.
We'll call this class Foo. There's nothing really special in it,
except that it ends with byte[0], and we always allocate more space
for the data that Foo manages.

struct Foo
{
// stuff

char byte[0];

};

Now, we always have a pointer to a Foo - never a reference or a static
allocation. And because Foo is actually hidden a bit, there's a
different type that is exposed to all the other code... let's call it
Thing:

typedef Foo* Thing;

Now, what I'd _like_ to do is overload the [] operator in such a way
that, given a Thing, I can index into Foo::byte and get what I need. I
can obviously put the [] operator inside of Foo, but then I end up
with a lot of code that looks like this:

Thing p = ... ;
doSomething((*p)[4]);

Which isn't exactly very readable. I've also toyed with making Thing
something other than a typedef and putting the [] operator inside of
that. In my side test app, that works, but there would be a lot of
code to change if I decided to go down that road; I'd rather not.

This is more of a C99 hack that isn't and won't be supported by C++
IIRC.

Even C99 doesn't support it, this is usually a vendor extension to the
language. The standard form is:
struct Foo
{
// stuff
char byte[];
};

From ISO/IEC 9899:1999, Section 6.7.2.1§16:
"As a special case, the last element of a structure with more than one
named member may have an incomplete array type; this is called a
flexible array member."
By "this" I mean the array size of 0 plus an allocation a la

Foo* p = (Foo*)malloc(sizeof(Foo)+sizeof(char)*123);

Also, you can't overload operator[] on pointers. You should write
your own class "Thing" or simply use a vector<char> instead which is
smart enough to manage its array and clean up on destruction.

When mapping structures of variable size (network or open type font
data), it is often useful.
 

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

No members online now.

Forum statistics

Threads
473,743
Messages
2,569,478
Members
44,899
Latest member
RodneyMcAu

Latest Threads

Top