Question

I have a vector of many boost::any In this vector I need to perform some operations on std::vector and on the elements of type IContainer

class   IContainer
{
public:
  virtual ~IContainer(){}
  virtual const boost::any operator[](std::string) const = 0;
};

class   AContainer : public IContainer
{
  std::vector<int>      vect_;
  std::string name_;
public:
  AContainer() : vect_({0, 1, 2, 3, 4, 5}), name_("AContainer") {}
  virtual const boost::any operator[](std::string key) const
  {
    if (key == "str")
      return (name_);
    if (key == "vect")
      return (vect_);
    return nullptr;
  }
};

So I have done the following function (imo quite ugly) but who works correctly

m is const std::vector<boost::any>&

for (const auto & elem : m)
    {
      try
        {
          std::vector<int> v = boost::any_cast<std::vector<int>>(elem);
          display(v);
        }
      catch(boost::bad_any_cast){}
      try
        {
          std::vector<IContainer*> v = boost::any_cast<std::vector<IContainer*>>(elem);
          display(v);
        }
      catch(boost::bad_any_cast){}
      try
        {
          AContainer v(boost::any_cast<AContainer>(elem));
          try
            {
              display(boost::any_cast<const std::vector<int>>(v["vect"]));
            }
          catch (boost::bad_any_cast){}
          try
            {
              std::cout << boost::any_cast<const std::string>(v["str"]) << std::endl;
            }
          catch (boost::bad_any_cast){}
          try
            {
              display(boost::any_cast<std::vector<int> >(v));
            }
          catch (boost::bad_any_cast) {}
        }
      catch(boost::bad_any_cast){}
    }

I have tried to add many "try{}try{}catch{}" but it's not working

Do you have any solutions better than what I have done

Edit

I have tried the solutions of James Kanze, user1131467 and Praetorian

So the 3 are working nicely, but when I have calculate the time of execution, the answer of user1131467 is a bit faster than the other. I must now find a solution to store each types in a map to avoid all this if/else

I will also take a look at boost::variant

Was it helpful?

Solution

Using the pointer-form of any_cast is much cleaner, as it uses the nullability of pointers:

for (const auto & elem : m)
    if (T1* p = any_cast<T1>(&elem))
    {
         do stuff with *p;
    }
    else if (T2* p = any_cast<T2>(&elem))
    {
         do stuff with *p;
    }
    else if (...)
    {
         ...
    }

This also has the advantage of doing the cast once per case.

OTHER TIPS

You can create a function along the lines of:

template <typename T>
bool 
isInstanceOf( boost::any const& object )
{
    return boost::any_cast<T>( &object ) != nullptr;
}

and use it, with if's to check:

if ( isInstanceOf<std::vector<int>>( elem ) ) {
    display( boost::any_cast<std::vector<int>>( elem ) );
} else if ( isInstanceOf<std::vector<IContainer*>>( elem) ) {
    display( boost::any_cast<std::vector<IContainer*>>( elem) );
} 
//  ...

You could write your own wrapper around any_cast that swallows exceptions.

template<typename T>
bool nothrow_any_cast( boost::any& source, T& out )
{
  try {
    out = boost::any_cast<T>( source );
  } catch ( boost::bad_any_cast const& ) {
    return false;
  }
  return true;
}

And then use it as

std::vector<int> vect;
std::string str;

if( nothrow_any_cast(v["vect"], vect ) ) {
  // succeeded
} else if( nothrow_any_cast(v["str"], str ) ) {
  // succeeded
} ...

However, if you do this, you're default constructing all the types, and then assigning them; so even if it looks a little cleaner, it's debatable whether it is any better than what you already have.

You can put all of the statements in one try block:

try {
    // do all your stuff
}
catch (boost::bad_any_cast) {}

You missed to add any instruction when entering your catch(...){} block. So when an exception is thrown you do not handle it at all.

An appropriate handling usually includes to trace the level of error and try to resolve the state of error (if possible).

Since in every branch you do catch the same exception you can aggregate all of them to one block:

try{ ... }
catch(boost::bad_any_cast) { ...} 

Why not use the pointer alternative of boost::any_cast, it does not throw anything, it returns nullptr if the requested type does not match with the store type. And if you get a pointer back, you can just write an overload for display, that takes any pointer, checks it for being null and calls the actual display function if the pointer is not null.

template<typename T>
void display(T* p) { if ( p ) display(*p); }

With the template above, only display is called for the correct cast.

for ( const auto& elem : m ) {
  display(boost::any_cast<int>(&elem));
  display(boost::any_cast<my_type>(&elem));
  ....
}

None of the answeres go the simple way that is now in the standard.

If you use "std::any" then you can just use the "type()" function to get the typeid of the containing item.

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top