Array operator overloading from typedef?

Discussion in 'C++' started by Jeff M., Apr 20, 2009.

  1. Jeff M.

    Jeff M. Guest

    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.
    Jeff M., Apr 20, 2009
    #1
    1. Advertising

  2. Jeff M.

    SG Guest

    On 20 Apr., 05:35, "Jeff M." <> wrote:
    >
    > 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
    SG, Apr 20, 2009
    #2
    1. Advertising

  3. Jeff M.

    Guest

    On Apr 19, 10:06 pm, SG <> wrote:
    > On 20 Apr., 05:35, "Jeff M." <> wrote:
    >
    >
    >
    >
    >
    > > 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.
    , Apr 20, 2009
    #3
  4. Jeff M.

    James Kanze Guest

    On Apr 20, 5:35 am, "Jeff M." <> wrote:
    > 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.

    --
    James Kanze (GABI Software) email:
    Conseils en informatique orientée objet/
    Beratung in objektorientierter Datenverarbeitung
    9 place Sémard, 78210 St.-Cyr-l'École, France, +33 (0)1 30 23 00 34
    James Kanze, Apr 20, 2009
    #4
  5. Jeff M.

    James Kanze Guest

    On Apr 20, 7:06 am, SG <> wrote:
    > On 20 Apr., 05:35, "Jeff M." <> wrote:
    > > 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<double> core dumps.)

    > 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.

    --
    James Kanze (GABI Software) email:
    Conseils en informatique orientée objet/
    Beratung in objektorientierter Datenverarbeitung
    9 place Sémard, 78210 St.-Cyr-l'École, France, +33 (0)1 30 23 00 34
    James Kanze, Apr 20, 2009
    #5
  6. SG wrote:
    > On 20 Apr., 05:35, "Jeff M." <> wrote:
    >> 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.

    --
    Michael
    Michael DOUBEZ, Apr 20, 2009
    #6
    1. Advertising

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

It takes just 2 minutes to sign up (and it's free!). Just click the sign up button to choose a username and then you can ask your own questions on the forum.
Similar Threads
  1. John Smith
    Replies:
    2
    Views:
    417
    Ivan Vecerina
    Oct 6, 2004
  2. Replies:
    11
    Views:
    726
    James Kanze
    May 16, 2007
  3. hurcan solter
    Replies:
    3
    Views:
    723
    Cholo Lennon
    Aug 29, 2007
  4. Replies:
    11
    Views:
    555
  5. Replies:
    2
    Views:
    306
Loading...

Share This Page