boost::any replacement for the code below

你说的曾经没有我的故事 提交于 2019-12-18 09:24:33

问题


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 is used. I know a template class would do it, but finding it hard to write this template. - C++ Rookie.

 struct Properties {
 public:
 Properties() {}
 Properties(const std::string &s, const boost::any & p) {
      name = s;
      value = p;
 }

 template <typename T>
 Properties(T n) {
      value = n;
 }
 boost::any value;

 std::string name;
};

回答1:


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




回答2:


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.



来源:https://stackoverflow.com/questions/21176190/boostany-replacement-for-the-code-below

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!