c++ boost::any 定义我自己的 print ,

发布于 2024-10-17 08:12:18 字数 454 浏览 6 评论 0原文

我正在努力寻找如何使用 boost::any 创建一个可以首先使用模板打印任何类型的打印函数。

template <typename T>
struct printer {
    void print(ostream& os, const boost::any& a);
}; 

我需要首先定义 print()。 我希望对any有真正的运算符<<,这个想法很简单:将类的实例附加到每个any对象 printer 具有合适的 T,并在 any 的值类型更改时更改此对象。 第一个技术问题是打印机对象依赖于 T,而 any 不是(也不应该是)类模板。

拜托,我真的需要今晚或明天的帮助,我明天有截止日期,但我希望今晚就可以完成。

Am struggling a lot to find how to do to use boost::any to create a print function that can print any type using template first.

template <typename T>
struct printer {
    void print(ostream& os, const boost::any& a);
}; 

I need to define first print().
i wish to have the real operator << for any, The idea is simple: attach to each any object an instance of class
printer<T> with the suitable T and change this object when the value type of the any changes.
A first technical problem is that the printer object depends on T whereas any is not (and should not be) a class template.

Please I really need a hand is for tonight or tomorrow I have a deadline for tomorrow but I wish to work on it tonight.

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

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

发布评论

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

评论(4

浅听莫相离 2024-10-24 08:12:18

有一个非常简单的方法可以做到这一点,如“超越 C++ 标准库:简介”中所述Boost”:

struct streamer {
  virtual void print(ostream &o, const boost::any &a) const =0;
  virtual streamer * clone() const = 0;
  virtual ~streamer() {}
};

template <class T>
struct streamer_impl: streamer{
  void print(ostream &o, const boost::any &a) const { o << *boost::any_cast<T>(a); }
  streamer *clone() const { return new streamer_impl<T>(); }
};

class any_out {
  streamer *streamer_;
  boost::any o_;
  void swap(any_out & r){
    std::swap(streamer_, r.streamer_);
    std::swap(o_, r.o_);
  }
public:
  any_out(): streamer_(0) {}
  template<class T> any_out(const T& value)
    : streamer_(new streamer_impl<T>()), o_(value) {}
  any_out(const any_out& a)
    : streamer_(a.streamer_ ? a.streamer_->clone() : 0), o_(a.o_) {}

  template <class T>
  any_out & operator=(const T& r) { 
    any_out(r).swap(*this);
    return *this;
  }
  ~any_out() { delete streamer_; }

  friend std::ostream &operator<<(std::ostream& o, const any_out & a) {
    if(a.streamer_)
      a.streamer_->print(o, a);
    return o;
  }
};

然后使用 any_out 而不是 boost::any

There is quite easy way to do this, described in "Beyond the C++ Standard Library: An Introduction to Boost":

struct streamer {
  virtual void print(ostream &o, const boost::any &a) const =0;
  virtual streamer * clone() const = 0;
  virtual ~streamer() {}
};

template <class T>
struct streamer_impl: streamer{
  void print(ostream &o, const boost::any &a) const { o << *boost::any_cast<T>(a); }
  streamer *clone() const { return new streamer_impl<T>(); }
};

class any_out {
  streamer *streamer_;
  boost::any o_;
  void swap(any_out & r){
    std::swap(streamer_, r.streamer_);
    std::swap(o_, r.o_);
  }
public:
  any_out(): streamer_(0) {}
  template<class T> any_out(const T& value)
    : streamer_(new streamer_impl<T>()), o_(value) {}
  any_out(const any_out& a)
    : streamer_(a.streamer_ ? a.streamer_->clone() : 0), o_(a.o_) {}

  template <class T>
  any_out & operator=(const T& r) { 
    any_out(r).swap(*this);
    return *this;
  }
  ~any_out() { delete streamer_; }

  friend std::ostream &operator<<(std::ostream& o, const any_out & a) {
    if(a.streamer_)
      a.streamer_->print(o, a);
    return o;
  }
};

and then you use any_out instead of boost::any.

月亮邮递员 2024-10-24 08:12:18

我这样做,我认为这是干净和安全的:

any_extension.hpp:

namespace cpputil
{

struct AnyWriter
{
    /// Register a type with the AnyWriter.
    /// @pre T must have an ostream << operator available somewhere
    template<class T> static bool registerType()
    {
        return registeredTypes().emplace(std::type_index(typeid(T)),
                                         std::bind(&AnyWriter::write<T>,
                                                   std::placeholders::_1,
                                                   std::placeholders::_2)).second;
    }

    /// Write any registred object to a stream
    /// @pre Underlying type must have been registered with a call to AnyWriter::registerType<T>
    /// @param os is reference to a std::ostream
    /// @param anyObject is a reference to a boost::any
    static void writeAny(std::ostream& os, const boost::any& anyObject);
private:

    // A function object that converts an any to a type and streams it to an ostream
    using WriteFunction = std::function<void (std::ostream&, const boost::any&)>;

    // a map of typeinfo to WriteFunction
    using RegisteredTypes = std::unordered_map<std::type_index, WriteFunction >;

    // retrieve the WriteFunction map in a safe way
    static RegisteredTypes& registeredTypes();

    // Convert an any to a type, and write it to a stream
    template<class T> static void write(std::ostream& os, const boost::any& anyObject) {
        try {
            const T& typedObject = boost::any_cast<const T&>(anyObject);
            os << typedObject;
        }
        catch(boost::bad_any_cast& e) {
            os << "<exception in conversion: " << e.what() << ">";
        }
    }

};
}

namespace std {
    ostream& operator<<(ostream& os, const ::boost::any& anyObject);
}

any_extension.cpp:

#include "any_extension.h"
#include <string>

namespace cpputil {

namespace AnyWriterRegistration {
    const bool stringRegistered = AnyWriter::registerType<std::string>();
    const bool intRegistered = AnyWriter::registerType<int>();
    const bool doubleRegistered = AnyWriter::registerType<double>();
}



AnyWriter::RegisteredTypes& AnyWriter::registeredTypes()
{
    static RegisteredTypes _registrationMap;
    return _registrationMap;
}

void AnyWriter::writeAny(std::ostream &os, const boost::any &anyObject)
{
    auto registered = registeredTypes();
    auto iFind = registered.find(anyObject.type());
    if(iFind == registered.end()) {
        os << "<unregistered type: " << anyObject.type().name() << ">";
    }
    else {
        iFind->second(os, anyObject);
    }
}

}

namespace std {
ostream& operator<<(ostream& os, const ::boost::any& anyObject)
{
    if(anyObject.empty()) {
        os << "<empty>";
    }
    else {
        cpputil::AnyWriter::writeAny(os, anyObject);
    }
    return os;
}
}

对于您想要支持的任何类型,只需确保已为其类型和运算符调用 AnyWriter::register() 即可;<为它而存在。

例如:

any_test.cpp:

struct chicken {};
std::operator<<(std::ostream& os, const chicken& aChicken) {
    os << "cluck!";
    return os;
}

namespace {
    const bool chickenRegistered = AnyWriter::register<Chicken>();
}

void chickenTest() {
    boost::any animal = chicken();
    std::cout << animal << std::endl;
}

输出:
咕!

I do it like this, which I think is clean and safe:

any_extension.hpp:

namespace cpputil
{

struct AnyWriter
{
    /// Register a type with the AnyWriter.
    /// @pre T must have an ostream << operator available somewhere
    template<class T> static bool registerType()
    {
        return registeredTypes().emplace(std::type_index(typeid(T)),
                                         std::bind(&AnyWriter::write<T>,
                                                   std::placeholders::_1,
                                                   std::placeholders::_2)).second;
    }

    /// Write any registred object to a stream
    /// @pre Underlying type must have been registered with a call to AnyWriter::registerType<T>
    /// @param os is reference to a std::ostream
    /// @param anyObject is a reference to a boost::any
    static void writeAny(std::ostream& os, const boost::any& anyObject);
private:

    // A function object that converts an any to a type and streams it to an ostream
    using WriteFunction = std::function<void (std::ostream&, const boost::any&)>;

    // a map of typeinfo to WriteFunction
    using RegisteredTypes = std::unordered_map<std::type_index, WriteFunction >;

    // retrieve the WriteFunction map in a safe way
    static RegisteredTypes& registeredTypes();

    // Convert an any to a type, and write it to a stream
    template<class T> static void write(std::ostream& os, const boost::any& anyObject) {
        try {
            const T& typedObject = boost::any_cast<const T&>(anyObject);
            os << typedObject;
        }
        catch(boost::bad_any_cast& e) {
            os << "<exception in conversion: " << e.what() << ">";
        }
    }

};
}

namespace std {
    ostream& operator<<(ostream& os, const ::boost::any& anyObject);
}

any_extension.cpp:

#include "any_extension.h"
#include <string>

namespace cpputil {

namespace AnyWriterRegistration {
    const bool stringRegistered = AnyWriter::registerType<std::string>();
    const bool intRegistered = AnyWriter::registerType<int>();
    const bool doubleRegistered = AnyWriter::registerType<double>();
}



AnyWriter::RegisteredTypes& AnyWriter::registeredTypes()
{
    static RegisteredTypes _registrationMap;
    return _registrationMap;
}

void AnyWriter::writeAny(std::ostream &os, const boost::any &anyObject)
{
    auto registered = registeredTypes();
    auto iFind = registered.find(anyObject.type());
    if(iFind == registered.end()) {
        os << "<unregistered type: " << anyObject.type().name() << ">";
    }
    else {
        iFind->second(os, anyObject);
    }
}

}

namespace std {
ostream& operator<<(ostream& os, const ::boost::any& anyObject)
{
    if(anyObject.empty()) {
        os << "<empty>";
    }
    else {
        cpputil::AnyWriter::writeAny(os, anyObject);
    }
    return os;
}
}

For any type that you'd like supported, simply ensure that AnyWriter::register() has been called for its type and an operator<< exists for it.

For example:

any_test.cpp:

struct chicken {};
std::operator<<(std::ostream& os, const chicken& aChicken) {
    os << "cluck!";
    return os;
}

namespace {
    const bool chickenRegistered = AnyWriter::register<Chicken>();
}

void chickenTest() {
    boost::any animal = chicken();
    std::cout << animal << std::endl;
}

output:
cluck!

巾帼英雄 2024-10-24 08:12:18

Pawel Zubrycki 的回答非常好(提到 Björn Karlsson 的书)。

但代码在以下几行中存在一些错误:

// ...
o << *boost::any_cast<T>(a);    // should be:   o << *boost::any_cast<T>(&a);
// ...
a.streamer_->print(o, a);       // should be:   a.streamer_->print(o, a.o_);

这是 Pawel Zubrycki 答案的正确版本,该版本有效(部分......)

#ifndef ANY_OUT_H
#define ANY_OUT_H

#include <iostream>
#include <boost/any.hpp>

struct streamer {
  virtual void print(std::ostream &o, const boost::any &a) const =0;
  virtual streamer * clone() const = 0;
  virtual ~streamer() {}
};

template <class T>
struct streamer_impl: streamer{
  void print(std::ostream &o, const boost::any &a) const { o << *boost::any_cast<T>(&a); }
  streamer *clone() const { return new streamer_impl<T>(); }
};

class any_out {
  streamer *streamer_;
  boost::any o_;
  void swap(any_out & r){
    std::swap(streamer_, r.streamer_);
    std::swap(o_, r.o_);
  }
public:
  any_out(): streamer_(0) {}
  template<class T> any_out(const T& value)
    : streamer_(new streamer_impl<T>()), o_(value) {}
  any_out(const any_out& a)
    : streamer_(a.streamer_ ? a.streamer_->clone() : 0), o_(a.o_) {}

  template <class T>
  any_out & operator=(const T& r) {
    any_out(r).swap(*this);
    return *this;
  }
  ~any_out() { delete streamer_; }

  friend std::ostream &operator<<(std::ostream& o, const any_out & a);
};

std::ostream &operator<<(std::ostream& o, const any_out & a) {
  if(a.streamer_)
    a.streamer_->print(o, a.o_);
  return o;
}

#endif

此测试代码有效:

{
   any_out a = 5;
   std::cout << a << std::endl;
}

但是!!!

以下内容< strong>不起作用:

int main()
{
  char str[] = "mystring";
  any_out a = str;
  std::cout << a << std::endl;

  a = "myconststring";
  std::cout << a << std::endl;
}

这里什么也没打印

为什么??

那么在下面的构造函数中,类型就会混乱

any_out(const T& value)

那么,如果我们将 Streamer 实例化为从

new streamer_impl<T>()

构造函数中删除引用,

  any_out(const T value)

,即...是一种解决方案。

另一个解决方案是保留引用并调整 streamer_impl 的模板实例化。参见下文

以下推荐解决方案带来的是:

#ifndef ANY_OUT_H
#define ANY_OUT_H

#include <iostream>
#include <boost/any.hpp>

struct streamer {
  virtual void print(std::ostream &o, const boost::any &a) const =0;
  virtual streamer * clone() const = 0;
  virtual ~streamer() {}
};

template <class T>
struct streamer_impl: streamer{
  void print(std::ostream &o, const boost::any &a) const { o << boost::any_cast<T>(a); }
  streamer *clone() const { return new streamer_impl<T>(); }
};

class any_out {
  boost::any o_;
  streamer *streamer_;
  void swap(any_out & r){
    std::swap(streamer_, r.streamer_);
    std::swap(o_, r.o_);
  }
public:
  any_out(): streamer_(0) {}

  template<class T> any_out(const T& value)
    : o_(value),
#if 1
      streamer_(new streamer_impl<typename std::decay<decltype(value)>::type>)
#else
      streamer_((o_.type() == typeid(const char *))
                ? static_cast<streamer *>(new streamer_impl<const char *>)
                : static_cast<streamer *>(new streamer_impl<T>))
#endif
  {
  }

  // template<class T> any_out(const T value)
  //   : o_(value),
  //     streamer_(new streamer_impl<T>)
  // {
  // }

  any_out(const any_out& a)
    : o_(a.o_), streamer_(a.streamer_ ? a.streamer_->clone() : 0) {}

  template <class T>
  any_out & operator=(const T& r) {
    any_out(r).swap(*this);
    return *this;
  }
  ~any_out() { delete streamer_; }

  friend std::ostream &operator<<(std::ostream& o, const any_out & a);
};

std::ostream &operator<<(std::ostream& o, const any_out & a) {
  if(a.streamer_)
    a.streamer_->print(o, a.o_);
  return o;
}

#endif

上面带来一些麻烦的测试代码现在可以很好地工作(使用“推荐解决方案”):

int main()
{
  char str[] = "mystring";
  any_out a = str;
  std::cout << a << std::endl;

  a = "myconststring";
  std::cout << a << std::endl;
}

Very nice answer by Pawel Zubrycki (mentioning Björn Karlsson's book).

But the code has a few errors in the following lines:

// ...
o << *boost::any_cast<T>(a);    // should be:   o << *boost::any_cast<T>(&a);
// ...
a.streamer_->print(o, a);       // should be:   a.streamer_->print(o, a.o_);

Here's a corrected version of Pawel Zubrycki's answer that works (partially...)

#ifndef ANY_OUT_H
#define ANY_OUT_H

#include <iostream>
#include <boost/any.hpp>

struct streamer {
  virtual void print(std::ostream &o, const boost::any &a) const =0;
  virtual streamer * clone() const = 0;
  virtual ~streamer() {}
};

template <class T>
struct streamer_impl: streamer{
  void print(std::ostream &o, const boost::any &a) const { o << *boost::any_cast<T>(&a); }
  streamer *clone() const { return new streamer_impl<T>(); }
};

class any_out {
  streamer *streamer_;
  boost::any o_;
  void swap(any_out & r){
    std::swap(streamer_, r.streamer_);
    std::swap(o_, r.o_);
  }
public:
  any_out(): streamer_(0) {}
  template<class T> any_out(const T& value)
    : streamer_(new streamer_impl<T>()), o_(value) {}
  any_out(const any_out& a)
    : streamer_(a.streamer_ ? a.streamer_->clone() : 0), o_(a.o_) {}

  template <class T>
  any_out & operator=(const T& r) {
    any_out(r).swap(*this);
    return *this;
  }
  ~any_out() { delete streamer_; }

  friend std::ostream &operator<<(std::ostream& o, const any_out & a);
};

std::ostream &operator<<(std::ostream& o, const any_out & a) {
  if(a.streamer_)
    a.streamer_->print(o, a.o_);
  return o;
}

#endif

This test-code works:

{
   any_out a = 5;
   std::cout << a << std::endl;
}

However!!!!

The following does not work:

int main()
{
  char str[] = "mystring";
  any_out a = str;
  std::cout << a << std::endl;

  a = "myconststring";
  std::cout << a << std::endl;
}

Here nothing gets printed.

Why??

Well the type is messed up, in the following constructor

any_out(const T& value)

if we then instantiate streamer as

new streamer_impl<T>()

Removing the reference from the constructor, i.e.

  any_out(const T value)

... is one solution.

Another solution is to leave the reference and tweak the template instantiation of streamer_impl. See below

Which brings as to the following recommened solution is:

#ifndef ANY_OUT_H
#define ANY_OUT_H

#include <iostream>
#include <boost/any.hpp>

struct streamer {
  virtual void print(std::ostream &o, const boost::any &a) const =0;
  virtual streamer * clone() const = 0;
  virtual ~streamer() {}
};

template <class T>
struct streamer_impl: streamer{
  void print(std::ostream &o, const boost::any &a) const { o << boost::any_cast<T>(a); }
  streamer *clone() const { return new streamer_impl<T>(); }
};

class any_out {
  boost::any o_;
  streamer *streamer_;
  void swap(any_out & r){
    std::swap(streamer_, r.streamer_);
    std::swap(o_, r.o_);
  }
public:
  any_out(): streamer_(0) {}

  template<class T> any_out(const T& value)
    : o_(value),
#if 1
      streamer_(new streamer_impl<typename std::decay<decltype(value)>::type>)
#else
      streamer_((o_.type() == typeid(const char *))
                ? static_cast<streamer *>(new streamer_impl<const char *>)
                : static_cast<streamer *>(new streamer_impl<T>))
#endif
  {
  }

  // template<class T> any_out(const T value)
  //   : o_(value),
  //     streamer_(new streamer_impl<T>)
  // {
  // }

  any_out(const any_out& a)
    : o_(a.o_), streamer_(a.streamer_ ? a.streamer_->clone() : 0) {}

  template <class T>
  any_out & operator=(const T& r) {
    any_out(r).swap(*this);
    return *this;
  }
  ~any_out() { delete streamer_; }

  friend std::ostream &operator<<(std::ostream& o, const any_out & a);
};

std::ostream &operator<<(std::ostream& o, const any_out & a) {
  if(a.streamer_)
    a.streamer_->print(o, a.o_);
  return o;
}

#endif

The test-code that gave some trouble above, now works nicely (with the "recommeded solution"):

int main()
{
  char str[] = "mystring";
  any_out a = str;
  std::cout << a << std::endl;

  a = "myconststring";
  std::cout << a << std::endl;
}
是你 2024-10-24 08:12:18

在 Boost 邮件列表中查看此主题:
http://lists.boost.org/Archives/boost/2005/01 /79232.php

它有一些想法,其中一些看起来不错,而另一些(对我来说)则不然。不过,总的来说,这似乎是一项很难以一般方式完成的任务,因为(如该线程中提到的),某些类型永远不会是 ostream'able,但可以包含在 boost::any 中代码>对象。

Check out this thread on the Boost mailing list:
http://lists.boost.org/Archives/boost/2005/01/79232.php

It has a few ideas, some of which seem sort of OK and some of which don't (to me). Overall, though, this seems like a difficult task to accomplish in a general way, since (as mentioned in that thread), some types will never be ostream'able, yet could be contained in a boost::any object.

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