【问题标题】:visitor pattern for boost::anyboost::any 的访问者模式
【发布时间】:2012-11-15 13:57:38
【问题描述】:

我找到了这个https://gist.github.com/2945472,但我需要一个不依赖于 c++11 的实现。我尝试将其转换为仅使用 boost,但遇到了一些问题。

这是我想出的:

#include <boost/any.hpp>
#include <boost/function.hpp>
#include <boost/bind.hpp>
#include <boost/lambda/lambda.hpp>
#include <boost/unordered_map.hpp>

struct type_info_hash {
    std::size_t operator()(std::type_info const & t) const {
        return t.hash_code();
    }
};

struct equal_ref {
    template <typename T> bool operator()(boost::reference_wrapper<T> a,boost::reference_wrapper<T> b) const {
        return a.get() == b.get();
    }
};
struct any_visitor {
    boost::unordered_map<boost::reference_wrapper<std::type_info const>, boost::function<void(boost::any&)>, type_info_hash, equal_ref> fs;

    template <typename T> void insert_visitor(boost::function<void(T)> f) {
        try {
            fs.insert(std::make_pair(boost::ref(typeid(T)), boost::bind(f, boost::any_cast<T>(boost::lambda::_1))));
        } catch (boost::bad_any_cast& e) {
            std::cout << e.what() << std::endl;
        }
    }

    bool operator()(boost::any & x) {
        boost::unordered_map<boost::reference_wrapper<std::type_info const>, boost::function<void(boost::any&)>, type_info_hash, equal_ref>::iterator it = fs.find(boost::ref(x.type()));
        if (it != fs.end()) {
            it->second(x);
            return true;
        } else {
            return false;
        }
    }
};

struct abc {};

void fa(int i) { std::cout << "fa(" << i << ")" << std::endl; }
void fb(abc) { std::cout << "fb(abc())" << std::endl; }

int main() {
    any_visitor f;
    f.insert_visitor<int>(fa);
    f.insert_visitor<abc>(fb);

    std::vector<boost::any> xs;
    xs.push_back(1);
    xs.push_back(abc());
    xs.push_back(1.5);

    for (auto & x : xs) {
        if (!f(x)) std::cout << "no visitor registered" << std::endl;
    }
}

我在插入地图时收到 bad_any_cast。不应该 any_cast 仅由 it->second(x) 调用吗?我做错了什么?

【问题讨论】:

  • 您是否考虑过使用开箱即用支持访问者的boost::variantany 的使用假定类型可以是anything,即类型系统中的all 类型。 variant 假定存在您可能希望在对象中使用的类型的子集。访问者更接近variant,因为必须定义不同的功能。
  • 我的意图是用它从 boost::program_options 中写出配置文件,它使用 boost::any。

标签: c++ boost visitor-pattern boost-any


【解决方案1】:

您不能将_1 转换为T(在绑定表达式时)。

你需要一个懒惰的演员。也许定义一个函数并使用嵌套的绑定表达式,或者使用带有自定义 any_cast 演员的 Boost Phoenix。

这是嵌套绑定方法:

#include <boost/any.hpp>
#include <boost/function.hpp>
#include <boost/bind.hpp>
#include <boost/lambda/lambda.hpp>
#include <boost/unordered_map.hpp>

struct type_info_hash {
    std::size_t operator()(std::type_info const & t) const {
        return 42; // t.hash_code();
    }
};

struct equal_ref {
    template <typename T> bool operator()(boost::reference_wrapper<T> a,boost::reference_wrapper<T> b) const {
        return a.get() == b.get();
    }
};
struct any_visitor {
    boost::unordered_map<boost::reference_wrapper<std::type_info const>, boost::function<void(boost::any&)>, type_info_hash, equal_ref> fs;

    template <typename T> static T any_cast_f(boost::any& any) { return boost::any_cast<T>(any); }

    template <typename T> void insert_visitor(boost::function<void(T)> f) {
        try {
            fs.insert(std::make_pair(boost::ref(typeid(T)), boost::bind(f, boost::bind(any_cast_f<T>, boost::lambda::_1))));
        } catch (boost::bad_any_cast& e) {
            std::cout << e.what() << std::endl;
        }
    }

    bool operator()(boost::any & x) {
        boost::unordered_map<boost::reference_wrapper<std::type_info const>, boost::function<void(boost::any&)>, type_info_hash, equal_ref>::iterator it = fs.find(boost::ref(x.type()));
        if (it != fs.end()) {
            it->second(x);
            return true;
        } else {
            return false;
        }
    }
};

struct abc {};

void fa(int i) { std::cout << "fa(" << i << ")" << std::endl; }
void fb(abc) { std::cout << "fb(abc())" << std::endl; }

int main() {
    any_visitor f;
    f.insert_visitor<int>(fa);
    f.insert_visitor<abc>(fb);

    std::vector<boost::any> xs;
    xs.push_back(1);
    xs.push_back(abc());
    xs.push_back(1.5);

    for (auto it=xs.begin(); it!=xs.end(); ++it)
        if (!f(*it)) std::cout << "no visitor registered" << std::endl;
}

打印输出:

fa(1)
fb(abc())
no visitor registered

【讨论】:

  • 注意我替换了 type_info_hash 仿函数体,让它在我的系统上编译。不过,嵌套的绑定表达式很摇滚 :)
  • 看起来我在处理这个问题时忘记删除 -std=c++11 标志。我需要编写自己的 type_info hash_code 方法。我也错过了删除 main 循环中的“自动”。
  • 我也注意到了;不像 lambda -> 嵌套绑定那样难以修复 :) (在黑暗中拍摄:也许您想知道 Boost Extension 库如何尝试进行可移植类型信息比较,我记得为此阅读过:stackoverflow.com/a/5838527/85371
【解决方案2】:

尝试使用可扩展的 any https://sourceforge.net/projects/extendableany/?source=directory.

struct f_method
{
    typedef void (boost::mpl::_1::* signature) () const;

    template <typename T>
    struct wrapper
        : public T
    {
        void f() const
        {
            return this->call(f_method());
        }
    };

    struct implementation
    {
        void operator() (int i) const
        {
           std::cout << "fa(" << i << ")" << std::endl;
        }

        void operator() (abc) const
        {
           std::cout << "fb(abc())" << std::endl;
        }

        template <typename T>
        void operator() (const T& t) const
        {
            std::cout << "Errr" << std::endl;
        }
    };
};

typedef xany<boost::mpl::list<f_method> > any;

int main() {
    std::vector<any> xs;
    xs.push_back(1);
    xs.push_back(abc());
    xs.push_back(1.5);

    for (auto it=xs.begin(); it!=xs.end(); ++it)
        (*it).f();
}

【讨论】:

    猜你喜欢
    • 2011-04-06
    • 1970-01-01
    • 2022-12-04
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2020-07-18
    • 1970-01-01
    相关资源
    最近更新 更多