Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Overloaded operator not throwing exceptions

I have a class that contains a map

Any& Map::operator[]( const unsigned int field ) const
{
  try
  {
    iterator it;
    if ((it = m_fields.find(field)) != m_fields.end())
      return it->second;

    throw std::runtime_error("Field " + boost::lexical_cast<std::string>(field) + " not found.");
  }
  catch(boost::bad_any_cast& )
  {
    throw std::runtime_error("Failed conversion field " + boost::lexical_cast<std::string>(field)  + " using boost::any_cast ");
  }
}

I want it to throw an exception when the field does not exists in the map so the program does not crash on a bad get, but the throw does not seem to work on the overloaded operator, the program crashes anyway so either the throw or the catch are being ignored. If I use the same code but with a common function

Any& Map::get( const unsigned int field ) const
{
  //...

it works.

Did I ran in some c++ limitation or am I doing something wrong?

--EDIT :

I ran the debugger and to my surprise that code doesn't even execute, another method does

Any& Map::operator[]( const unsigned int  field )
{
  iterator it;
  if ((it = m_fields.find(field)) == m_fields.end())
  {
    Any newValue;
    m_fields[field] = newValue;
    return m_fields[field];
  }

  return it->second;
}

and the crash occurs because of a boost assert on Any that tries to convert a uninitialized variable. This method is probably used on inserts on the map, like

Map a;
a[3] = "foo";

So I guess I have no way to differentiate when the operator is used on an atribution or on a get and the use of this operator is highly unsafe for gets

like image 819
ibrabeicker Avatar asked Nov 05 '22 04:11

ibrabeicker


1 Answers

Re your edit: overload resolution generally only takes into account the arguments, not the use. If you have a const and a non-const function with otherwise identical signatures, the non-const will be chosen if possible (e.g. called on a non-const object).

In a case where you do want different behavior depending on the use, the traditional solution is to provide a proxy. Your Map class would contain two functions get and set, and the non-const operator[] would return a proxy which looks something like:

class Proxy  // member of Map
{
    Map* myOwner;
    int myIndex;
public:
    Proxy( Map* owner, int index )
        : myOwner( owner )
        , myIndex( index )
    {
    }
    void operator=( Any const& rhs ) const
    {
        myOwner->put( myIndex, rhs );
    }
    operator Any() const
    {
        return myOwner->get( myIndex );
    }
};
like image 164
James Kanze Avatar answered Nov 09 '22 14:11

James Kanze