使用 boost::bind 的访问者模式重载函数

发布于 2024-11-07 10:00:13 字数 1235 浏览 0 评论 0原文

我正在尝试将访问者模式添加到我的代码中,并希望尽可能保持通用。更具体地说,我希望不必将回调函数硬编码到我的 accept 函数中。因此,作为 accept 函数的参数,我给出一个 boost::function 对象,然后由访问的对象调用该对象。

然而,我的问题是,我无法绑定到重载函数(因为 boost::bind 不知道要绑定到哪个确切函数),并且我无法将重载函数转换为正确的函数,因为我不知道不知道所访问类的确切类型(这很重要)。

有什么办法可以创造出我想要的东西吗?我进行了搜索,但只发现了有关如何解决绑定问题的问题(这是通过强制转换,这是我无法做到的)。

下面是一些无法编译的代码,但显示了我想要归档的内容:

#include <string>
#include <vector>
#include <boost/bind.hpp>
#include <boost/function.hpp>

struct A
{
    virtual void acceptVisitor (boost::function<void (A const &)> callbackFnc)
    {
        callbackFnc(*this);
    }
};

struct B : virtual public A {};

std::string printMe (A const & a) { return "A"; }
std::string printMe(B const & a) { return "B"; }


int main()
{
    std::vector<std::string> stringVector;

    boost::function<void (A const &)> bindedFnc = boost::bind(&std::vector<std::string>::push_back, 
        &stringVector, boost::bind(&printMe, _1));

    A A1;
    B A2;

    A1.acceptVisitor(bindedFnc);
    A2.acceptVisitor(bindedFnc);
}

[编辑]修复了示例代码,因为以前的版本(如ildjarn所说)没有'实际上并没有调用 accept 函数。

I'm trying to add a Visitor pattern to my code and want to keep this as general as possible. More specificly, I'd like not having to hardcode the callback function into my accept function. So, as a parameter to the accept function, I give a boost::function object, which is then called by the visited object.

My problem is, however, that I can't bind to overloaded functions (because boost::bind doesn't know which exact function to bind to) and I can't cast the overloaded function to the correct one, because I don't know the exact type of the visited class (this is important).

Is there any way to create what I want? I searched SO, but found only questions about how to fix the bind issue (which is by casting, which is something I can't do).

Below is some code that does not compile, but shows what I'd like to archieve:

#include <string>
#include <vector>
#include <boost/bind.hpp>
#include <boost/function.hpp>

struct A
{
    virtual void acceptVisitor (boost::function<void (A const &)> callbackFnc)
    {
        callbackFnc(*this);
    }
};

struct B : virtual public A {};

std::string printMe (A const & a) { return "A"; }
std::string printMe(B const & a) { return "B"; }


int main()
{
    std::vector<std::string> stringVector;

    boost::function<void (A const &)> bindedFnc = boost::bind(&std::vector<std::string>::push_back, 
        &stringVector, boost::bind(&printMe, _1));

    A A1;
    B A2;

    A1.acceptVisitor(bindedFnc);
    A2.acceptVisitor(bindedFnc);
}

[Edit] Fixed example code, because previous version (as ildjarn remarked) didn't actually call the accept function.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(2

终难愈 2024-11-14 10:00:13

这应该能让你成功一半。它使用 Boost 1.46.0 使用 Visual C++ 2010 和 g++ 4.5.1 进行编译。它不能使用 Visual C++ 2010 C++0x 实现进行编译;我还不确定为什么。

设置:

#include <iostream>
#include <iterator>
#include <string>
#include <vector>

#include <boost/bind.hpp>
#include <boost/function.hpp>

// This helper allows you to do a push_back in a bind; you can't bind
// directly to std::vector::push_back because the type of a Standard
// Library member function is unspecified.
struct do_push_back
{
    typedef void result_type;

    template <typename TSequence, typename TElement>
    void operator()(TSequence* sequence, const TElement& element) const
    {
        sequence->push_back(element);
    }
};

演示:

// Class hierarchy for demonstration:
struct B { };
struct D : B { };

// Instead of using overlodaed nonmember functions, you can overload
// operator() in a function object.  This allows you to bind to an 
// instance of this function object, not directly to one of the overloads.
struct make_string
{
    typedef std::string result_type;
    std::string operator()(const B&) const { return "B"; }
    std::string operator()(const D&) const { return "D"; }
};

int main()
{
    std::vector<std::string> strings;

    // Note that we do not use a boost::function here:
    auto f = boost::bind(do_push_back(), 
                         &strings, 
                         boost::bind(make_string(), _1));

    // Call our 'f' with B and D objects:
    f(B());
    f(D());

    std::copy(strings.begin(), strings.end(),
              std::ostream_iterator<std::string>(std::cout));
}

结果:

BD

这就是为什么这只是解决方案的一半:您不能将调用 boost::bind 的结果存储在 boost::function 中。问题是,当您使用 boost::function 来存储绑定函数对象时,它将始终传递 const A&作为绑定函数的参数。

即使您使用 D 参数调用 boost::function 对象,它也会转换为 const B&。当您使用 boost::function 时,很多类型信息都会丢失;这种类型信息的丢失是使 boost::function 可以用作通用可调用对象容器所必需的。

但这并不意味着您不能传递绑定的函数对象;您只需使用模板来防止类型信息丢失:

template <typename TFunction>
void test(std::vector<std::string>& strings, TFunction f)
{
    f(B());
    f(D());
}

// In main():
test(strings, f);

// Or, if you don't have C++0x's "auto", you can pass the bound 
// function object directly:
test(strings, boost::bind(do_push_back(), 
                          &strings, 
                          boost::bind(make_string(), _1)));

不幸的是,为了不丢失类型信息,您必须将绑定的函数对象传递给函数模板。这意味着您将 acceptVisitor 设为虚拟成员函数的想法不适用于此解决方案(不可能有虚拟函数模板)。

无论如何,希望这对您有帮助。

This should get you halfway there. It compiles with Visual C++ 2010 and g++ 4.5.1 using Boost 1.46.0. It does not compile with the Visual C++ 2010 C++0x <functional> implementation; I'm not yet sure why.

The setup:

#include <iostream>
#include <iterator>
#include <string>
#include <vector>

#include <boost/bind.hpp>
#include <boost/function.hpp>

// This helper allows you to do a push_back in a bind; you can't bind
// directly to std::vector::push_back because the type of a Standard
// Library member function is unspecified.
struct do_push_back
{
    typedef void result_type;

    template <typename TSequence, typename TElement>
    void operator()(TSequence* sequence, const TElement& element) const
    {
        sequence->push_back(element);
    }
};

The demonstration:

// Class hierarchy for demonstration:
struct B { };
struct D : B { };

// Instead of using overlodaed nonmember functions, you can overload
// operator() in a function object.  This allows you to bind to an 
// instance of this function object, not directly to one of the overloads.
struct make_string
{
    typedef std::string result_type;
    std::string operator()(const B&) const { return "B"; }
    std::string operator()(const D&) const { return "D"; }
};

int main()
{
    std::vector<std::string> strings;

    // Note that we do not use a boost::function here:
    auto f = boost::bind(do_push_back(), 
                         &strings, 
                         boost::bind(make_string(), _1));

    // Call our 'f' with B and D objects:
    f(B());
    f(D());

    std::copy(strings.begin(), strings.end(),
              std::ostream_iterator<std::string>(std::cout));
}

The result:

BD

Here's why this is only half of the solution: you can't store the result of the call to boost::bind in a boost::function. The problem is that when you use a boost::function<void(const B&)> to store the bound function object, it will always pass a const A& as an argument to the bound function.

Even if you call the boost::function object with a D argument, it gets converted to a const B&. A lot of type information is lost when you use a boost::function; this loss of type information is necessary to make boost::function usable as a generic callable object container.

That doesn't mean that you can't pass the bound function object around, though; you just have to use templates to prevent type information from getting lost:

template <typename TFunction>
void test(std::vector<std::string>& strings, TFunction f)
{
    f(B());
    f(D());
}

// In main():
test(strings, f);

// Or, if you don't have C++0x's "auto", you can pass the bound 
// function object directly:
test(strings, boost::bind(do_push_back(), 
                          &strings, 
                          boost::bind(make_string(), _1)));

It is unfortunate that in order not to lose type information you have to pass the bound function object to a function template. It means that your idea of making acceptVisitor a virtual member function won't work with this solution (it isn't possible to have a virtual function template).

In any case, hopefully this is helpful to you.

那片花海 2024-11-14 10:00:13

也许您会发现 Loki 库中的通用访客模板很有用,

请参阅 http://loki-lib.sourceforge.net

#include <iostream>
#include <Loki/Visitor.h>

struct Animal : public Loki::BaseVisitable<void, Loki::DefaultCatchAll, false>
{
};

struct Cat : public Animal
{
    LOKI_DEFINE_VISITABLE();
};

struct Dog : public Animal
{
    LOKI_DEFINE_VISITABLE();
};

struct Flower : public Loki::BaseVisitable<void, Loki::DefaultCatchAll, false>
{
};

struct Tulip : public Flower
{
    LOKI_DEFINE_VISITABLE();
};

struct AnimalAndFlowerVisitor 
    : public Loki::BaseVisitor
    , public Loki::Visitor<Cat, void, false>
    , public Loki::Visitor<Dog, void, false>
    , public Loki::Visitor<Tulip, void, false>
{
    void Visit(Dog & dog)
    {
        std::cout << "Do something with the dog\n";
    }

    void Visit(Cat & cat)
    {
        std::cout << "Do something with the cat\n";
    }

    void Visit(Tulip & tulip)
    {
        std::cout << "Do something with the tulip\n";
    }
};

int main(int argc, char* argv[])
{
    Dog dog;
    Cat cat;
    Tulip tulip;

    Animal & animalDog = dog;
    Flower & tulipFlower = tulip;

    AnimalAndFlowerVisitor visitor;
    animalDog.Accept(visitor);    // will print "Do something with the dog"
    tulipFlower.Accept(visitor);  // will print "Do something with the tulip"

    return 0;
}

Perhaps you will find the generic Visitor template from the Loki library useful

See http://loki-lib.sourceforge.net

#include <iostream>
#include <Loki/Visitor.h>

struct Animal : public Loki::BaseVisitable<void, Loki::DefaultCatchAll, false>
{
};

struct Cat : public Animal
{
    LOKI_DEFINE_VISITABLE();
};

struct Dog : public Animal
{
    LOKI_DEFINE_VISITABLE();
};

struct Flower : public Loki::BaseVisitable<void, Loki::DefaultCatchAll, false>
{
};

struct Tulip : public Flower
{
    LOKI_DEFINE_VISITABLE();
};

struct AnimalAndFlowerVisitor 
    : public Loki::BaseVisitor
    , public Loki::Visitor<Cat, void, false>
    , public Loki::Visitor<Dog, void, false>
    , public Loki::Visitor<Tulip, void, false>
{
    void Visit(Dog & dog)
    {
        std::cout << "Do something with the dog\n";
    }

    void Visit(Cat & cat)
    {
        std::cout << "Do something with the cat\n";
    }

    void Visit(Tulip & tulip)
    {
        std::cout << "Do something with the tulip\n";
    }
};

int main(int argc, char* argv[])
{
    Dog dog;
    Cat cat;
    Tulip tulip;

    Animal & animalDog = dog;
    Flower & tulipFlower = tulip;

    AnimalAndFlowerVisitor visitor;
    animalDog.Accept(visitor);    // will print "Do something with the dog"
    tulipFlower.Accept(visitor);  // will print "Do something with the tulip"

    return 0;
}
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文