I wish get rid of boost dependency on my code. I have the following struct construct. When calling and using this struct at another place in the code boost::any_cast
Just for fun, I thought I'd create a minimalist any implementation:
//////////////////////////////////////////
// my_any.hpp
#include <memory>
#include <stdexcept>
struct my_any
{
my_any() = default;
template <typename T> my_any(T const& v) : _storage(new storage<T>(v)) { }
my_any(my_any const& other) : _storage(other._storage? std::move(other._storage->clone()) : nullptr) {}
void swap(my_any& other) { _storage.swap(other._storage); }
friend void swap(my_any& a, my_any& b) { a.swap(b); };
my_any& operator=(my_any other) { swap(other); return *this; }
// todo move semantics
private:
struct storage_base {
virtual std::unique_ptr<storage_base> clone() = 0;
virtual ~storage_base() = default;
};
template <typename T>
struct storage : storage_base {
T value;
explicit storage(T const& v) : value(v) {}
std::unique_ptr<storage_base> clone() { return std::unique_ptr<storage_base>(new storage<T>(value)); }
};
std::unique_ptr<storage_base> _storage;
template<typename T> friend T & any_cast(my_any &);
template<typename T> friend T const& any_cast(my_any const&);
};
template <typename T> T& any_cast(my_any& a) {
if (auto p = dynamic_cast<my_any::storage<T>*>(a._storage.get()))
return p->value;
else
throw std::bad_cast();
}
template <typename T> T const& any_cast(my_any const& a) {
if (auto p = dynamic_cast<my_any::storage<T> const*>(a._storage.get()))
return p->value;
else
throw std::bad_cast();
}
You can then use it precisely the same fashion as your use-cases showed:
struct Properties {
public:
Properties(const std::string &s="", const my_any& p={})
: name(s), value(p) {}
template <typename T> Properties(T n) { value = n; }
std::string name;
my_any value;
};
#include <vector>
#include <iostream>
typedef std::vector<Properties> Props;
int main()
{
Props v;
v.emplace_back("bye", 42);
v.emplace_back("vector", v);
std::cout << "v.size(): " << v.size() << "\n";
std::cout << "v[0].value: " << any_cast<int>(v[0].value) << "\n";
std::cout << "v[1].value.size(): " << any_cast<Props>(v[1].value).size() << "\n";
v[0].value = v;
try {
std::cout << "v[0].value: " << any_cast<int>(v[0].value) << "\n";
} catch(std::exception const& e)
{
std::cout << e.what() << " exception caught, ok!\n";
}
std::cout << "v[0].value.size(): " << any_cast<Props>(v[0].value).size() << "\n";
}
See the output Live On Coliru
boost::any
uses type erasure to store objects of any type, and you can assign it values of different types at runtime. The any_cast
is used to retrieve the original value, with the correct type, that was stored in the any
object. For instance, your current class allows you to do this
Properties p("int", 42);
std::cout << boost::any_cast<int>(p.value) << '\n';
p = Properties("string", std::string("hello"));
std::cout << boost::any_cast<std::string>(p.value) << '\n';
You cannot just convert the class above into a template and get identical functionality. If you do that, you'll only be able to store a single type of value. And you must change the entire struct
into a template, not just the constructor.
template<typename T>
struct Properties {
public:
Properties() {}
Properties(std::string s, T p)
: name(std::move(s)) // should use initialization list instead
, value(std::move(p)) // of assignment within the body
{}
Properties(T n)
: value(std::move(n))
{}
std::string name;
T value;
};
However, the code I posted above is now illegal.
Properties<int> p("int", 42);
std::cout << p.value << '\n';
// p = Properties<std::string>("string", std::string("hello"));
// will not compile because Properties<int> and Properties<std::string> are
// distinct types
If these restrictions are OK, then the modified definition should work for you.