overloading global dereference operator?

Discussion in 'C++' started by TuxC0d3, Dec 4, 2005.

  1. TuxC0d3

    TuxC0d3 Guest

    Hi!

    I'm diving into the some more ++ specific aspects of c++ (and finally
    accepting that c++ is more than "a plus added to c" :), so that means
    using namespaces, templates, std::strings, lists, vectors, operator
    overloading and what not.. And i was wondering if there is a way to
    override the global dereference operator, so to be able to check the
    address that one tries to dereference. This gives the ability to throw
    an exception when one would try to dereference NULL for instance.

    I have tried several different ways to do it, but gcc doesn't like what
    i'm asking of him...

    This attempt is the most reasonable to me:
    43 template<class T> T& operator * (T* ptr)
    44 {
    45 if (!ptr)
    46 throw EAccessViolation();
    47
    48 return (*ptr);
    49 }
    (sorry about the line numbers.. Just the vim settings on my console)

    Even though this seems right to me, g++ gives me the following
    complaint when i compile that like thus:
    [tim@server tests]$ g++ -Wall -o test operatornew.cpp > c.log 2>&1
    [tim@server tests]$ cat c.log
    operatornew.cpp:44: error: `T& operator*(T*)' must have an argument of
    class or
    enumerated type
    [tim@server tests]$

    Could it be that the compiler gets confused and/or thinks that i want
    to overide the multiply operator? Or am i just beeing totaly clueless
    here? :)

    I have searched for this for what feels like all around the net, but i
    can't seem to find anything like it. Everything that comes up about
    dereference operator overloading is the operator->() for inside a
    class.. I have also tried putting this global, which naturaly didn't
    work becouse -> is obviously only used for members..

    Can anyone give me a lead on this and/or provide some example code?
    Tnx!

    Tim.
     
    TuxC0d3, Dec 4, 2005
    #1
    1. Advertising

  2. TuxC0d3

    John Carson Guest

    "TuxC0d3" <> wrote in message
    news:
    > Hi!
    >
    > I'm diving into the some more ++ specific aspects of c++ (and finally
    > accepting that c++ is more than "a plus added to c" :), so that means
    > using namespaces, templates, std::strings, lists, vectors, operator
    > overloading and what not.. And i was wondering if there is a way to
    > override the global dereference operator, so to be able to check the
    > address that one tries to dereference. This gives the ability to throw
    > an exception when one would try to dereference NULL for instance.
    >
    > I have tried several different ways to do it, but gcc doesn't like
    > what i'm asking of him...
    >
    > This attempt is the most reasonable to me:
    > 43 template<class T> T& operator * (T* ptr)
    > 44 {
    > 45 if (!ptr)
    > 46 throw EAccessViolation();
    > 47
    > 48 return (*ptr);
    > 49 }
    > (sorry about the line numbers.. Just the vim settings on my console)
    >
    > Even though this seems right to me, g++ gives me the following
    > complaint when i compile that like thus:
    > [tim@server tests]$ g++ -Wall -o test operatornew.cpp > c.log 2>&1
    > [tim@server tests]$ cat c.log
    > operatornew.cpp:44: error: `T& operator*(T*)' must have an argument of
    > class or
    > enumerated type
    > [tim@server tests]$
    >
    > Could it be that the compiler gets confused and/or thinks that i want
    > to overide the multiply operator? Or am i just beeing totaly clueless
    > here? :)
    >
    > I have searched for this for what feels like all around the net, but i
    > can't seem to find anything like it. Everything that comes up about
    > dereference operator overloading is the operator->() for inside a
    > class.. I have also tried putting this global, which naturaly didn't
    > work becouse -> is obviously only used for members..
    >
    > Can anyone give me a lead on this and/or provide some example code?
    > Tnx!


    You can't overload operators that involve only built in types. At least one
    operand must be of class/struct/enum type. Pointers are considered built in
    types, even if they point to user-defined types. Thus you cannot overload
    any operator that works exclusively on pointers. For example, the following
    won't compile:

    #include <iostream>

    struct S
    {};

    void operator+(S*lptr, S*rptr)
    {
    std::cout << "New plus pointers operator\n";
    }

    --
    John Carson
     
    John Carson, Dec 4, 2005
    #2
    1. Advertising

  3. TuxC0d3

    TuxC0d3 Guest

    Hmm, ok that sounds reasonable..

    However, it would be nice if it could be made to work, for instance by
    some kind of class-with-operator-overloading technique.. (Like wrapping
    a pointer inside a class to make it a "smart" pointer, or such a
    thing..)
    That might make it work a decent bit slower, but when building in debug
    mode, it could provide me with a lot of helpful information...

    I'm thinking about something like
    template(class T) class ptr
    {
    T* m_ptr;
    public:
    //... add default constructor, copy ctor and operator=, etc here
    T& operator*()
    {
    if (!m_ptr)
    throw EAccessViolation();
    return *m_ptr;
    }
    };

    Wouldn't something like that be able to solve it as well, or am i
    probably thinking along the wrong lines? (for instance that this
    wouldn't give me an object which acts like a pointer, but gives me a
    ptr::* (or something like that))

    Don't be afraid to speak tech by the way.. I've already got my pretty
    little callback technique (based on a class with () operator
    overloading, a template constructor and pointer-to-memberfunction)
    working like a charm in only a couple of hours with almost no
    experience in operator overloading, templates, etc etc. :) (Though i
    do understand quite some things about working with pointers)

    --
    Tim.
     
    TuxC0d3, Dec 4, 2005
    #3
  4. TuxC0d3

    John Carson Guest

    "TuxC0d3" <> wrote in message
    news:
    > Hmm, ok that sounds reasonable..
    >
    > However, it would be nice if it could be made to work, for instance by
    > some kind of class-with-operator-overloading technique.. (Like
    > wrapping a pointer inside a class to make it a "smart" pointer, or
    > such a thing..)
    > That might make it work a decent bit slower, but when building in
    > debug mode, it could provide me with a lot of helpful information...
    >
    > I'm thinking about something like
    > template(class T) class ptr
    > {
    > T* m_ptr;
    > public:
    > //... add default constructor, copy ctor and operator=, etc here
    > T& operator*()
    > {
    > if (!m_ptr)
    > throw EAccessViolation();
    > return *m_ptr;
    > }
    > };
    >
    > Wouldn't something like that be able to solve it as well, or am i
    > probably thinking along the wrong lines? (for instance that this
    > wouldn't give me an object which acts like a pointer, but gives me a
    > ptr::* (or something like that))


    Sure, wrapping a pointer in a class will give you something pretty close to
    what you want. The following (quickly composed) class seems to work OK,
    though it is still missing some needed operators (I haven't defined a copy
    constructor or assignment operator since the compiler supplied ones seem
    adequate):

    #include <iostream>
    using namespace std;

    struct S
    {
    int x;
    };


    class EAccessViolation
    {};

    template<class T> class ptr
    {
    T* m_ptr;
    public:
    ptr() : m_ptr(0)
    {}
    ptr(T *rawptr) : m_ptr(rawptr)
    {}
    bool operator==(ptr &rhs)
    {
    return m_ptr == rhs.m_ptr;
    }
    T*operator->() const
    {
    if (!m_ptr)
    throw EAccessViolation();
    return m_ptr;
    }
    T& operator*() const
    {
    if (!m_ptr)
    throw EAccessViolation();
    return *m_ptr;
    }
    };


    int main()
    {
    S s1,s2;
    ptr<S> p1=&s1, p2, p3;

    // p1 is initialised but p2 is not so should be able to
    // dereference p1 OK but get an exception from p2

    p3 = p1;
    if(p1==p3)
    cout << "p1 and p3 are equal\n";
    else
    cout << "p1 and p3 are unequal\n";


    try
    {
    p1->x = 5;
    cout << "Dereference of p1 successful\n";
    }
    catch(EAccessViolation&)
    {
    cout << "Access violation for p1\n";
    }
    try
    {
    p2->x = 5;
    cout << "Dereference of p2 successful\n";
    }
    catch(EAccessViolation&)
    {
    cout << "Access violation for p2\n";
    }
    try
    {
    s2 = *p1;
    cout << "Dereference successful for p1\n";
    }
    catch(EAccessViolation&)
    {
    cout << "Access violation for p1\n";
    }
    try
    {
    s2 = *p2;
    cout << "Dereference successful for p2\n";
    }
    catch(EAccessViolation&)
    {
    cout << "Access violation for p2\n";
    }
    }

    Note that a wrapped pointer is never quite the same as a raw pointer.
    Consider:

    struct Test
    {
    Test(int *pint)
    {}
    };

    void foo(const Test &test)
    {}

    int main()
    {
    int *pint1;
    foo(pint1); // will compile

    ptr<int> pint2;
    foo(pint2); // won't compile since two user defined
    //conversions required
    }


    --
    John Carson
     
    John Carson, Dec 4, 2005
    #4
  5. TuxC0d3

    Greg Guest

    John Carson wrote:
    > "TuxC0d3" <> wrote in message
    > news:
    > > Hmm, ok that sounds reasonable..
    > >
    > > However, it would be nice if it could be made to work, for instance by
    > > some kind of class-with-operator-overloading technique.. (Like
    > > wrapping a pointer inside a class to make it a "smart" pointer, or
    > > such a thing..)
    > > That might make it work a decent bit slower, but when building in
    > > debug mode, it could provide me with a lot of helpful information...
    > >
    > > I'm thinking about something like
    > > template(class T) class ptr
    > > {
    > > T* m_ptr;
    > > public:
    > > //... add default constructor, copy ctor and operator=, etc here
    > > T& operator*()
    > > {
    > > if (!m_ptr)
    > > throw EAccessViolation();
    > > return *m_ptr;
    > > }
    > > };
    > >
    > > Wouldn't something like that be able to solve it as well, or am i
    > > probably thinking along the wrong lines? (for instance that this
    > > wouldn't give me an object which acts like a pointer, but gives me a
    > > ptr::* (or something like that))

    >
    > Sure, wrapping a pointer in a class will give you something pretty close to
    > what you want. The following (quickly composed) class seems to work OK,
    > though it is still missing some needed operators (I haven't defined a copy
    > constructor or assignment operator since the compiler supplied ones seem
    > adequate):
    >
    > #include <iostream>
    > using namespace std;
    >
    > struct S
    > {
    > int x;
    > };
    >
    >
    > class EAccessViolation
    > {};
    >
    > template<class T> class ptr
    > {
    > T* m_ptr;
    > public:
    > ptr() : m_ptr(0)
    > {}
    > ptr(T *rawptr) : m_ptr(rawptr)
    > {}
    > bool operator==(ptr &rhs)
    > {
    > return m_ptr == rhs.m_ptr;
    > }
    > T*operator->() const
    > {
    > if (!m_ptr)
    > throw EAccessViolation();
    > return m_ptr;
    > }
    > T& operator*() const
    > {
    > if (!m_ptr)
    > throw EAccessViolation();
    > return *m_ptr;
    > }


    Some implementations would elect to add these methods:

    operator T*()
    {
    return m_ptr;
    }

    operator const T*() const
    {
    return m_ptr;
    }
    > };
    >
    >
    > Note that a wrapped pointer is never quite the same as a raw pointer.
    > Consider:
    >
    > struct Test
    > {
    > Test(int *pint)
    > {}
    > };
    >
    > void foo(const Test &test)
    > {}
    >
    > int main()
    > {
    > int *pint1;
    > foo(pint1); // will compile
    >
    > ptr<int> pint2;
    > foo(pint2); // won't compile since two user defined
    > //conversions required


    With the conversion methods added, there is one user-defined conversion
    from ptr<int> to int* so the above function call will compile and work
    correctly.

    Although adding these conversion methods makes the smart pointer
    virtually indistinguishable from a raw pointer, it can cause problems
    if the smart pointer is maintaining a reference count. The implicit
    conversion allows the smart pointer to be assigned to a raw pointer
    without incrementing the reference count. For this (and other reasons)
    an implicit conversion to the corresponding raw pointer is usually not
    provided, or done so conditionally. And any client wanting to perform
    such a conversion must do so explicitly.

    Greg
     
    Greg, Dec 5, 2005
    #5
  6. TuxC0d3

    John Carson Guest

    "Greg" <> wrote in message
    news:
    > John Carson wrote:
    >>
    >> Note that a wrapped pointer is never quite the same as a raw pointer.
    >> Consider:
    >>
    >> struct Test
    >> {
    >> Test(int *pint)
    >> {}
    >> };
    >>
    >> void foo(const Test &test)
    >> {}
    >>
    >> int main()
    >> {
    >> int *pint1;
    >> foo(pint1); // will compile
    >>
    >> ptr<int> pint2;
    >> foo(pint2); // won't compile since two user defined
    >> //conversions required
    >> }

    >
    > Some implementations would elect to add these methods:
    >
    > operator T*()
    > {
    > return m_ptr;
    > }
    >
    > operator const T*() const
    > {
    > return m_ptr;
    > }
    >
    > With the conversion methods added, there is one user-defined
    > conversion from ptr<int> to int* so the above function call will
    > compile and work correctly.


    Actually, no. In fact my comments implicitly assumed exactly that operator
    (I should have made this explicit). foo does not take a int * argument; it
    takes a reference to Test. Thus there are two conversions required:

    1. int<ptr> to int *
    2. int * to Test

    Accordingly, the code will not compile.

    Of course, one might not want the conversion from int<ptr> to Test, but that
    is another issue. The issue at hand is the equivalence of int * and ptr<int>
    and the foo function illustrates a non-equivalence.

    --
    John Carson
     
    John Carson, Dec 5, 2005
    #6
  7. TuxC0d3

    John Carson Guest

    "John Carson" <> wrote in message
    news:439414f7$0$13317$
    >Thus there are two
    > conversions required:
    > 1. int<ptr> to int *
    > 2. int * to Test


    That should be ptr<int>, not int<ptr>

    --
    John Carson
     
    John Carson, Dec 6, 2005
    #7
    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. Joe Seigh
    Replies:
    18
    Views:
    840
    tom_usenet
    Sep 22, 2003
  2. Tim Milstead
    Replies:
    0
    Views:
    5,138
    Tim Milstead
    Apr 20, 2004
  3. Robert Wierschke
    Replies:
    3
    Views:
    11,090
    Louise56
    Aug 3, 2004
  4. John Smith
    Replies:
    2
    Views:
    433
    Ivan Vecerina
    Oct 6, 2004
  5. Zachary  Turner

    overloading the global operator new

    Zachary Turner, Dec 10, 2004, in forum: C++
    Replies:
    1
    Views:
    774
    Raymond Martineau
    Dec 11, 2004
Loading...

Share This Page