0

It took me a while to figure this out, but the semantics of boost::any are confusing.

With value types, you use it like so:

int value = 100;
boost::any something;
something = value;
//...later...
int value = boost::any_cast<int>(&something);

This code is clear and makes sense, but stores value internally as a copy. This means for larger objects I place inside boost::any, they will be copied. Also any functions that I replace void* with this will expect that the value outside the function is modified when I modify the value contained in the boost::any object (which won't happen, since it copied it).

So if I put pointers into it, things get weird:

int value = 100;
boost::any something;
something = &value;
//...later...
int* value = *boost::any_cast<int*>(&something);

I have to dereference the return value in this case because boost::any_cast returns int**! I also haven't checked but I think this may crash if something.empty() == true. This is just not straightforward at all.

I do not want to store values in my boost::any, I want it to only function on pointers and behave semantically closer to void*. Pointers in, pointers out, with some type-safety mixed in. Essentially what I want is boost::any_pointer, or something like that. Is there a way to prohibit boost::any from accepting anything except pointers? And if not, is there an alternative to boost::any that can give me the semantics I'm looking for?

AnatolyS
  • 4,249
  • 18
  • 28
void.pointer
  • 24,859
  • 31
  • 132
  • 243

3 Answers3

2

You are using any_cast wrong:

There are essentially two (three) flavors.

  • Taking a reference to any and returning a value or reference to the content
  • Taking a pointer to any and returning a pointer to the content

Examples:

#include <boost/any.hpp>
int main()
{
    // Any holding a value
    {
        boost::any any_value(1);
        // Throws bad_any_cast if the content is not 'int' (in this case):
        int  value = boost::any_cast<int>(any_value);
        // Throws bad_any_cast if the content is not 'int' (in this case):
        int& reference = boost::any_cast<int&>(any_value);
        // Returns a null pointer if the content is not 'int' (in this case):
        int* pointer = boost::any_cast<int>(&any_value);
    }

    // Any holding a pointer (which is nothing else but a value)
    {
        int integer = 0;
        boost::any any_ptr(&integer);
        // Throws bad_any_cast if the content is not 'int*' (in this case):
        int * pointer = boost::any_cast<int*>(any_ptr);
        // Throws bad_any_cast if the content is not 'int*' (in this case):
        int*& pointer_reference = boost::any_cast<int*&>(any_ptr);
        // Returns a null pointer if the content is not 'int*' (in this case):
        int** pointer_pointer = boost::any_cast<int*>(&any_ptr);
    }
}

See also: http://en.cppreference.com/w/cpp/experimental/any and http://en.cppreference.com/w/cpp/experimental/any/any_cast

  • I see that syntax throw a lot of people off. It's the same though for std::any. – Trevor Hickey Jun 13 '16 at 20:27
  • I'm trying to avoid exceptions while using `boost::any`, which is why I'm using the pointer overload. Your first and second examples seem like they would throw exceptions; at least in my tests it did. – void.pointer Jun 13 '16 at 23:58
  • Here is a sample I wrote that shows it throwing; i don't want it to throw, i want a `nullptr` returned if the types are wrong: http://coliru.stacked-crooked.com/a/f6ffdf4387e335d6 – void.pointer Jun 14 '16 at 00:04
  • @void.pointer you could catch the exception and return nullptr from there. – Arunmu Jun 14 '16 at 05:18
1

NOTE: I am assuming you want to store pointers in boost::any, since you are looking for something on the lines of boost::any_pointer (though non-existent).

boost::any_cast returns the pointer to the actual value stored inside it (the held object inside holder). So it kind of saves a copy unless you actually want to copy it later.

template<typename ValueType>
    ValueType * any_cast(any * operand) BOOST_NOEXCEPT
    {
        return operand && operand->type() == boost::typeindex::type_id<ValueType>()
            ? &static_cast<any::holder<BOOST_DEDUCED_TYPENAME remove_cv<ValueType>::type> *>(operand->content)->held
            : 0;
    }

You can always create a wrapper around boost::any to allow only pointer types:

class MyAny {
public:
  template <typename T, 
        typename = typename std::enable_if<std::is_pointer<std::remove_cv<T>::type>::value>::type>
  MyAny(T ptr): any_(ptr) {}

 template <typename ValueType>
 ValueType operator*() {
     return *boost::any_cast<ValueType>(&any_);
 }

private:
  boost::any any_
};

Above is a rough code, I have not compiled and tested it.

std::enable_if type trait is available in C++11, but can be found in boost as well. It is what that will constrain your MyAny to only pointer types.

Arunmu
  • 6,837
  • 1
  • 24
  • 46
0

Although this is quite an old question, I nevertheless would like to point out a simple misconception [I think] in the preface of the asked questions:

int value = 100;
boost::any something;
something = &value;                                    // 1)
//...later...
int* value = *boost::any_cast<int*>(&something);       // 2)

1) This saved "int *" in something.

2) This contains essentially several steps:

  • "&something" gets the address of something, i.e. return "boost::any *"
  • "boost::any_cast()" now casts this address to a pointer to an int - which is probably not what void.pointer wanted. And I don't know whether - and if, why - this seems to work.
  • And then you dereference "*boost::..." this, i.e. cancelling the "&" from the first step again.

What I think, void.pointer wanted to do here, is more along the following line:

int* value = boost::any_cast<int*>(something);

.

PS: I would have liked to put this in a simple comment and not a full-fledged answer - stackoverflow however requires me to collect enough points first, so...

J. Wilde
  • 31
  • 3